Features - User Management (Phase 4.1): - Database: Add user_modules table for fine-grained module permissions - Database: Add 4 user permissions (view/create/edit/delete) to role_permissions - Backend: UserService (780 lines) - CRUD with tenant isolation - Backend: UserController + UserRoutes (648 lines) - 13 API endpoints - Backend: Batch import users from Excel - Frontend: UserListPage (412 lines) - list/filter/search/pagination - Frontend: UserFormPage (341 lines) - create/edit with module config - Frontend: UserDetailPage (393 lines) - details/tenant/module management - Frontend: 3 modal components (592 lines) - import/assign/configure - API: GET/POST/PUT/DELETE /api/admin/users/* endpoints Architecture Upgrade - Module Permission System: - Backend: Add getUserModules() method in auth.service - Backend: Login API returns modules array in user object - Frontend: AuthContext adds hasModule() method - Frontend: Navigation filters modules based on user.modules - Frontend: RouteGuard checks requiredModule instead of requiredVersion - Frontend: Remove deprecated version-based permission system - UX: Only show accessible modules in navigation (clean UI) - UX: Smart redirect after login (avoid 403 for regular users) Fixes: - Fix UTF-8 encoding corruption in ~100 docs files - Fix pageSize type conversion in userService (String to Number) - Fix authUser undefined error in TopNavigation - Fix login redirect logic with role-based access check - Update Git commit guidelines v1.2 with UTF-8 safety rules Database Changes: - CREATE TABLE user_modules (user_id, tenant_id, module_code, is_enabled) - ADD UNIQUE CONSTRAINT (user_id, tenant_id, module_code) - INSERT 4 permissions + role assignments - UPDATE PUBLIC tenant with 8 module subscriptions Technical: - Backend: 5 new files (~2400 lines) - Frontend: 10 new files (~2500 lines) - Docs: 1 development record + 2 status updates + 1 guideline update - Total: ~4900 lines of code Status: User management 100% complete, module permission system operational
216 lines
3.7 KiB
CSS
216 lines
3.7 KiB
CSS
/**
|
||
* ConversationList 会话列表样式
|
||
*
|
||
* 现代感设计(参考 Ant Design X Ultramodern):
|
||
* - 简洁清爽
|
||
* - 柔和阴影
|
||
* - 流畅过渡
|
||
*/
|
||
|
||
.conversation-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
width: 280px;
|
||
background: #ffffff;
|
||
border-right: 1px solid #e5e7eb;
|
||
}
|
||
|
||
/* 头部 */
|
||
.conversation-list-header {
|
||
padding: 20px 16px;
|
||
border-bottom: 1px solid #f3f4f6;
|
||
}
|
||
|
||
.conversation-list-logo {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.conversation-list-logo .default-logo {
|
||
font-size: 24px;
|
||
color: #6366f1;
|
||
}
|
||
|
||
.conversation-list-title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
/* 新建会话按钮 */
|
||
.conversation-list-new {
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
.new-conversation-btn {
|
||
height: 44px;
|
||
border-radius: 12px;
|
||
border: 1px dashed #d1d5db;
|
||
background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
|
||
color: #6366f1;
|
||
font-weight: 500;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.new-conversation-btn:hover {
|
||
border-color: #6366f1;
|
||
background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 100%);
|
||
color: #4f46e5;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.15);
|
||
}
|
||
|
||
/* 会话列表内容 */
|
||
.conversation-list-content {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 8px 12px;
|
||
}
|
||
|
||
/* 分组标签 */
|
||
.conversation-group {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.conversation-group-label {
|
||
padding: 8px 4px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
/* 会话项 */
|
||
.conversation-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
padding: 12px;
|
||
border-radius: 12px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
margin-bottom: 4px;
|
||
position: relative;
|
||
}
|
||
|
||
.conversation-item:hover {
|
||
background: #f9fafb;
|
||
}
|
||
|
||
.conversation-item.active {
|
||
background: linear-gradient(135deg, #eef2ff 0%, #e0e7ff 100%);
|
||
}
|
||
|
||
.conversation-item.active::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 3px;
|
||
height: 24px;
|
||
background: #6366f1;
|
||
border-radius: 0 3px 3px 0;
|
||
}
|
||
|
||
/* 会话图标 */
|
||
.conversation-item-icon {
|
||
flex-shrink: 0;
|
||
width: 36px;
|
||
height: 36px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 10px;
|
||
background: #f3f4f6;
|
||
color: #6b7280;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.conversation-item.active .conversation-item-icon {
|
||
background: #6366f1;
|
||
color: white;
|
||
}
|
||
|
||
.agent-icon {
|
||
font-size: 20px;
|
||
}
|
||
|
||
/* 会话内容 */
|
||
.conversation-item-content {
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.conversation-item-title {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #111827;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.conversation-item-preview {
|
||
font-size: 12px;
|
||
color: #6b7280;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
margin-top: 4px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* 更多按钮 */
|
||
.conversation-item-more {
|
||
opacity: 0;
|
||
flex-shrink: 0;
|
||
color: #9ca3af;
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
|
||
.conversation-item:hover .conversation-item-more {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* 空状态 */
|
||
.conversation-empty {
|
||
padding: 40px 20px;
|
||
}
|
||
|
||
/* 滚动条 */
|
||
.conversation-list-content::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.conversation-list-content::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.conversation-list-content::-webkit-scrollbar-thumb {
|
||
background: #e5e7eb;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.conversation-list-content::-webkit-scrollbar-thumb:hover {
|
||
background: #d1d5db;
|
||
}
|
||
|
||
/* 响应式 */
|
||
@media (max-width: 768px) {
|
||
.conversation-list {
|
||
width: 100%;
|
||
border-right: none;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
max-height: 40vh;
|
||
}
|
||
}
|
||
|
||
|
||
|