feat: implement Redux store with slices for user, order, file, and printer management

- Added Redux store configuration in `src/store/index.ts`.
- Created slices for user, order, file, and printer with respective reducers and actions.
- Implemented custom hooks `useAppDispatch` and `useAppSelector` for typed Redux usage.
- Defined types for file, order, printer, and user in `src/types`.
- Added utility functions for formatting prices, file sizes, and dates in `src/utils`.
- Created common styles and animations in `src/styles` for consistent UI design.
- Integrated API request handling with token management in `src/utils/request.ts`.
- Implemented local storage management for user token and info in `src/utils/storage.ts`.
This commit is contained in:
Mikuisnotavailable
2026-03-30 23:19:43 +08:00
parent 96cfc609b2
commit a424d06731
69 changed files with 5555 additions and 106 deletions

367
src/pages/login/index.css Normal file
View File

@@ -0,0 +1,367 @@
/* ============================================
登录页面样式 - 爱印通
============================================ */
@import '../../styles/variables.css';
page {
background: linear-gradient(180deg, #e8f0fe 0%, #f0f5ff 30%, #fafbfc 100%);
min-height: 100vh;
}
.login-page {
min-height: 100vh;
padding: 100px 32px 60px;
display: flex;
flex-direction: column;
}
/* 头部区域 */
.login-header {
text-align: center;
margin-bottom: 48px;
animation: fadeInDown 0.6s ease forwards;
}
.login-logo {
width: 80px;
height: 80px;
background: var(--primary-gradient);
border-radius: 24px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20px;
box-shadow: 0 8px 24px rgba(90, 147, 223, 0.3);
font-size: 40px;
}
.login-title {
font-size: 40px;
font-weight: 700;
color: var(--text-primary);
letter-spacing: 2px;
display: block;
margin-bottom: 8px;
}
.login-subtitle {
font-size: 24px;
color: var(--text-tertiary);
display: block;
}
/* 表单卡片 */
.login-form {
background: rgba(255, 255, 255, 0.95);
border-radius: var(--radius-xl);
padding: 36px 28px;
box-shadow: var(--shadow-lg);
border: 1px solid rgba(255, 255, 255, 0.8);
backdrop-filter: blur(20px);
animation: slideUp 0.6s ease 0.2s forwards;
opacity: 0;
}
.form-item {
margin-bottom: 24px;
}
.form-label {
font-size: var(--font-size-base);
color: var(--text-secondary);
font-weight: var(--font-weight-medium);
display: block;
margin-bottom: 12px;
padding-left: 4px;
}
.form-input {
width: 100%;
height: 88px;
background: #f8f9fb;
border-radius: var(--radius-md);
padding: 0 24px;
font-size: var(--font-size-base);
color: var(--text-primary);
border: 1px solid var(--border-primary);
transition: all var(--transition-base);
}
.form-input:focus {
border-color: var(--primary-color);
background: #fff;
box-shadow: 0 0 0 4px rgba(90, 147, 223, 0.1);
}
.form-input::placeholder {
color: var(--text-placeholder);
}
.form-error {
font-size: var(--font-size-xs);
color: var(--error-color);
margin-top: 8px;
display: block;
padding-left: 4px;
}
/* 验证码输入区域 */
.code-input-wrapper {
display: flex;
gap: 16px;
}
.code-input {
flex: 1;
}
.code-btn {
width: 140px;
height: 88px;
background: var(--primary-gradient);
color: #fff;
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all var(--transition-base);
white-space: nowrap;
}
.code-btn::after {
border: none;
}
.code-btn:active {
transform: scale(0.96);
}
.code-btn[disabled],
.code-btn.disabled {
background: linear-gradient(135deg, #c5cede 0%, #b0bccf 100%);
color: rgba(255, 255, 255, 0.7);
}
/* 登录按钮 */
.login-btn {
width: 100%;
height: 96px;
background: var(--primary-gradient);
color: #fff;
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
border-radius: 48px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 32px;
box-shadow: 0 8px 24px rgba(90, 147, 223, 0.3);
transition: all var(--transition-base);
letter-spacing: 2px;
}
.login-btn::after {
border: none;
}
.login-btn:active {
transform: scale(0.97);
box-shadow: 0 4px 12px rgba(90, 147, 223, 0.2);
}
.login-btn[disabled],
.login-btn.disabled {
background: linear-gradient(135deg, #c5cede 0%, #b0bccf 100%);
box-shadow: none;
}
/* 分割线 */
.divider {
display: flex;
align-items: center;
margin: 36px 0;
}
.divider-line {
flex: 1;
height: 1px;
background: var(--border-primary);
}
.divider-text {
padding: 0 20px;
font-size: var(--font-size-xs);
color: var(--text-tertiary);
}
/* 微信登录按钮 */
.wechat-btn {
width: 100%;
height: 96px;
background: linear-gradient(135deg, #07c160 0%, #06ad56 100%);
color: #fff;
font-size: var(--font-size-lg);
font-weight: var(--font-weight-medium);
border-radius: 48px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8px 24px rgba(7, 193, 96, 0.3);
transition: all var(--transition-base);
letter-spacing: 2px;
}
.wechat-btn::after {
border: none;
}
.wechat-btn:active {
transform: scale(0.97);
}
/* 底部链接 */
.login-footer {
display: flex;
justify-content: center;
align-items: center;
margin-top: 40px;
flex-wrap: wrap;
gap: 4px;
}
.footer-text {
font-size: var(--font-size-xs);
color: var(--text-tertiary);
}
.link-text {
font-size: var(--font-size-xs);
color: var(--primary-color);
font-weight: var(--font-weight-medium);
}
/* 昵称弹窗 */
.nickname-modal {
background: rgba(255, 255, 255, 0.98);
border-radius: var(--radius-xl);
padding: 48px 32px;
margin-top: 100px;
box-shadow: var(--shadow-xl);
animation: modalIn 0.3s ease forwards;
}
@keyframes modalIn {
from {
opacity: 0;
transform: scale(0.9) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.modal-title {
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
color: var(--text-primary);
display: block;
text-align: center;
margin-bottom: 12px;
}
.modal-subtitle {
font-size: var(--font-size-base);
color: var(--text-tertiary);
display: block;
text-align: center;
margin-bottom: 32px;
}
.nickname-input {
width: 100%;
height: 88px;
background: #f8f9fb;
border-radius: var(--radius-md);
padding: 0 24px;
font-size: var(--font-size-base);
color: var(--text-primary);
border: 1px solid var(--border-primary);
margin-bottom: 24px;
}
.nickname-input:focus {
border-color: var(--primary-color);
background: #fff;
box-shadow: 0 0 0 4px rgba(90, 147, 223, 0.1);
}
.submit-nickname-btn {
width: 100%;
height: 96px;
background: var(--primary-gradient);
color: #fff;
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
border-radius: 48px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8px 24px rgba(90, 147, 223, 0.3);
}
.submit-nickname-btn::after {
border: none;
}
/* 动画 */
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 装饰元素 */
.login-page::before {
content: '';
position: fixed;
top: -100px;
right: -100px;
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(125, 179, 245, 0.15) 0%, transparent 70%);
border-radius: 50%;
pointer-events: none;
}
.login-page::after {
content: '';
position: fixed;
bottom: -50px;
left: -50px;
width: 250px;
height: 250px;
background: radial-gradient(circle, rgba(125, 179, 245, 0.1) 0%, transparent 70%);
border-radius: 50%;
pointer-events: none;
}