mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-22 03:44:49 +01:00
Beautiful admin panel (#35)
* Fixed some issues Solve the problem of repeated addition of user objects in db.json after logging into the control panel * 更新 * 更新 * 修复问题 * Update * Add missing files
This commit is contained in:
376
EpinelPS/wwwroot/admin/assets/css/nikke-dashboard.css
Normal file
376
EpinelPS/wwwroot/admin/assets/css/nikke-dashboard.css
Normal file
@@ -0,0 +1,376 @@
|
||||
/* NIKKE 仪表盘专用样式 */
|
||||
|
||||
/* 仪表板容器 */
|
||||
.nikke-dashboard {
|
||||
padding: var(--spacing-lg);
|
||||
min-height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
/* 统计卡片 */
|
||||
.nikke-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: var(--spacing-lg);
|
||||
margin-bottom: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
background-color: var(--nikke-gray-dark);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-lg);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all var(--transition-normal);
|
||||
border: 1px solid var(--nikke-gray);
|
||||
}
|
||||
|
||||
.stats-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
border-color: var(--nikke-accent);
|
||||
}
|
||||
|
||||
.stats-card .icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: var(--spacing-md);
|
||||
color: var(--nikke-accent);
|
||||
}
|
||||
|
||||
.stats-card .value {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--nikke-light);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.stats-card .label {
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: var(--nikke-gray-light);
|
||||
font-weight: normal;
|
||||
padding: 0;
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.stats-card .decoration {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 5px;
|
||||
background: linear-gradient(to bottom, var(--nikke-accent), transparent);
|
||||
}
|
||||
|
||||
/* 图表卡片 */
|
||||
.chart-card {
|
||||
background-color: var(--nikke-gray-dark);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-lg);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
border: 1px solid var(--nikke-gray);
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.chart-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-weight: 600;
|
||||
color: var(--nikke-light);
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-body {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chart-card .btn-outline-light {
|
||||
color: var(--nikke-gray-light);
|
||||
border-color: var(--nikke-gray);
|
||||
background-color: transparent;
|
||||
font-size: 0.85rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
}
|
||||
|
||||
.chart-card .btn-outline-light:hover,
|
||||
.chart-card .btn-outline-light.active {
|
||||
color: var(--nikke-light);
|
||||
background-color: var(--nikke-accent);
|
||||
border-color: var(--nikke-accent);
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
.nikke-progress {
|
||||
height: 8px;
|
||||
background-color: var(--nikke-gray);
|
||||
border-radius: var(--radius-full);
|
||||
overflow: hidden;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.nikke-progress-bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, var(--nikke-accent-dark), var(--nikke-accent));
|
||||
border-radius: var(--radius-full);
|
||||
}
|
||||
|
||||
/* 活动列表 */
|
||||
.activity-list {
|
||||
background-color: var(--nikke-gray-dark);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--nikke-gray);
|
||||
box-shadow: var(--shadow-md);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.activity-header {
|
||||
background-color: var(--nikke-primary);
|
||||
color: var(--nikke-light);
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
border-bottom: 1px solid var(--nikke-primary-dark);
|
||||
}
|
||||
|
||||
.activity-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
border-bottom: 1px solid rgba(58, 58, 72, 0.5);
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.activity-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.activity-item:hover {
|
||||
background-color: rgba(43, 57, 144, 0.1);
|
||||
}
|
||||
|
||||
.activity-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--nikke-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
margin-right: var(--spacing-md);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.activity-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.activity-title {
|
||||
color: var(--nikke-light);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.activity-time {
|
||||
color: var(--nikke-gray-light);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* 用户相关样式 */
|
||||
.user-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
margin-right: var(--spacing-md);
|
||||
border: 2px solid var(--nikke-accent);
|
||||
background-color: var(--nikke-gray);
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-weight: 500;
|
||||
color: var(--nikke-light);
|
||||
}
|
||||
|
||||
.badge-admin {
|
||||
display: inline-block;
|
||||
background-color: var(--nikke-accent);
|
||||
color: white;
|
||||
padding: 0.35em 0.65em;
|
||||
font-size: 0.75em;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
border-radius: var(--radius-sm);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* 通知 */
|
||||
.nikke-notification {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
min-width: 300px;
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
background-color: var(--nikke-gray-dark);
|
||||
color: var(--nikke-light);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 1050;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.nikke-notification.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.nikke-notification.info {
|
||||
border-left: 5px solid var(--nikke-info);
|
||||
}
|
||||
|
||||
.nikke-notification.success {
|
||||
border-left: 5px solid var(--nikke-success);
|
||||
}
|
||||
|
||||
.nikke-notification.warning {
|
||||
border-left: 5px solid var(--nikke-warning);
|
||||
}
|
||||
|
||||
.nikke-notification.error {
|
||||
border-left: 5px solid var(--nikke-danger);
|
||||
}
|
||||
|
||||
.notification-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.notification-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.notification-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--nikke-gray-light);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.notification-close:hover {
|
||||
color: var(--nikke-light);
|
||||
}
|
||||
|
||||
.notification-body {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 仪表盘适应性 */
|
||||
@media (max-width: 1199.98px) {
|
||||
.nikke-stats {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.nikke-stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.nikke-notification {
|
||||
min-width: calc(100% - 40px);
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 自定义图表样式 */
|
||||
canvas {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* 确保图表中的文字使用正确的颜色 */
|
||||
.chartjs-render-monitor {
|
||||
color: var(--nikke-light);
|
||||
}
|
||||
|
||||
/* 图表工具提示定制 */
|
||||
.chartjs-tooltip {
|
||||
background-color: var(--nikke-gray-dark) !important;
|
||||
border: 1px solid var(--nikke-gray) !important;
|
||||
color: var(--nikke-light) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
box-shadow: var(--shadow-md) !important;
|
||||
padding: var(--spacing-sm) var(--spacing-md) !important;
|
||||
font-family: 'Exo 2', 'Noto Sans SC', sans-serif !important;
|
||||
font-size: 0.85rem !important;
|
||||
}
|
||||
|
||||
/* 页面加载动画 */
|
||||
.dashboard-loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(23, 23, 31, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.dashboard-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 3px solid var(--nikke-gray);
|
||||
border-top: 3px solid var(--nikke-accent);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 仪表盘特殊动画 */
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0 0 0 0 rgba(232, 62, 140, 0.4); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(232, 62, 140, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(232, 62, 140, 0); }
|
||||
}
|
||||
|
||||
.pulse-animation {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
251
EpinelPS/wwwroot/admin/assets/css/nikke-i18n.css
Normal file
251
EpinelPS/wwwroot/admin/assets/css/nikke-i18n.css
Normal file
@@ -0,0 +1,251 @@
|
||||
/* NIKKE 国际化支持样式 */
|
||||
[data-i18n],
|
||||
[data-i18n-placeholder],
|
||||
[data-i18n-title] {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.lang-loading [data-i18n],
|
||||
.lang-loading [data-i18n-placeholder],
|
||||
.lang-loading [data-i18n-title] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.language-transition-enter-active,
|
||||
.language-transition-leave-active {
|
||||
transition: opacity 0.3s, transform 0.3s;
|
||||
}
|
||||
|
||||
.language-transition-enter,
|
||||
.language-transition-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
/* 语言切换器样式 - 放在右上角 */
|
||||
.language-switcher {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1050;
|
||||
font-family: 'Exo 2', 'Noto Sans SC', sans-serif;
|
||||
animation: fadeIn 0.5s ease forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 登录页面特殊位置 */
|
||||
body.login-page .language-switcher {
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
/* 仪表盘页面特殊位置 */
|
||||
body:not(.login-page) .language-switcher {
|
||||
top: 15px;
|
||||
right: 170px; /* 预留用户菜单空间 */
|
||||
}
|
||||
|
||||
.language-switcher-toggle {
|
||||
background-color: rgba(35, 35, 45, 0.9);
|
||||
color: white;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.language-switcher-toggle:hover {
|
||||
background-color: rgba(43, 57, 144, 0.9);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.language-switcher-toggle i.fa-globe {
|
||||
margin-right: 8px;
|
||||
color: var(--nikke-accent-light, #f16eac);
|
||||
}
|
||||
|
||||
.language-switcher-toggle i.fa-chevron-down {
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
transition: transform 0.3s ease;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.language-switcher.open .language-switcher-toggle i.fa-chevron-down {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.language-options {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
margin-top: 5px;
|
||||
background-color: rgba(35, 35, 45, 0.95);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
transition: max-height 0.3s ease, opacity 0.3s ease;
|
||||
opacity: 0;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.language-switcher.open .language-options {
|
||||
max-height: 200px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.language-option {
|
||||
padding: 8px 15px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
transition: background-color 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.language-option:hover {
|
||||
background-color: rgba(43, 57, 144, 0.5);
|
||||
}
|
||||
|
||||
.language-option.active {
|
||||
background-color: rgba(232, 62, 140, 0.2);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.language-option.active:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
background-color: var(--nikke-accent, #e83e8c);
|
||||
}
|
||||
|
||||
.language-name {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.language-code {
|
||||
opacity: 0.5;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* RTL 支持 (针对阿拉伯语等从右到左的语言) */
|
||||
[dir="rtl"] .language-switcher-toggle i.fa-globe {
|
||||
margin-right: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
[dir="rtl"] .language-switcher-toggle i.fa-chevron-down {
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
[dir="rtl"] .language-options {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
[dir="rtl"] .language-option.active:after {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* 语言特定字体 */
|
||||
html[lang="zh"] body {
|
||||
font-family: 'Noto Sans SC', 'Exo 2', sans-serif;
|
||||
}
|
||||
|
||||
html[lang="ja"] body {
|
||||
font-family: 'Noto Sans JP', 'Exo 2', sans-serif;
|
||||
}
|
||||
|
||||
html[lang="ko"] body {
|
||||
font-family: 'Noto Sans KR', 'Exo 2', sans-serif;
|
||||
}
|
||||
|
||||
/* 提高可访问性 */
|
||||
.language-option:focus {
|
||||
outline: 2px solid var(--nikke-accent, #e83e8c);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 767.98px) {
|
||||
.language-switcher-toggle {
|
||||
padding: 4px 10px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
body:not(.login-page) .language-switcher {
|
||||
top: 60px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.language-option {
|
||||
padding: 6px 12px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 导航栏集成的语言切换器 */
|
||||
.language-switcher.navbar-integrated {
|
||||
position: static;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.language-switcher.navbar-integrated .language-switcher-toggle {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-md);
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.language-switcher.navbar-integrated .language-switcher-toggle:hover {
|
||||
background-color: rgba(43, 57, 144, 0.2);
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.language-switcher.navbar-integrated .language-options {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
z-index: 1050;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 移动设备上的导航栏集成 */
|
||||
@media (max-width: 767.98px) {
|
||||
.language-switcher.navbar-integrated {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.language-switcher.navbar-integrated .language-options {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* 登录页面的语言切换器 */
|
||||
body.login-page .language-switcher {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1050;
|
||||
}
|
||||
229
EpinelPS/wwwroot/admin/assets/css/nikke-login.css
Normal file
229
EpinelPS/wwwroot/admin/assets/css/nikke-login.css
Normal file
@@ -0,0 +1,229 @@
|
||||
body.login-page {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: url('/admin/assets/img/nikke-login-bg.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
font-family: 'Exo 2', 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.login-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(23, 23, 31, 0.85), rgba(43, 57, 144, 0.7));
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
background-color: rgba(35, 35, 45, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.3), 0 0 15px rgba(232, 62, 140, 0.5);
|
||||
animation: fadeIn 0.8s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.login-header {
|
||||
padding: 30px 40px 15px;
|
||||
text-align: center;
|
||||
background: linear-gradient(to right, var(--nikke-primary-dark), var(--nikke-primary));
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.login-header::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image: url('/admin/assets/img/nikke-pattern.png');
|
||||
background-size: 200px;
|
||||
opacity: 0.1;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.login-header .logo {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 120px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
margin-bottom: 5px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-body {
|
||||
padding: 30px 40px;
|
||||
}
|
||||
|
||||
.form-floating {
|
||||
position: relative;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-floating label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 15px;
|
||||
height: 100%;
|
||||
padding: 1rem 0.75rem;
|
||||
pointer-events: none;
|
||||
border: 1px solid transparent;
|
||||
transform-origin: 0 0;
|
||||
transition: opacity .1s ease-in-out,transform .1s ease-in-out;
|
||||
color: var(--nikke-gray-light);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-floating .form-control {
|
||||
height: 58px;
|
||||
padding: 1.5rem 0.75rem 0.5rem;
|
||||
color: white;
|
||||
background-color: var(--nikke-gray);
|
||||
border: 1px solid var(--nikke-gray-dark);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.form-floating .form-control:focus {
|
||||
box-shadow: 0 0 0 0.25rem rgba(232, 62, 140, 0.25);
|
||||
border-color: var(--nikke-accent);
|
||||
}
|
||||
|
||||
.form-floating .form-control:focus ~ label,
|
||||
.form-floating .form-control:not(:placeholder-shown) ~ label {
|
||||
opacity: .65;
|
||||
transform: scale(.85) translateY(-0.5rem) translateX(0.15rem);
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: linear-gradient(to right, var(--nikke-accent-dark), var(--nikke-accent));
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
margin-top: 10px;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-normal);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.login-button::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.login-button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 15px rgba(232, 62, 140, 0.4);
|
||||
}
|
||||
|
||||
.login-button:hover::after {
|
||||
transform: translateX(100%);
|
||||
transition: transform 1s;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background-color: rgba(244, 66, 131, 0.2);
|
||||
color: var(--nikke-danger);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin: 15px 0;
|
||||
display: none;
|
||||
animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;
|
||||
}
|
||||
|
||||
.error-message.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
10%, 90% { transform: translate3d(-1px, 0, 0); }
|
||||
20%, 80% { transform: translate3d(2px, 0, 0); }
|
||||
30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
|
||||
40%, 60% { transform: translate3d(4px, 0, 0); }
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
text-align: center;
|
||||
padding: 0 40px 30px;
|
||||
color: var(--nikke-gray-light);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.version {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
font-size: 12px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 动画光效 */
|
||||
.nikke-glow {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: radial-gradient(circle, rgba(232, 62, 140, 0.4) 0%, rgba(232, 62, 140, 0) 70%);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
body.login-page:hover .nikke-glow {
|
||||
opacity: 1;
|
||||
}
|
||||
426
EpinelPS/wwwroot/admin/assets/css/nikke-theme.css
Normal file
426
EpinelPS/wwwroot/admin/assets/css/nikke-theme.css
Normal file
@@ -0,0 +1,426 @@
|
||||
:root {
|
||||
/* NIKKE主题色系 */
|
||||
--nikke-primary: #2b3990; /* NIKKE蓝色 */
|
||||
--nikke-primary-light: #4754b9;
|
||||
--nikke-primary-dark: #1a2370;
|
||||
|
||||
--nikke-accent: #e83e8c; /* NIKKE粉色 */
|
||||
--nikke-accent-light: #f16eac;
|
||||
--nikke-accent-dark: #c72c70;
|
||||
|
||||
/* 中性色 */
|
||||
--nikke-dark: #17171f;
|
||||
--nikke-gray-dark: #23232d;
|
||||
--nikke-gray: #3a3a48;
|
||||
--nikke-gray-light: #7c7c8a;
|
||||
--nikke-light: #f8f9fc;
|
||||
|
||||
/* 功能色 */
|
||||
--nikke-success: #32d296;
|
||||
--nikke-warning: #faa05a;
|
||||
--nikke-danger: #f44283;
|
||||
--nikke-info: #3fafec;
|
||||
|
||||
/* 尺寸 */
|
||||
--spacing-xs: 4px;
|
||||
--spacing-sm: 8px;
|
||||
--spacing-md: 16px;
|
||||
--spacing-lg: 24px;
|
||||
--spacing-xl: 32px;
|
||||
|
||||
/* 阴影 */
|
||||
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
--shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.25);
|
||||
--shadow-neon: 0 0 10px rgba(232, 62, 140, 0.5);
|
||||
|
||||
/* 过渡 */
|
||||
--transition-fast: 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
--transition-normal: 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
--transition-slow: 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
|
||||
/* 圆角 */
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-full: 9999px;
|
||||
}
|
||||
|
||||
/* 全局样式 */
|
||||
body {
|
||||
font-family: 'Exo 2', 'Noto Sans SC', sans-serif;
|
||||
background: linear-gradient(135deg, var(--nikke-dark) 0%, rgba(26, 35, 112, 0.9) 100%);
|
||||
color: var(--nikke-light);
|
||||
line-height: 1.6;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('/assets/img/nikke-bg-pattern.png');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
opacity: 0.2;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 600;
|
||||
margin-bottom: var(--spacing-md);
|
||||
color: var(--nikke-light);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.display-4 {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
color: var(--nikke-light);
|
||||
text-shadow: 0 0 10px rgba(232, 62, 140, 0.5);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--nikke-accent);
|
||||
text-decoration: none;
|
||||
transition: all var(--transition-fast);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--nikke-accent-light);
|
||||
text-shadow: 0 0 8px rgba(232, 62, 140, 0.4);
|
||||
}
|
||||
|
||||
a.nav-link:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background-color: var(--nikke-accent);
|
||||
transition: width var(--transition-normal);
|
||||
}
|
||||
|
||||
a.nav-link:hover:after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navbar {
|
||||
background-color: var(--nikke-primary-dark) !important;
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
border-bottom: 1px solid rgba(232, 62, 140, 0.3) !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: var(--nikke-light) !important;
|
||||
font-size: 1.4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navbar-brand:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-right: 10px;
|
||||
background-image: url('/admin/assets/img/nikke-logo-icon.png');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.navbar-light .navbar-nav .nav-link {
|
||||
color: rgba(255, 255, 255, 0.85) !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.navbar-light .navbar-nav .nav-link:hover,
|
||||
.navbar-light .navbar-nav .nav-link:focus,
|
||||
.navbar-light .navbar-nav .nav-link.active {
|
||||
color: var(--nikke-light) !important;
|
||||
background-color: rgba(232, 62, 140, 0.2);
|
||||
}
|
||||
|
||||
/* 容器样式 */
|
||||
.container, .container-fluid {
|
||||
padding-top: var(--spacing-lg);
|
||||
padding-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.table {
|
||||
background-color: var(--nikke-gray-dark);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--nikke-gray);
|
||||
box-shadow: var(--shadow-md);
|
||||
color: var(--nikke-light);
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
background-color: var(--nikke-primary);
|
||||
color: var(--nikke-light);
|
||||
text-transform: uppercase;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 1px;
|
||||
border-bottom: none;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.table tbody tr:nth-of-type(odd) {
|
||||
background-color: rgba(43, 57, 144, 0.1);
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background-color: rgba(232, 62, 140, 0.1);
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 0.85rem 1rem;
|
||||
vertical-align: middle;
|
||||
border-top: 1px solid var(--nikke-gray);
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn {
|
||||
border-radius: var(--radius-md);
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
padding: 0.5rem 1.25rem;
|
||||
transition: all var(--transition-fast);
|
||||
border: none;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--nikke-primary);
|
||||
border-color: var(--nikke-primary);
|
||||
color: var(--nikke-light);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--nikke-primary-light);
|
||||
border-color: var(--nikke-primary-light);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(43, 57, 144, 0.4);
|
||||
}
|
||||
|
||||
.btn-primary:focus, .btn-primary:active {
|
||||
background-color: var(--nikke-primary-dark) !important;
|
||||
border-color: var(--nikke-primary-dark) !important;
|
||||
box-shadow: 0 0 0 0.25rem rgba(43, 57, 144, 0.5) !important;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: var(--nikke-danger);
|
||||
border-color: var(--nikke-danger);
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: var(--nikke-accent-dark);
|
||||
border-color: var(--nikke-accent-dark);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(232, 62, 140, 0.4);
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
color: var(--nikke-primary);
|
||||
border-color: var(--nikke-primary);
|
||||
}
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
background-color: var(--nikke-primary);
|
||||
color: var(--nikke-light);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 表单样式 */
|
||||
.form-control, .form-select {
|
||||
background-color: var(--nikke-gray-dark);
|
||||
border: 1px solid var(--nikke-gray);
|
||||
color: var(--nikke-light);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.6rem 1rem;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
background-color: var(--nikke-gray);
|
||||
border-color: var(--nikke-accent);
|
||||
color: var(--nikke-light);
|
||||
box-shadow: 0 0 0 0.25rem rgba(232, 62, 140, 0.25);
|
||||
}
|
||||
|
||||
.form-label {
|
||||
color: var(--nikke-light);
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control::placeholder {
|
||||
color: var(--nikke-gray-light);
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.card {
|
||||
background-color: var(--nikke-gray-dark);
|
||||
border: 1px solid var(--nikke-gray);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-md);
|
||||
transition: all var(--transition-normal);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background-color: var(--nikke-primary);
|
||||
color: var(--nikke-light);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
padding: 1rem 1.25rem;
|
||||
border-bottom: 1px solid var(--nikke-primary-dark);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1.5rem;
|
||||
color: var(--nikke-light);
|
||||
}
|
||||
|
||||
/* 徽章样式 */
|
||||
.badge {
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
padding: 0.35em 0.65em;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.badge-admin {
|
||||
background-color: var(--nikke-accent);
|
||||
color: var(--nikke-light);
|
||||
}
|
||||
|
||||
/* 页脚样式 */
|
||||
.footer {
|
||||
background-color: var(--nikke-gray-dark);
|
||||
color: var(--nikke-gray-light);
|
||||
padding: var(--spacing-md) 0;
|
||||
border-top: 1px solid var(--nikke-gray);
|
||||
}
|
||||
|
||||
/* 动画和效果 */
|
||||
@keyframes glow {
|
||||
0% { box-shadow: 0 0 5px rgba(232, 62, 140, 0.5); }
|
||||
50% { box-shadow: 0 0 20px rgba(232, 62, 140, 0.8); }
|
||||
100% { box-shadow: 0 0 5px rgba(232, 62, 140, 0.5); }
|
||||
}
|
||||
|
||||
.glow-effect {
|
||||
animation: glow 2s infinite;
|
||||
}
|
||||
|
||||
/* 数据表格特殊样式 */
|
||||
.admin-badge {
|
||||
background-color: var(--nikke-accent);
|
||||
color: white;
|
||||
padding: 0.25em 0.5em;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* 自定义组件 */
|
||||
.stat-card {
|
||||
background-color: var(--nikke-gray-dark);
|
||||
border: 1px solid var(--nikke-gray);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 1.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
transition: all var(--transition-normal);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.stat-card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(232, 62, 140, 0.1));
|
||||
transform: skewX(-25deg) translateX(100%);
|
||||
transition: transform 1s;
|
||||
}
|
||||
|
||||
.stat-card:hover::after {
|
||||
transform: skewX(-25deg) translateX(-150%);
|
||||
}
|
||||
|
||||
.stat-card .icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: var(--spacing-md);
|
||||
color: var(--nikke-accent);
|
||||
}
|
||||
|
||||
.stat-card .value {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--nikke-light);
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-size: 1rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: var(--nikke-gray-light);
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.navbar-brand {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.display-4 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.table {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
215
EpinelPS/wwwroot/admin/assets/i18n/en.json
Normal file
215
EpinelPS/wwwroot/admin/assets/i18n/en.json
Normal file
@@ -0,0 +1,215 @@
|
||||
{
|
||||
"app": {
|
||||
"name": "NIKKE Admin Console",
|
||||
"version": "Version v2.5.3",
|
||||
"footer": "© 2025 - NIKKE: Goddess of Victory - Admin Console"
|
||||
},
|
||||
"auth": {
|
||||
"login": "Login",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"enter": "Enter Console",
|
||||
"welcome": "Please login to access admin features",
|
||||
"verifying": "Verifying...",
|
||||
"success": "Login successful",
|
||||
"error": {
|
||||
"required": "Please enter username and password",
|
||||
"invalid": "Login failed, please check your credentials",
|
||||
"network": "Network error, please try again later"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"loading": "Loading...",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"save": "Save",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"actions": "Actions",
|
||||
"search": "Search",
|
||||
"status": {
|
||||
"online": "Online",
|
||||
"offline": "Offline"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "System Notification",
|
||||
"dashboardRefreshed": "Dashboard data successfully refreshed"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
"dashboard": "Dashboard",
|
||||
"events": "Events",
|
||||
"users": "Users",
|
||||
"mail": "Mail",
|
||||
"config": "Configuration",
|
||||
"database": "Database",
|
||||
"profile": "Profile",
|
||||
"changePass": "Change Password",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Console Overview",
|
||||
"refresh": "Refresh Data",
|
||||
"refreshing": "Refreshing...",
|
||||
"statsCards": {
|
||||
"users": "Registered Users",
|
||||
"server": "Server Status",
|
||||
"events": "Active Events",
|
||||
"mail": "Mail Today"
|
||||
},
|
||||
"charts": {
|
||||
"activity": "User Activity Trends",
|
||||
"server": "Server Resource Usage",
|
||||
"periods": {
|
||||
"day": "Day",
|
||||
"week": "Week",
|
||||
"month": "Month"
|
||||
}
|
||||
},
|
||||
"serverStats": {
|
||||
"cpu": "CPU Usage",
|
||||
"memory": "Memory Usage",
|
||||
"storage": "Storage Usage"
|
||||
},
|
||||
"activity": {
|
||||
"title": "Recent Activity",
|
||||
"newUser": "New User Registration",
|
||||
"loginAlert": "Unusual Login Detected",
|
||||
"configUpdate": "System Configuration Updated",
|
||||
"rewards": "Global Rewards Distributed",
|
||||
"newEvent": "New Event Created",
|
||||
"bugfix": "Game Bug Fixed",
|
||||
"timeFormat": {
|
||||
"min": "minutes ago",
|
||||
"hour": "hours ago",
|
||||
"yesterday": "yesterday",
|
||||
"daysAgo": "days ago"
|
||||
}
|
||||
},
|
||||
"quickAccess": {
|
||||
"users": {
|
||||
"title": "User Management",
|
||||
"desc": "Manage game user accounts, permissions and roles"
|
||||
},
|
||||
"events": {
|
||||
"title": "Event Management",
|
||||
"desc": "Create, edit and monitor game events"
|
||||
},
|
||||
"mail": {
|
||||
"title": "Mail System",
|
||||
"desc": "Send system mails and rewards to players"
|
||||
},
|
||||
"database": {
|
||||
"title": "Database",
|
||||
"desc": "Manage and maintain game database"
|
||||
},
|
||||
"enter": "Enter"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"title": "User Management",
|
||||
"add": "Add New User",
|
||||
"table": {
|
||||
"id": "ID",
|
||||
"username": "Username",
|
||||
"nickname": "Nickname",
|
||||
"playerName": "Player Name",
|
||||
"isAdmin": "Permission",
|
||||
"isBanned": "Banned",
|
||||
"actions": "Actions"
|
||||
},
|
||||
"search": {
|
||||
"username": "Username",
|
||||
"usernamePlaceholder": "Search username...",
|
||||
"userType": "User Type",
|
||||
"types": {
|
||||
"all": "All",
|
||||
"admin": "Admin",
|
||||
"user": "Regular User"
|
||||
},
|
||||
"sort": "Sort By",
|
||||
"sortOptions": {
|
||||
"username": "Username",
|
||||
"created": "Creation Date"
|
||||
},
|
||||
"button": "Search",
|
||||
"searching": "Searching..."
|
||||
},
|
||||
"pagination": {
|
||||
"prev": "Previous",
|
||||
"next": "Next"
|
||||
},
|
||||
"modal": {
|
||||
"add": "Add New User",
|
||||
"close": "Close",
|
||||
"username": "Username",
|
||||
"nickname": "Nickname",
|
||||
"password": "Password",
|
||||
"isAdmin": "Admin Privileges",
|
||||
"cancel": "Cancel",
|
||||
"save": "Save User",
|
||||
"saving": "Saving..."
|
||||
},
|
||||
"modify": {
|
||||
"title": "Modify User Info",
|
||||
"subtitle": "User Information",
|
||||
"isAdmin": "Admin Privileges: ",
|
||||
"disableGacha": "Disable Gacha System: ",
|
||||
"disableGachaHint": "Allows all characters to have equal chances of getting pulled",
|
||||
"isBanned": "Banned:",
|
||||
"cheats": "Cheat Functions",
|
||||
"campaign": "Campaign:",
|
||||
"skipStages": "Skip Stages",
|
||||
"characters": "Characters:",
|
||||
"addAllChars": "Add All Characters",
|
||||
"addChar": "Add Character",
|
||||
"setCharLevels": "Set Character Levels",
|
||||
"setSkillLevels": "Set Skill Levels",
|
||||
"setCoreLevel": "Set Core Level",
|
||||
"inventory": "Inventory:",
|
||||
"addAllEquip": "Add All Equipment",
|
||||
"addItem": "Add Item",
|
||||
"misc": "Miscellaneous:",
|
||||
"finishTutorials": "Finish All Tutorials",
|
||||
"backToList": "Back to List"
|
||||
},
|
||||
"password": {
|
||||
"title": "Change Password",
|
||||
"changeButton": "Change Password",
|
||||
"backToList": "Back to List",
|
||||
"enterNewPassword": "Enter new password",
|
||||
"hint": "Please enter a strong password with letters, numbers and special characters"
|
||||
},
|
||||
"delete": {
|
||||
"title": "Delete User",
|
||||
"confirmation": "Are you sure you want to delete this user?",
|
||||
"backToList": "Back to List"
|
||||
},
|
||||
"notifications": {
|
||||
"searchDone": "Search completed",
|
||||
"requiredFields": "Please fill in the required fields",
|
||||
"userAdded": "User added successfully! Please refresh the page to view."
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"config": {
|
||||
"title": "Event Configuration",
|
||||
"comingSoon": "Coming Soon!"
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"title": "In-game Mail",
|
||||
"comingSoon": "Coming Soon!"
|
||||
},
|
||||
"config": {
|
||||
"server": {
|
||||
"title": "Server Configuration",
|
||||
"logLevel": "Log Level:"
|
||||
},
|
||||
"database": {
|
||||
"title": "Database Configuration",
|
||||
"reload": "Reload Database",
|
||||
"reloadHint": "Loads changes from db.json into memory. Discards unsaved changes."
|
||||
}
|
||||
}
|
||||
}
|
||||
215
EpinelPS/wwwroot/admin/assets/i18n/ja.json
Normal file
215
EpinelPS/wwwroot/admin/assets/i18n/ja.json
Normal file
@@ -0,0 +1,215 @@
|
||||
{
|
||||
"app": {
|
||||
"name": "NIKKEアドミンコンソール",
|
||||
"version": "バージョン v2.5.3",
|
||||
"footer": "© 2025 - NIKKE: 勝利の女神 - アドミンコンソール"
|
||||
},
|
||||
"auth": {
|
||||
"login": "ログイン",
|
||||
"username": "ユーザー名",
|
||||
"password": "パスワード",
|
||||
"enter": "コンソールに入る",
|
||||
"welcome": "管理機能にアクセスするにはログインしてください",
|
||||
"verifying": "確認中...",
|
||||
"success": "ログイン成功",
|
||||
"error": {
|
||||
"required": "ユーザー名とパスワードを入力してください",
|
||||
"invalid": "ログインに失敗しました。認証情報を確認してください",
|
||||
"network": "ネットワークエラー。後ほど再試行してください"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"loading": "読み込み中...",
|
||||
"confirm": "確認",
|
||||
"cancel": "キャンセル",
|
||||
"save": "保存",
|
||||
"delete": "削除",
|
||||
"edit": "編集",
|
||||
"actions": "アクション",
|
||||
"search": "検索",
|
||||
"status": {
|
||||
"online": "オンライン",
|
||||
"offline": "オフライン"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "システム通知",
|
||||
"dashboardRefreshed": "ダッシュボードデータが正常に更新されました"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
"dashboard": "ダッシュボード",
|
||||
"events": "イベント",
|
||||
"users": "ユーザー",
|
||||
"mail": "メール",
|
||||
"config": "設定",
|
||||
"database": "データベース",
|
||||
"profile": "プロフィール",
|
||||
"changePass": "パスワード変更",
|
||||
"logout": "ログアウト"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "コンソール概要",
|
||||
"refresh": "データを更新",
|
||||
"refreshing": "更新中...",
|
||||
"statsCards": {
|
||||
"users": "登録ユーザー",
|
||||
"server": "サーバーステータス",
|
||||
"events": "アクティブイベント",
|
||||
"mail": "今日のメール"
|
||||
},
|
||||
"charts": {
|
||||
"activity": "ユーザーアクティビティ傾向",
|
||||
"server": "サーバーリソース使用状況",
|
||||
"periods": {
|
||||
"day": "日",
|
||||
"week": "週",
|
||||
"month": "月"
|
||||
}
|
||||
},
|
||||
"serverStats": {
|
||||
"cpu": "CPU使用率",
|
||||
"memory": "メモリ使用率",
|
||||
"storage": "ストレージ使用率"
|
||||
},
|
||||
"activity": {
|
||||
"title": "最近のアクティビティ",
|
||||
"newUser": "新規ユーザー登録",
|
||||
"loginAlert": "異常なログインを検出",
|
||||
"configUpdate": "システム設定が更新されました",
|
||||
"rewards": "全体報酬を配布しました",
|
||||
"newEvent": "新しいイベントが作成されました",
|
||||
"bugfix": "ゲームのバグを修正しました",
|
||||
"timeFormat": {
|
||||
"min": "分前",
|
||||
"hour": "時間前",
|
||||
"yesterday": "昨日",
|
||||
"daysAgo": "日前"
|
||||
}
|
||||
},
|
||||
"quickAccess": {
|
||||
"users": {
|
||||
"title": "ユーザー管理",
|
||||
"desc": "ゲームユーザーアカウント、権限、ロールを管理"
|
||||
},
|
||||
"events": {
|
||||
"title": "イベント管理",
|
||||
"desc": "ゲームイベントの作成、編集、監視"
|
||||
},
|
||||
"mail": {
|
||||
"title": "メールシステム",
|
||||
"desc": "プレイヤーにシステムメールと報酬を送信"
|
||||
},
|
||||
"database": {
|
||||
"title": "データベース",
|
||||
"desc": "ゲームデータベースの管理と保守"
|
||||
},
|
||||
"enter": "入る"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"title": "ユーザー管理",
|
||||
"add": "新規ユーザー追加",
|
||||
"table": {
|
||||
"id": "ID",
|
||||
"username": "ユーザー名",
|
||||
"nickname": "ニックネーム",
|
||||
"playerName": "プレイヤー名",
|
||||
"isAdmin": "権限",
|
||||
"isBanned": "禁止",
|
||||
"actions": "アクション"
|
||||
},
|
||||
"search": {
|
||||
"username": "ユーザー名",
|
||||
"usernamePlaceholder": "ユーザー名を検索...",
|
||||
"userType": "ユーザータイプ",
|
||||
"types": {
|
||||
"all": "すべて",
|
||||
"admin": "管理者",
|
||||
"user": "一般ユーザー"
|
||||
},
|
||||
"sort": "並び替え",
|
||||
"sortOptions": {
|
||||
"username": "ユーザー名",
|
||||
"created": "作成日"
|
||||
},
|
||||
"button": "検索",
|
||||
"searching": "検索中..."
|
||||
},
|
||||
"pagination": {
|
||||
"prev": "前へ",
|
||||
"next": "次へ"
|
||||
},
|
||||
"modal": {
|
||||
"add": "新規ユーザー追加",
|
||||
"close": "閉じる",
|
||||
"username": "ユーザー名",
|
||||
"nickname": "ニックネーム",
|
||||
"password": "パスワード",
|
||||
"isAdmin": "管理者権限",
|
||||
"cancel": "キャンセル",
|
||||
"save": "ユーザーを保存",
|
||||
"saving": "保存中..."
|
||||
},
|
||||
"modify": {
|
||||
"title": "ユーザー情報の変更",
|
||||
"subtitle": "ユーザー情報",
|
||||
"isAdmin": "管理者権限: ",
|
||||
"disableGacha": "ガチャシステムを無効化: ",
|
||||
"disableGachaHint": "すべてのキャラクターが同じ確率で排出されるようになります",
|
||||
"isBanned": "禁止:",
|
||||
"cheats": "チート機能",
|
||||
"campaign": "キャンペーン:",
|
||||
"skipStages": "ステージスキップ",
|
||||
"characters": "キャラクター:",
|
||||
"addAllChars": "すべてのキャラクターを追加",
|
||||
"addChar": "キャラクター追加",
|
||||
"setCharLevels": "キャラクターレベル設定",
|
||||
"setSkillLevels": "スキルレベル設定",
|
||||
"setCoreLevel": "コアレベル設定",
|
||||
"inventory": "インベントリ:",
|
||||
"addAllEquip": "すべての装備を追加",
|
||||
"addItem": "アイテム追加",
|
||||
"misc": "その他:",
|
||||
"finishTutorials": "すべてのチュートリアルを完了",
|
||||
"backToList": "一覧に戻る"
|
||||
},
|
||||
"password": {
|
||||
"title": "パスワード変更",
|
||||
"changeButton": "パスワード変更",
|
||||
"backToList": "リストに戻る",
|
||||
"enterNewPassword": "新しいパスワードを入力",
|
||||
"hint": "文字、数字、特殊文字を含む強力なパスワードを入力してください"
|
||||
},
|
||||
"delete": {
|
||||
"title": "ユーザー削除",
|
||||
"confirmation": "このユーザーを削除してもよろしいですか?",
|
||||
"backToList": "一覧に戻る"
|
||||
},
|
||||
"notifications": {
|
||||
"searchDone": "検索完了",
|
||||
"requiredFields": "必須フィールドに入力してください",
|
||||
"userAdded": "ユーザーが正常に追加されました!ページを更新して確認してください。"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"config": {
|
||||
"title": "イベント設定",
|
||||
"comingSoon": "近日公開!"
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"title": "ゲーム内メール",
|
||||
"comingSoon": "近日公開!"
|
||||
},
|
||||
"config": {
|
||||
"server": {
|
||||
"title": "サーバー設定",
|
||||
"logLevel": "ログレベル:"
|
||||
},
|
||||
"database": {
|
||||
"title": "データベース設定",
|
||||
"reload": "データベースを再読み込み",
|
||||
"reloadHint": "db.jsonからの変更をメモリに読み込みます。保存されていない変更は破棄されます。"
|
||||
}
|
||||
}
|
||||
}
|
||||
215
EpinelPS/wwwroot/admin/assets/i18n/ko.json
Normal file
215
EpinelPS/wwwroot/admin/assets/i18n/ko.json
Normal file
@@ -0,0 +1,215 @@
|
||||
{
|
||||
"app": {
|
||||
"name": "NIKKE 관리 콘솔",
|
||||
"version": "버전 v2.5.3",
|
||||
"footer": "© 2025 - NIKKE: 승리의 여신 - 관리 콘솔"
|
||||
},
|
||||
"auth": {
|
||||
"login": "로그인",
|
||||
"username": "사용자 이름",
|
||||
"password": "비밀번호",
|
||||
"enter": "콘솔 입장",
|
||||
"welcome": "관리 기능에 접근하려면 로그인하세요",
|
||||
"verifying": "확인 중...",
|
||||
"success": "로그인 성공",
|
||||
"error": {
|
||||
"required": "사용자 이름과 비밀번호를 입력하세요",
|
||||
"invalid": "로그인 실패, 자격 증명을 확인하세요",
|
||||
"network": "네트워크 오류, 나중에 다시 시도하세요"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"loading": "로딩 중...",
|
||||
"confirm": "확인",
|
||||
"cancel": "취소",
|
||||
"save": "저장",
|
||||
"delete": "삭제",
|
||||
"edit": "편집",
|
||||
"actions": "작업",
|
||||
"search": "검색",
|
||||
"status": {
|
||||
"online": "온라인",
|
||||
"offline": "오프라인"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "시스템 알림",
|
||||
"dashboardRefreshed": "대시보드 데이터가 성공적으로 새로고침되었습니다"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
"dashboard": "대시보드",
|
||||
"events": "이벤트",
|
||||
"users": "사용자",
|
||||
"mail": "메일",
|
||||
"config": "구성",
|
||||
"database": "데이터베이스",
|
||||
"profile": "프로필",
|
||||
"changePass": "비밀번호 변경",
|
||||
"logout": "로그아웃"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "콘솔 개요",
|
||||
"refresh": "데이터 새로고침",
|
||||
"refreshing": "새로고침 중...",
|
||||
"statsCards": {
|
||||
"users": "등록된 사용자",
|
||||
"server": "서버 상태",
|
||||
"events": "활성 이벤트",
|
||||
"mail": "오늘의 메일"
|
||||
},
|
||||
"charts": {
|
||||
"activity": "사용자 활동 추세",
|
||||
"server": "서버 리소스 사용량",
|
||||
"periods": {
|
||||
"day": "일",
|
||||
"week": "주",
|
||||
"month": "월"
|
||||
}
|
||||
},
|
||||
"serverStats": {
|
||||
"cpu": "CPU 사용량",
|
||||
"memory": "메모리 사용량",
|
||||
"storage": "저장소 사용량"
|
||||
},
|
||||
"activity": {
|
||||
"title": "최근 활동",
|
||||
"newUser": "새 사용자 등록",
|
||||
"loginAlert": "비정상 로그인 감지됨",
|
||||
"configUpdate": "시스템 구성 업데이트됨",
|
||||
"rewards": "전체 보상 배포됨",
|
||||
"newEvent": "새 이벤트 생성됨",
|
||||
"bugfix": "게임 버그 수정됨",
|
||||
"timeFormat": {
|
||||
"min": "분 전",
|
||||
"hour": "시간 전",
|
||||
"yesterday": "어제",
|
||||
"daysAgo": "일 전"
|
||||
}
|
||||
},
|
||||
"quickAccess": {
|
||||
"users": {
|
||||
"title": "사용자 관리",
|
||||
"desc": "게임 사용자 계정, 권한 및 역할 관리"
|
||||
},
|
||||
"events": {
|
||||
"title": "이벤트 관리",
|
||||
"desc": "게임 이벤트 생성, 편집 및 모니터링"
|
||||
},
|
||||
"mail": {
|
||||
"title": "메일 시스템",
|
||||
"desc": "플레이어에게 시스템 메일 및 보상 전송"
|
||||
},
|
||||
"database": {
|
||||
"title": "데이터베이스",
|
||||
"desc": "게임 데이터베이스 관리 및 유지보수"
|
||||
},
|
||||
"enter": "입장"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"title": "사용자 관리",
|
||||
"add": "새 사용자 추가",
|
||||
"table": {
|
||||
"id": "ID",
|
||||
"username": "사용자 이름",
|
||||
"nickname": "닉네임",
|
||||
"playerName": "플레이어 이름",
|
||||
"isAdmin": "권한",
|
||||
"isBanned": "차단됨",
|
||||
"actions": "작업"
|
||||
},
|
||||
"search": {
|
||||
"username": "사용자 이름",
|
||||
"usernamePlaceholder": "사용자 이름 검색...",
|
||||
"userType": "사용자 유형",
|
||||
"types": {
|
||||
"all": "전체",
|
||||
"admin": "관리자",
|
||||
"user": "일반 사용자"
|
||||
},
|
||||
"sort": "정렬 기준",
|
||||
"sortOptions": {
|
||||
"username": "사용자 이름",
|
||||
"created": "생성일"
|
||||
},
|
||||
"button": "검색",
|
||||
"searching": "검색 중..."
|
||||
},
|
||||
"pagination": {
|
||||
"prev": "이전",
|
||||
"next": "다음"
|
||||
},
|
||||
"modal": {
|
||||
"add": "새 사용자 추가",
|
||||
"close": "닫기",
|
||||
"username": "사용자 이름",
|
||||
"nickname": "닉네임",
|
||||
"password": "비밀번호",
|
||||
"isAdmin": "관리자 권한",
|
||||
"cancel": "취소",
|
||||
"save": "사용자 저장",
|
||||
"saving": "저장 중..."
|
||||
},
|
||||
"modify": {
|
||||
"title": "사용자 정보 수정",
|
||||
"subtitle": "사용자 정보",
|
||||
"isAdmin": "관리자 권한: ",
|
||||
"disableGacha": "가챠 시스템 비활성화: ",
|
||||
"disableGachaHint": "모든 캐릭터가 동일한 확률로 출현하도록 합니다",
|
||||
"isBanned": "차단됨:",
|
||||
"cheats": "치트 기능",
|
||||
"campaign": "캠페인:",
|
||||
"skipStages": "스테이지 건너뛰기",
|
||||
"characters": "캐릭터:",
|
||||
"addAllChars": "모든 캐릭터 추가",
|
||||
"addChar": "캐릭터 추가",
|
||||
"setCharLevels": "캐릭터 레벨 설정",
|
||||
"setSkillLevels": "스킬 레벨 설정",
|
||||
"setCoreLevel": "코어 레벨 설정",
|
||||
"inventory": "인벤토리:",
|
||||
"addAllEquip": "모든 장비 추가",
|
||||
"addItem": "아이템 추가",
|
||||
"misc": "기타:",
|
||||
"finishTutorials": "모든 튜토리얼 완료",
|
||||
"backToList": "목록으로 돌아가기"
|
||||
},
|
||||
"password": {
|
||||
"title": "비밀번호 변경",
|
||||
"changeButton": "비밀번호 변경",
|
||||
"backToList": "목록으로 돌아가기",
|
||||
"enterNewPassword": "새 비밀번호 입력",
|
||||
"hint": "문자, 숫자 및 특수 문자를 포함한 강력한 비밀번호를 입력하세요"
|
||||
},
|
||||
"delete": {
|
||||
"title": "사용자 삭제",
|
||||
"confirmation": "이 사용자를 삭제하시겠습니까?",
|
||||
"backToList": "목록으로 돌아가기"
|
||||
},
|
||||
"notifications": {
|
||||
"searchDone": "검색 완료",
|
||||
"requiredFields": "필수 필드를 작성해주세요",
|
||||
"userAdded": "사용자가 성공적으로 추가되었습니다! 페이지를 새로고침하여 확인하세요."
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"config": {
|
||||
"title": "이벤트 구성",
|
||||
"comingSoon": "곧 출시 예정!"
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"title": "게임 내 메일",
|
||||
"comingSoon": "곧 출시 예정!"
|
||||
},
|
||||
"config": {
|
||||
"server": {
|
||||
"title": "서버 구성",
|
||||
"logLevel": "로그 레벨:"
|
||||
},
|
||||
"database": {
|
||||
"title": "데이터베이스 구성",
|
||||
"reload": "데이터베이스 다시 로드",
|
||||
"reloadHint": "db.json의 변경사항을 메모리에 로드합니다. 저장되지 않은 변경사항은 버려집니다."
|
||||
}
|
||||
}
|
||||
}
|
||||
215
EpinelPS/wwwroot/admin/assets/i18n/zh.json
Normal file
215
EpinelPS/wwwroot/admin/assets/i18n/zh.json
Normal file
@@ -0,0 +1,215 @@
|
||||
{
|
||||
"app": {
|
||||
"name": "胜利女神控制台",
|
||||
"version": "版本 v2.5.3",
|
||||
"footer": "© 2025 - NIKKE: 胜利女神 - 管理控制台"
|
||||
},
|
||||
"auth": {
|
||||
"login": "登录",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"enter": "进入控制台",
|
||||
"welcome": "请登录以访问管理功能",
|
||||
"verifying": "验证中...",
|
||||
"success": "登录成功",
|
||||
"error": {
|
||||
"required": "请输入用户名和密码",
|
||||
"invalid": "登录失败,请检查用户名和密码",
|
||||
"network": "网络错误,请稍后重试"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"loading": "加载中...",
|
||||
"confirm": "确认",
|
||||
"cancel": "取消",
|
||||
"save": "保存",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"actions": "操作",
|
||||
"search": "搜索",
|
||||
"status": {
|
||||
"online": "在线",
|
||||
"offline": "离线"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "系统通知",
|
||||
"dashboardRefreshed": "仪表盘数据已成功刷新"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
"dashboard": "仪表盘",
|
||||
"events": "活动",
|
||||
"users": "用户",
|
||||
"mail": "邮件",
|
||||
"config": "配置",
|
||||
"database": "数据库",
|
||||
"profile": "个人信息",
|
||||
"changePass": "修改密码",
|
||||
"logout": "退出登录"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "控制台概览",
|
||||
"refresh": "刷新数据",
|
||||
"refreshing": "刷新中...",
|
||||
"statsCards": {
|
||||
"users": "注册用户",
|
||||
"server": "服务器状态",
|
||||
"events": "活动进行中",
|
||||
"mail": "今日邮件"
|
||||
},
|
||||
"charts": {
|
||||
"activity": "用户活跃度趋势",
|
||||
"server": "服务器资源使用",
|
||||
"periods": {
|
||||
"day": "日",
|
||||
"week": "周",
|
||||
"month": "月"
|
||||
}
|
||||
},
|
||||
"serverStats": {
|
||||
"cpu": "CPU 使用率",
|
||||
"memory": "内存使用率",
|
||||
"storage": "存储使用率"
|
||||
},
|
||||
"activity": {
|
||||
"title": "最近活动",
|
||||
"newUser": "新用户注册",
|
||||
"loginAlert": "检测到异常登录",
|
||||
"configUpdate": "系统配置已更新",
|
||||
"rewards": "发放全服奖励",
|
||||
"newEvent": "新活动已创建",
|
||||
"bugfix": "修复游戏漏洞",
|
||||
"timeFormat": {
|
||||
"min": "分钟前",
|
||||
"hour": "小时前",
|
||||
"yesterday": "昨天",
|
||||
"daysAgo": "前天"
|
||||
}
|
||||
},
|
||||
"quickAccess": {
|
||||
"users": {
|
||||
"title": "用户管理",
|
||||
"desc": "管理游戏用户账号,权限和角色"
|
||||
},
|
||||
"events": {
|
||||
"title": "活动管理",
|
||||
"desc": "创建、编辑和监控游戏活动"
|
||||
},
|
||||
"mail": {
|
||||
"title": "邮件系统",
|
||||
"desc": "发送系统邮件和奖励给玩家"
|
||||
},
|
||||
"database": {
|
||||
"title": "数据库",
|
||||
"desc": "管理和维护游戏数据库"
|
||||
},
|
||||
"enter": "进入"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"title": "用户管理",
|
||||
"add": "添加新用户",
|
||||
"table": {
|
||||
"id": "ID",
|
||||
"username": "用户名",
|
||||
"nickname": "昵称",
|
||||
"playerName": "玩家名称",
|
||||
"isAdmin": "权限",
|
||||
"isBanned": "禁止登录",
|
||||
"actions": "操作"
|
||||
},
|
||||
"search": {
|
||||
"username": "用户名",
|
||||
"usernamePlaceholder": "搜索用户名...",
|
||||
"userType": "用户类型",
|
||||
"types": {
|
||||
"all": "全部",
|
||||
"admin": "管理员",
|
||||
"user": "普通用户"
|
||||
},
|
||||
"sort": "排序方式",
|
||||
"sortOptions": {
|
||||
"username": "用户名",
|
||||
"created": "创建时间"
|
||||
},
|
||||
"button": "搜索",
|
||||
"searching": "搜索中..."
|
||||
},
|
||||
"pagination": {
|
||||
"prev": "上一页",
|
||||
"next": "下一页"
|
||||
},
|
||||
"modal": {
|
||||
"add": "添加新用户",
|
||||
"close": "关闭",
|
||||
"username": "用户名",
|
||||
"nickname": "昵称",
|
||||
"password": "密码",
|
||||
"isAdmin": "管理员权限",
|
||||
"cancel": "取消",
|
||||
"save": "保存用户",
|
||||
"saving": "保存中..."
|
||||
},
|
||||
"modify": {
|
||||
"title": "修改用户信息",
|
||||
"subtitle": "用户信息",
|
||||
"isAdmin": "管理员权限: ",
|
||||
"disableGacha": "禁用抽卡系统: ",
|
||||
"disableGachaHint": "允许所有角色有相同概率被抽到",
|
||||
"isBanned": "禁止登录:",
|
||||
"cheats": "作弊功能",
|
||||
"campaign": "战役:",
|
||||
"skipStages": "跳过关卡",
|
||||
"characters": "角色:",
|
||||
"addAllChars": "添加所有角色",
|
||||
"addChar": "添加角色",
|
||||
"setCharLevels": "设置角色等级",
|
||||
"setSkillLevels": "设置技能等级",
|
||||
"setCoreLevel": "设置核心等级",
|
||||
"inventory": "物品:",
|
||||
"addAllEquip": "添加所有装备",
|
||||
"addItem": "添加物品",
|
||||
"misc": "其他:",
|
||||
"finishTutorials": "完成所有教程",
|
||||
"backToList": "返回列表"
|
||||
},
|
||||
"password": {
|
||||
"title": "修改密码",
|
||||
"changeButton": "修改密码",
|
||||
"backToList": "返回列表",
|
||||
"enterNewPassword": "输入新密码",
|
||||
"hint": "请输入强密码,包含字母、数字和特殊字符"
|
||||
},
|
||||
"delete": {
|
||||
"title": "删除用户",
|
||||
"confirmation": "您确定要删除这个用户吗?",
|
||||
"backToList": "返回列表"
|
||||
},
|
||||
"notifications": {
|
||||
"searchDone": "搜索完成",
|
||||
"requiredFields": "请填写必填字段",
|
||||
"userAdded": "用户添加成功!请刷新页面查看。"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"config": {
|
||||
"title": "活动配置",
|
||||
"comingSoon": "即将上线!"
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"title": "游戏内邮件",
|
||||
"comingSoon": "即将上线!"
|
||||
},
|
||||
"config": {
|
||||
"server": {
|
||||
"title": "服务器配置",
|
||||
"logLevel": "日志级别:"
|
||||
},
|
||||
"database": {
|
||||
"title": "数据库配置",
|
||||
"reload": "重新加载数据库",
|
||||
"reloadHint": "从db.json加载更改到内存中。未保存的更改将丢失。"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
BIN
EpinelPS/wwwroot/admin/assets/img/nikke-bg-pattern.png
Normal file
BIN
EpinelPS/wwwroot/admin/assets/img/nikke-bg-pattern.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 239 KiB |
357
EpinelPS/wwwroot/admin/assets/js/nikke-i18n.js
Normal file
357
EpinelPS/wwwroot/admin/assets/js/nikke-i18n.js
Normal file
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* NIKKE控制台国际化支持 - 同步版
|
||||
*/
|
||||
class NikkeI18n {
|
||||
constructor() {
|
||||
this.supportedLanguages = ['zh', 'en', 'ja', 'ko'];
|
||||
this.languageNames = {'zh': '简体中文', 'en': 'English', 'ja': '日本語', 'ko': '한국어'};
|
||||
this.currentLang = localStorage.getItem('nikke_lang') || this.detectBrowserLanguage() || 'zh';
|
||||
this.resources = {
|
||||
};
|
||||
this.isApplyingLanguage = false;
|
||||
this.isRenderingSwitcher = false;
|
||||
this.observer = null;
|
||||
|
||||
// 立即同步加载所有语言文件
|
||||
this.loadAllLanguages();
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
try {
|
||||
document.documentElement.classList.add('lang-loading');
|
||||
this.applyLanguage();
|
||||
this.registerEventListeners();
|
||||
this.renderLanguageSwitcher();
|
||||
window.dispatchEvent(new CustomEvent('nikke:i18n-ready'));
|
||||
} catch (error) {
|
||||
console.error('初始化失败:', error);
|
||||
this.fallbackToDefaultLanguage();
|
||||
} finally {
|
||||
document.documentElement.classList.remove('lang-loading');
|
||||
}
|
||||
}
|
||||
|
||||
detectBrowserLanguage() {
|
||||
try {
|
||||
const browserLang = navigator.language || navigator.userLanguage;
|
||||
if (!browserLang) return 'en';
|
||||
const baseLang = browserLang.split('-')[0];
|
||||
return this.supportedLanguages.includes(baseLang) ? baseLang : 'en';
|
||||
} catch {
|
||||
return 'en';
|
||||
}
|
||||
}
|
||||
|
||||
fallbackToDefaultLanguage() {
|
||||
this.currentLang = 'en';
|
||||
try {
|
||||
localStorage.setItem('nikke_lang', this.currentLang);
|
||||
} catch (e) {}
|
||||
this.applyLanguage();
|
||||
}
|
||||
|
||||
loadAllLanguages() {
|
||||
// 同步预加载所有语言文件
|
||||
for (const lang of this.supportedLanguages) {
|
||||
if (this.resources[lang]) continue;
|
||||
|
||||
try {
|
||||
// 使用同步XMLHttpRequest加载语言文件
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', `/admin/assets/i18n/${lang}.json`, false); // false表示同步请求
|
||||
xhr.send();
|
||||
|
||||
if (xhr.status === 200) {
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
if (data && typeof data === 'object') {
|
||||
this.resources[lang] = data;
|
||||
} else {
|
||||
throw new Error('格式无效');
|
||||
}
|
||||
} else {
|
||||
throw new Error(`加载失败: ${lang} (${xhr.status})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`加载语言文件失败: ${lang}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 确保当前语言已加载
|
||||
if (!this.resources[this.currentLang]) {
|
||||
// 当前语言未加载成功,回退到英文
|
||||
console.warn(`当前语言 ${this.currentLang} 加载失败,回退到英文`);
|
||||
this.currentLang = 'en';
|
||||
try {
|
||||
localStorage.setItem('nikke_lang', 'en');
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
switchLanguage(lang) {
|
||||
if (!this.supportedLanguages.includes(lang)) return;
|
||||
|
||||
try {
|
||||
document.documentElement.classList.add('lang-loading');
|
||||
|
||||
// 确保语言资源已加载
|
||||
if (!this.resources[lang]) {
|
||||
try {
|
||||
// 尝试同步加载缺失的语言资源
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', `/admin/assets/i18n/${lang}.json`, false);
|
||||
xhr.send();
|
||||
|
||||
if (xhr.status === 200) {
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
if (data && typeof data === 'object') {
|
||||
this.resources[lang] = data;
|
||||
} else {
|
||||
throw new Error('格式无效');
|
||||
}
|
||||
} else {
|
||||
throw new Error(`加载失败: ${lang} (${xhr.status})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`切换到语言 ${lang} 时加载失败`, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.currentLang = lang;
|
||||
localStorage.setItem('nikke_lang', lang);
|
||||
this.applyLanguage();
|
||||
this.updateLanguageSwitcher();
|
||||
window.dispatchEvent(new CustomEvent('nikke:languageChanged', {detail: {lang}}));
|
||||
} catch (error) {
|
||||
console.error(`切换失败: ${lang}`, error);
|
||||
} finally {
|
||||
document.documentElement.classList.remove('lang-loading');
|
||||
}
|
||||
}
|
||||
|
||||
t(key, params = {}) {
|
||||
try {
|
||||
if (!this.resources[this.currentLang]) return key;
|
||||
|
||||
const keys = key.split('.');
|
||||
let value = this.resources[this.currentLang];
|
||||
|
||||
for (const k of keys) {
|
||||
value = value?.[k];
|
||||
if (value === undefined) {
|
||||
if (this.resources['en']) {
|
||||
let enValue = this.resources['en'];
|
||||
for (const k of keys) {
|
||||
enValue = enValue?.[k];
|
||||
if (enValue === undefined) return key;
|
||||
}
|
||||
return typeof enValue === 'string' ? this.replaceParams(enValue, params) : key;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof value === 'string' ? this.replaceParams(value, params) : key;
|
||||
} catch {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
replaceParams(text, params) {
|
||||
if (!text) return '';
|
||||
return text.replace(/\{\{([^}]+)}}/g, (_, key) =>
|
||||
params[key.trim()] !== undefined ? params[key.trim()] : `{{${key}}}`
|
||||
);
|
||||
}
|
||||
|
||||
applyLanguage() {
|
||||
if (this.isApplyingLanguage) return;
|
||||
this.isApplyingLanguage = true;
|
||||
|
||||
try {
|
||||
if (this.observer) this.observer.disconnect();
|
||||
|
||||
document.querySelectorAll('[data-i18n]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n');
|
||||
element.textContent = this.t(key);
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-placeholder');
|
||||
element.placeholder = this.t(key);
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-i18n-title]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-title');
|
||||
element.title = this.t(key);
|
||||
});
|
||||
|
||||
document.documentElement.lang = this.currentLang;
|
||||
} catch (error) {
|
||||
console.error('应用语言失败:', error);
|
||||
} finally {
|
||||
if (this.observer) {
|
||||
const mainContent = document.querySelector('.login-container') || document.body;
|
||||
this.observer.observe(mainContent, {childList: true, subtree: true});
|
||||
}
|
||||
this.isApplyingLanguage = false;
|
||||
}
|
||||
}
|
||||
|
||||
registerEventListeners() {
|
||||
if (this.observer) {
|
||||
this.observer.disconnect();
|
||||
this.observer = null;
|
||||
}
|
||||
|
||||
this.observer = new MutationObserver(mutations => {
|
||||
if (this.isApplyingLanguage || this.isRenderingSwitcher) return;
|
||||
|
||||
const hasRelevantChanges = mutations.some(mutation => {
|
||||
if (mutation.type !== 'childList' || !mutation.addedNodes.length) return false;
|
||||
|
||||
return Array.from(mutation.addedNodes).some(node => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return false;
|
||||
|
||||
if (node.classList && (
|
||||
node.classList.contains('language-switcher') ||
|
||||
node.id === 'nikke-i18n-styles'
|
||||
)) return false;
|
||||
|
||||
return (
|
||||
(node.hasAttribute && (
|
||||
node.hasAttribute('data-i18n') ||
|
||||
node.hasAttribute('data-i18n-placeholder') ||
|
||||
node.hasAttribute('data-i18n-title')
|
||||
)) ||
|
||||
(node.querySelector && node.querySelector('[data-i18n], [data-i18n-placeholder], [data-i18n-title]'))
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
if (hasRelevantChanges && !this.isApplyingLanguage) {
|
||||
this.applyLanguage();
|
||||
}
|
||||
});
|
||||
|
||||
const mainContent = document.querySelector('.login-container, .nikke-dashboard') || document.body;
|
||||
this.observer.observe(mainContent, {childList: true, subtree: true});
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => this.applyLanguage());
|
||||
} else {
|
||||
this.applyLanguage();
|
||||
}
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) this.applyLanguage();
|
||||
});
|
||||
}
|
||||
|
||||
renderLanguageSwitcher() {
|
||||
if (this.isRenderingSwitcher) return;
|
||||
this.isRenderingSwitcher = true;
|
||||
|
||||
try {
|
||||
if (document.querySelector('.language-switcher')) {
|
||||
this.isRenderingSwitcher = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.observer) this.observer.disconnect();
|
||||
|
||||
const switcherContainer = document.createElement('div');
|
||||
switcherContainer.className = 'language-switcher';
|
||||
switcherContainer.innerHTML = `
|
||||
<div class="language-switcher-toggle">
|
||||
<i class="fas fa-globe"></i>
|
||||
<span class="current-language">${this.languageNames[this.currentLang]}</span>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</div>
|
||||
<div class="language-options"></div>
|
||||
`;
|
||||
|
||||
const languageOptions = switcherContainer.querySelector('.language-options');
|
||||
this.supportedLanguages.forEach(lang => {
|
||||
const option = document.createElement('div');
|
||||
option.className = `language-option ${lang === this.currentLang ? 'active' : ''}`;
|
||||
option.setAttribute('data-lang', lang);
|
||||
option.innerHTML = `
|
||||
<span class="language-name">${this.languageNames[lang]}</span>
|
||||
<span class="language-code">${lang.toUpperCase()}</span>
|
||||
`;
|
||||
|
||||
option.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.switchLanguage(lang);
|
||||
switcherContainer.classList.remove('open');
|
||||
});
|
||||
|
||||
languageOptions.appendChild(option);
|
||||
});
|
||||
|
||||
const toggle = switcherContainer.querySelector('.language-switcher-toggle');
|
||||
toggle.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
switcherContainer.classList.toggle('open');
|
||||
});
|
||||
|
||||
// 点击外部区域关闭语言选择器
|
||||
const clickOutside = (e) => {
|
||||
if (!switcherContainer.contains(e.target)) {
|
||||
switcherContainer.classList.remove('open');
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', clickOutside);
|
||||
|
||||
// 找到合适的插入位置
|
||||
let target = document.getElementById('navbar-language-switcher');
|
||||
if (target) {
|
||||
target.appendChild(switcherContainer);
|
||||
switcherContainer.classList.add('navbar-integrated');
|
||||
} else {
|
||||
// 后备插入到body
|
||||
document.body.appendChild(switcherContainer);
|
||||
}
|
||||
|
||||
// 恢复DOM观察
|
||||
if (this.observer) {
|
||||
const mainContent = document.querySelector('.login-container') || document.body;
|
||||
this.observer.observe(mainContent, {childList: true, subtree: true});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('渲染语言切换器失败:', error);
|
||||
} finally {
|
||||
this.isRenderingSwitcher = false;
|
||||
}
|
||||
}
|
||||
|
||||
updateLanguageSwitcher() {
|
||||
try {
|
||||
const switcher = document.querySelector('.language-switcher');
|
||||
if (!switcher) return;
|
||||
|
||||
const currentLanguageSpan = switcher.querySelector('.current-language');
|
||||
if (currentLanguageSpan) {
|
||||
currentLanguageSpan.textContent = this.languageNames[this.currentLang];
|
||||
}
|
||||
|
||||
const options = switcher.querySelectorAll('.language-option');
|
||||
options.forEach(option => {
|
||||
const lang = option.getAttribute('data-lang');
|
||||
if (lang === this.currentLang) {
|
||||
option.classList.add('active');
|
||||
} else {
|
||||
option.classList.remove('active');
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('更新语言切换器失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化
|
||||
window.i18n = new NikkeI18n();
|
||||
38
EpinelPS/wwwroot/admin/assets/js/nikke-login.js
Normal file
38
EpinelPS/wwwroot/admin/assets/js/nikke-login.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// NIKKE登录页专用脚本
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 设置表单提交事件
|
||||
const form = document.getElementById('loginForm');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
AdminLogin();
|
||||
});
|
||||
}
|
||||
|
||||
// 允许按回车键提交
|
||||
const passwordInput = document.getElementById('PasswordBox');
|
||||
if (passwordInput) {
|
||||
passwordInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
AdminLogin();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 检查是否已经登录
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
// 已登录用户直接跳转
|
||||
// window.location.pathname = "/admin/dashboard";
|
||||
}
|
||||
|
||||
// 添加动画效果
|
||||
setTimeout(function() {
|
||||
const loginBox = document.querySelector('.login-box');
|
||||
if (loginBox) {
|
||||
loginBox.style.opacity = '1';
|
||||
loginBox.style.transform = 'translateY(0)';
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
42
EpinelPS/wwwroot/admin/assets/js/site.js
Normal file
42
EpinelPS/wwwroot/admin/assets/js/site.js
Normal file
@@ -0,0 +1,42 @@
|
||||
// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
// for details on configuring this project to bundle and minify static web assets.
|
||||
|
||||
// NIKKE管理控制台API通用工具函数
|
||||
|
||||
function runCmd(cmdName, cb, p1, p2)
|
||||
{
|
||||
fetch("/adminapi/RunCmd", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
cmdName: cmdName,
|
||||
p1: p1,
|
||||
p2: p2
|
||||
}),
|
||||
headers: {
|
||||
"Content-type": "application/json; charset=UTF-8",
|
||||
"Authorization": `Bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((json) => cb(json)).catch((error) => {
|
||||
console.error("命令执行失败:", error);
|
||||
alert("操作失败: " + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function runSimpleCmd(cmdName, p1, p2)
|
||||
{
|
||||
runCmd(cmdName, function(json){
|
||||
if (json.ok)
|
||||
alert("操作已完成");
|
||||
else
|
||||
alert("错误: " + json.error);
|
||||
}, p1, p2);
|
||||
}
|
||||
|
||||
function runSimpleCmdWithPr(cmdName, p1, p2Title)
|
||||
{
|
||||
let p2 = prompt(p2Title);
|
||||
if (p2 === undefined || p2 == null || p2 == "") return;
|
||||
runSimpleCmd(cmdName, p1, p2);
|
||||
}
|
||||
@@ -1,35 +1,168 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Security System Controller</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<title data-i18n="app.name">NIKKE: 胜利女神 - 管理控制台</title>
|
||||
|
||||
<!-- 字体 -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Exo+2:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;700&family=Noto+Sans+JP:wght@400;500;700&family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- 图标 -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
|
||||
<!-- 基础框架 -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
|
||||
|
||||
<link rel="stylesheet" href="/admin/assets/login.css">
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
|
||||
<!-- 自定义样式 -->
|
||||
<link rel="stylesheet" href="/admin/assets/css/nikke-theme.css">
|
||||
<link rel="stylesheet" href="/admin/assets/css/nikke-login.css">
|
||||
<link rel="stylesheet" href="/admin/assets/css/nikke-i18n.css">
|
||||
|
||||
<!-- 网站图标 -->
|
||||
<link rel="icon" href="/admin/assets/img/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<div class="LoginBox">
|
||||
<h1>Login</h1>
|
||||
<form onsubmit="return false;">
|
||||
<div class="mb-3">
|
||||
<label for="UsernameBox" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="UsernameBox" name="username">
|
||||
<body class="login-page">
|
||||
<div class="login-overlay"></div>
|
||||
<div class="nikke-glow"></div>
|
||||
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<div class="login-header">
|
||||
<img src="/admin/assets/img/nikke-logo.png" alt="NIKKE" class="logo">
|
||||
<h1 data-i18n="app.name">胜利女神控制台</h1>
|
||||
<p data-i18n="auth.welcome">请登录以访问管理功能</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="PasswordBox" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="PasswordBox" name="password">
|
||||
<div class="login-body">
|
||||
<form id="loginForm" onsubmit="return false;">
|
||||
<div class="form-floating">
|
||||
<input type="text" class="form-control" id="UsernameBox" name="username" placeholder=" " autocomplete="username">
|
||||
<label for="UsernameBox"><i class="fas fa-user me-2"></i><span data-i18n="auth.username">用户名</span></label>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input type="password" class="form-control" id="PasswordBox" name="password" placeholder=" " autocomplete="current-password">
|
||||
<label for="PasswordBox"><i class="fas fa-lock me-2"></i><span data-i18n="auth.password">密码</span></label>
|
||||
</div>
|
||||
<div id="errormsg" class="error-message"></div>
|
||||
<button type="button" class="login-button" onclick="AdminLogin()">
|
||||
<i class="fas fa-sign-in-alt me-2"></i><span data-i18n="auth.enter">进入控制台</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<p id="errormsg" style="color: red;"></p>
|
||||
<button class="btn btn-primary" onclick="AdminLogin()">Submit</button>
|
||||
</form>
|
||||
<div class="login-footer" data-i18n="app.name">
|
||||
NIKKE: 胜利女神 - 管理员控制台
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="version" data-i18n="app.version">v2.5.3</div>
|
||||
|
||||
<!-- 脚本 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||
<script src="/admin/assets/js/loginpage.js"></script>
|
||||
|
||||
<!-- 国际化支持 -->
|
||||
<script src="/admin/assets/js/nikke-i18n.js"></script>
|
||||
<script src="/admin/assets/js/nikke-login.js"></script>
|
||||
|
||||
<script>
|
||||
// 鼠标跟随光效
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
const glow = document.querySelector('.nikke-glow');
|
||||
glow.style.left = (e.clientX - 100) + 'px';
|
||||
glow.style.top = (e.clientY - 100) + 'px';
|
||||
});
|
||||
|
||||
// 增强错误消息显示
|
||||
function showErrorMessage(message) {
|
||||
const errorMsg = document.getElementById('errormsg');
|
||||
errorMsg.textContent = message;
|
||||
errorMsg.classList.add('visible');
|
||||
|
||||
// 添加抖动动画效果
|
||||
errorMsg.style.animation = 'none';
|
||||
setTimeout(() => {
|
||||
errorMsg.style.animation = 'shake 0.5s cubic-bezier(.36,.07,.19,.97) both';
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// 覆盖原有的登录函数
|
||||
async function AdminLogin() {
|
||||
const username = document.getElementById("UsernameBox").value;
|
||||
const password = document.getElementById("PasswordBox").value;
|
||||
|
||||
if (!username || !password) {
|
||||
showErrorMessage(i18n.t('auth.error.required'));
|
||||
return;
|
||||
}
|
||||
|
||||
const loginBtn = document.querySelector('.login-button');
|
||||
const originalText = loginBtn.innerHTML;
|
||||
|
||||
// 添加加载状态
|
||||
loginBtn.innerHTML = `<i class="fas fa-circle-notch fa-spin"></i> ${i18n.t('auth.verifying')}`;
|
||||
loginBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch("/adminapi/login", {
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.ok) {
|
||||
localStorage.setItem("token", data.token);
|
||||
|
||||
// 成功效果
|
||||
loginBtn.innerHTML = `<i class="fas fa-check"></i> ${i18n.t('auth.success')}`;
|
||||
loginBtn.style.background = 'linear-gradient(to right, #32d296, #38ef7d)';
|
||||
|
||||
// 添加成功动画,然后跳转
|
||||
setTimeout(() => {
|
||||
window.location.pathname = "/admin/dashboard";
|
||||
}, 800);
|
||||
} else {
|
||||
// 恢复按钮状态
|
||||
loginBtn.innerHTML = originalText;
|
||||
loginBtn.disabled = false;
|
||||
|
||||
// 显示错误
|
||||
const errorMessage = data.message || data.title || i18n.t('auth.error.invalid');
|
||||
showErrorMessage(errorMessage);
|
||||
}
|
||||
} catch (error) {
|
||||
// 恢复按钮状态
|
||||
loginBtn.innerHTML = originalText;
|
||||
loginBtn.disabled = false;
|
||||
|
||||
// 显示错误
|
||||
showErrorMessage(i18n.t('auth.error.network'));
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 语言变更时刷新页面内容
|
||||
window.addEventListener('nikke:languageChanged', function() {
|
||||
// 更新标题
|
||||
document.title = i18n.t('app.name');
|
||||
|
||||
// 更新错误消息
|
||||
const errorMsg = document.getElementById('errormsg');
|
||||
if (errorMsg.textContent) {
|
||||
errorMsg.textContent = i18n.t('auth.error.invalid');
|
||||
}
|
||||
|
||||
// 更新登录按钮
|
||||
const loginBtn = document.querySelector('.login-button');
|
||||
if (!loginBtn.disabled) {
|
||||
loginBtn.innerHTML = `<i class="fas fa-sign-in-alt me-2"></i><span>${i18n.t('auth.enter')}</span>`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user