diff --git a/backend/apps/system/api/user.py b/backend/apps/system/api/user.py index dadfa574..6992c883 100644 --- a/backend/apps/system/api/user.py +++ b/backend/apps/system/api/user.py @@ -1,9 +1,27 @@ from fastapi import APIRouter, Depends, Request -from common.core.deps import CurrentUser +from apps.system.models.user import user_grid +from common.core.deps import CurrentUser, SessionDep +from common.core.pagination import Paginator +from common.core.schemas import PaginatedResponse, PaginationParams router = APIRouter(tags=["user"], prefix="/user") @router.get("/info") async def user_info(current_user: CurrentUser): - return current_user.to_dict() \ No newline at end of file + return current_user.to_dict() + + +@router.get("/pager/{pageNum}/{pageSize}", response_model=PaginatedResponse[user_grid]) +async def pager( + session: SessionDep, + pageNum: int, + pageSize: int +): + pagination = PaginationParams(page=pageNum, size=pageSize) + paginator = Paginator(session) + filters = {} + return await paginator.get_paginated_response( + model=user_grid, + pagination=pagination, + **filters) \ No newline at end of file diff --git a/backend/apps/system/crud/user.py b/backend/apps/system/crud/user.py index 31388b45..a522fe24 100644 --- a/backend/apps/system/crud/user.py +++ b/backend/apps/system/crud/user.py @@ -1,12 +1,14 @@ from sqlmodel import Session, select -from ..models.user import sys_user +from ..models.user import sys_user, user_grid from common.core.security import verify_md5pwd def get_user_by_account(*, session: Session, account: str) -> sys_user | None: - statement = select(sys_user).where(sys_user.account == account) + #statement = select(sys_user).where(sys_user.account == account) + statement = select(user_grid.id, user_grid.account, user_grid.oid, user_grid.password).where(user_grid.account == account) session_user = session.exec(statement).first() - return session_user + result_user = sys_user.model_validate(session_user) + return result_user def authenticate(*, session: Session, account: str, password: str) -> sys_user | None: db_user = get_user_by_account(session=session, account=account) diff --git a/backend/apps/system/models/user.py b/backend/apps/system/models/user.py index b3063725..7fc12dd7 100644 --- a/backend/apps/system/models/user.py +++ b/backend/apps/system/models/user.py @@ -1,7 +1,8 @@ +from typing import Optional from sqlmodel import SQLModel, Field -class sys_user(SQLModel, table=True): +class sys_user(SQLModel): id: int = Field(primary_key=True, index=True) account: str = Field(max_length=255, unique=True) password: str = Field(max_length=255) @@ -12,4 +13,12 @@ class sys_user(SQLModel, table=True): "id": self.id, "account": self.account, "oid": self.oid - } \ No newline at end of file + } + +class user_grid(sys_user, table=True): + __tablename__ = "sys_user" + name: str + email: str + status: int + create_time: int + diff --git a/backend/common/core/deps.py b/backend/common/core/deps.py index 61cfd14e..4b4b732f 100644 --- a/backend/common/core/deps.py +++ b/backend/common/core/deps.py @@ -5,12 +5,12 @@ from fastapi import Depends, HTTPException, status # from fastapi.security import OAuth2PasswordBearer from jwt.exceptions import InvalidTokenError from pydantic import BaseModel, ValidationError -from sqlmodel import Session +from sqlmodel import Session, select from common.core.schemas import TokenPayload, XOAuth2PasswordBearer from common.core import security from common.core.config import settings from common.core.db import get_session -from apps.system.models.user import sys_user +from apps.system.models.user import sys_user, user_grid reusable_oauth2 = XOAuth2PasswordBearer( tokenUrl=f"{settings.API_V1_STR}/login/access-token" ) @@ -32,7 +32,11 @@ async def get_current_user(session: SessionDep, token: TokenDep) -> sys_user: status_code=status.HTTP_403_FORBIDDEN, detail="Could not validate credentials", ) - user = session.get(sys_user, token_data.id) + statement = select(user_grid.id, user_grid.account, user_grid.oid, user_grid.password).where(user_grid.id == token_data.id) + session_user = session.exec(statement).first() + if not session_user: + raise HTTPException(status_code=404, detail="User not found") + user = sys_user.model_validate(session_user) if not user: raise HTTPException(status_code=404, detail="User not found") """ if not user.is_active: diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts new file mode 100644 index 00000000..d3ebe6e8 --- /dev/null +++ b/frontend/src/api/auth.ts @@ -0,0 +1,9 @@ +import { request } from '@/utils/request' + +export const userApi = { + pager: (pageNumber: number, pageSize: number) => request.get(`/user/pager/${pageNumber}/${pageSize}`), + add: (data: any) => request.post('/settings/terminology', data), + edit: (data: any) => request.put('/settings/terminology', data), + delete: (id: number) => request.delete(`/settings/terminology/${id}`), + query: (id: number) => request.get(`/settings/terminology/${id}`) +} \ No newline at end of file diff --git a/frontend/src/components/layout/index.vue b/frontend/src/components/layout/index.vue index 6ab86bb7..83b3336c 100644 --- a/frontend/src/components/layout/index.vue +++ b/frontend/src/components/layout/index.vue @@ -45,7 +45,7 @@

{{ currentPageTitle }}

-
+
System manage
@@ -77,8 +77,27 @@
+
+ + + + + + {{ item.meta.title }} + + +
-
+
+
+ +
+
+
@@ -105,19 +124,31 @@ const route = useRoute() const userStore = useUserStore() const name = ref('admin') const activeMenu = computed(() => route.path) - const routerList = computed(() => { return router.getRoutes().filter(route => { - return route.path !== '/login' && !route.redirect && route.path !== '/:pathMatch(.*)*' + return route.path !== '/login' && !route.path.includes('/system') && !route.redirect && route.path !== '/:pathMatch(.*)*' }) }) + +const sysRouterList = computed(() => { + return router.getRoutes().filter(route => route.path.includes('/system')) +}) + +const showHead = computed(() => { + return route.path.includes('/system') +}) const workspace = ref('1') const options = [ { value: '1', label: 'Default workspace' }, { value: '2', label: 'Workspace 2' }, { value: '3', label: 'Workspace 3' } ] -const currentPageTitle = computed(() => route.meta.title || 'Dashboard') +const currentPageTitle = computed(() => { + if (route.path.includes('/system')) { + return 'System Settings' + } + return route.meta.title || 'Dashboard' +}) const resolveIcon = (iconName: any) => { const icons: Record = { 'ds': ds, @@ -129,13 +160,15 @@ const resolveIcon = (iconName: any) => { } const menuSelect = (e: any) => { - console.log(routerList.value) router.push(e.index) } const logout = () => { userStore.logout() router.push('/login') } +const toSystem = () => { + router.push('/system') +} \ No newline at end of file diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 26e7558e..33dca35c 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -66,6 +66,25 @@ const router = createRouter({ meta: { title: 'Setting', icon: 'setting' } } ] + }, + { + path: '/system', + component: Layout, + redirect: '/system/user', + children: [ + { + path: 'user', + name: 'user', + component: () => import('@/views/system/user/index.vue'), + meta: { title: 'User Management', icon: 'setting' } + }, + { + path: 'model', + name: 'model', + component: () => import('@/views/system/model/index.vue'), + meta: { title: 'AI Model Configuration', icon: 'setting' } + } + ] } ] }) diff --git a/frontend/src/utils/date.ts b/frontend/src/utils/date.ts new file mode 100644 index 00000000..4243bdca --- /dev/null +++ b/frontend/src/utils/date.ts @@ -0,0 +1,32 @@ +export function formatTimestamp(timestamp: number, format: string = 'YYYY-MM-DD'): string { + if (!timestamp) { + return '-' + } + + const date = new Date(timestamp) + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + + return format.replace(/YYYY|MM|DD|HH|mm|ss/g, (match) => { + switch (match) { + case 'YYYY': + return String(year) + case 'MM': + return month + case 'DD': + return day + case 'HH': + return hours + case 'mm': + return minutes + case 'ss': + return seconds + default: + return match + } + }) +} \ No newline at end of file diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index 8b069339..5cc881f7 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -71,7 +71,7 @@ class HttpService { } // Request logging - console.log(`[Request] ${config.method?.toUpperCase()} ${config.url}`) + // console.log(`[Request] ${config.method?.toUpperCase()} ${config.url}`) return config }, diff --git a/frontend/src/views/login/index.vue b/frontend/src/views/login/index.vue index b67eec3a..ea08f1a7 100644 --- a/frontend/src/views/login/index.vue +++ b/frontend/src/views/login/index.vue @@ -11,7 +11,7 @@