From 1ae64fa4835f050bc979edcc71435fc838b8bb0f Mon Sep 17 00:00:00 2001 From: hnu202326010131 <2950457847@qq.com> Date: Sat, 6 Dec 2025 09:59:28 +0000 Subject: [PATCH] =?UTF-8?q?feat(login):=20=E5=89=8D=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E8=81=94=E8=B0=83\n\n-=20Vite=20=E4=BB=A3=E7=90=86=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=20VITE=5FAPI=5FTA?= =?UTF-8?q?RGET\n-=20=E4=BF=9D=E7=95=99=20/api=20=E5=89=8D=E7=BC=80?= =?UTF-8?q?=E8=BD=AC=E5=8F=91=E5=90=8E=E7=AB=AF\n-=20Pinia=20=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=94=B9=E4=B8=BA=E8=B0=83=E7=94=A8=20/api/v1/user/lo?= =?UTF-8?q?gin=EF=BC=8C=E5=B9=B6=E5=9C=A8=E5=90=8E=E7=AB=AF=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E8=BE=BE=E6=97=B6=E5=9B=9E=E9=80=80=E6=BC=94=E7=A4=BA?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7\n-=20=E7=99=BB=E5=BD=95=E9=A1=B5=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=94=B9=E4=B8=BA=E5=BC=82=E6=AD=A5=E5=B9=B6=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=20loading\n-=20=E5=90=8E=E7=AB=AF=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E6=94=AF=E6=8C=81=E6=BC=94=E7=A4=BA=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=B9=B6=E4=BF=9D=E7=95=99=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C\n-=20=E6=B7=BB=E5=8A=A0=20.env.example=20?= =?UTF-8?q?=E4=B8=8E=20.env.development?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/routers/auth.py | 35 +++++++++++++++----------- frontend-vue/.env.development | 1 + frontend-vue/.env.example | 1 + frontend-vue/package-lock.json | 3 --- frontend-vue/src/app/stores/auth.ts | 36 ++++++++++++++++++--------- frontend-vue/src/app/views/Login.vue | 8 +++--- frontend-vue/vite.config.ts | 37 +++++++++++++++------------- 7 files changed, 72 insertions(+), 49 deletions(-) create mode 100644 frontend-vue/.env.development create mode 100644 frontend-vue/.env.example diff --git a/backend/app/routers/auth.py b/backend/app/routers/auth.py index eafa83b..5587e58 100644 --- a/backend/app/routers/auth.py +++ b/backend/app/routers/auth.py @@ -14,17 +14,24 @@ class LoginRequest(BaseModel): @router.post("/user/login") async def login(req: LoginRequest, db: AsyncSession = Depends(get_db)): - """处理登录请求,验证用户名与密码。""" - result = await db.execute(select(User).where(User.username == req.username).limit(1)) - user = result.scalars().first() - if not user: - raise HTTPException(status_code=401, detail="invalid_credentials") - if not user.is_active: - raise HTTPException(status_code=403, detail="inactive_user") - if not bcrypt.verify(req.password, user.password_hash): - raise HTTPException(status_code=401, detail="invalid_credentials") - await db.execute( - update(User).where(User.id == user.id).values(last_login=func.now(), updated_at=func.now()) - ) - await db.commit() - return {"ok": True, "username": user.username, "fullName": user.full_name} + demo = {"admin": "admin123", "ops": "ops123", "obs": "obs123"} + if req.username in demo and req.password == demo[req.username]: + return {"ok": True, "username": req.username, "fullName": req.username} + try: + result = await db.execute(select(User).where(User.username == req.username).limit(1)) + user = result.scalars().first() + if not user: + raise HTTPException(status_code=401, detail="invalid_credentials") + if not user.is_active: + raise HTTPException(status_code=403, detail="inactive_user") + if not bcrypt.verify(req.password, user.password_hash): + raise HTTPException(status_code=401, detail="invalid_credentials") + await db.execute( + update(User).where(User.id == user.id).values(last_login=func.now(), updated_at=func.now()) + ) + await db.commit() + return {"ok": True, "username": user.username, "fullName": user.full_name} + except HTTPException: + raise + except Exception: + raise HTTPException(status_code=500, detail="server_error") diff --git a/frontend-vue/.env.development b/frontend-vue/.env.development new file mode 100644 index 0000000..6252b23 --- /dev/null +++ b/frontend-vue/.env.development @@ -0,0 +1 @@ +VITE_API_TARGET=http://localhost:8000 diff --git a/frontend-vue/.env.example b/frontend-vue/.env.example new file mode 100644 index 0000000..6252b23 --- /dev/null +++ b/frontend-vue/.env.example @@ -0,0 +1 @@ +VITE_API_TARGET=http://localhost:8000 diff --git a/frontend-vue/package-lock.json b/frontend-vue/package-lock.json index 619b835..6e94841 100644 --- a/frontend-vue/package-lock.json +++ b/frontend-vue/package-lock.json @@ -1411,7 +1411,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1426,7 +1425,6 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -1486,7 +1484,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz", "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.25", "@vue/compiler-sfc": "3.5.25", diff --git a/frontend-vue/src/app/stores/auth.ts b/frontend-vue/src/app/stores/auth.ts index 6508055..35b5019 100644 --- a/frontend-vue/src/app/stores/auth.ts +++ b/frontend-vue/src/app/stores/auth.ts @@ -1,4 +1,6 @@ import { defineStore } from 'pinia' +import axios from 'axios' +const api = axios.create({ baseURL: '/api' }) type User = { username: string; role: 'admin'|'operator'|'observer' } @@ -24,19 +26,29 @@ export const useAuthStore = defineStore('auth', { if (this.user) localStorage.setItem('cm_user', JSON.stringify(this.user)) else localStorage.removeItem('cm_user') }, - login(username: string, password: string) { - const demo = { - admin: { u: 'admin', p: 'admin123', role: 'admin' }, - ops: { u: 'ops', p: 'ops123', role: 'operator' }, - obs: { u: 'obs', p: 'obs123', role: 'observer' } - } as const - const m = Object.values(demo).find(d => d.u === username && d.p === password) - if (!m) return { ok: false, message: '账号或密码错误' } - this.user = { username, role: m.role } - this.persist() - return { ok: true, role: m.role } + async login(username: string, password: string) { + try { + const r = await api.post('/v1/user/login', { username, password }) + const role = username === 'admin' ? 'admin' : username === 'ops' ? 'operator' : username === 'obs' ? 'observer' : 'observer' + this.user = { username, role } + this.persist() + return { ok: true, role } + } catch (e: any) { + if (!e?.response) { + const demo = { admin: 'admin123', ops: 'ops123', obs: 'obs123' } as const + const pass = demo[username as keyof typeof demo] + if (pass && password === pass) { + const role = username === 'admin' ? 'admin' : username === 'ops' ? 'operator' : 'observer' + this.user = { username, role } + this.persist() + return { ok: true, role } + } + } + const d = e?.response?.data + const message = d?.detail === 'invalid_credentials' ? '账号或密码错误' : d?.detail === 'inactive_user' ? '账号未激活' : '登录失败' + return { ok: false, message } + } }, logout() { this.user = null; this.persist() } } }) - diff --git a/frontend-vue/src/app/views/Login.vue b/frontend-vue/src/app/views/Login.vue index b625a24..7f55f03 100644 --- a/frontend-vue/src/app/views/Login.vue +++ b/frontend-vue/src/app/views/Login.vue @@ -21,12 +21,14 @@ import { useAuthStore } from '../stores/auth' const username = ref('') const password = ref('') const msg = ref('') +const loading = ref(false) const router = useRouter() const auth = useAuthStore() -function onSubmit() { - const r = auth.login(username.value, password.value) +async function onSubmit() { + loading.value = true + const r = await auth.login(username.value, password.value) + loading.value = false if (r.ok) router.replace({ name: auth.defaultPage }) else msg.value = r.message || '登录失败' } - diff --git a/frontend-vue/vite.config.ts b/frontend-vue/vite.config.ts index f2fc608..18358ef 100644 --- a/frontend-vue/vite.config.ts +++ b/frontend-vue/vite.config.ts @@ -1,18 +1,21 @@ -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' +import { defineConfig, loadEnv } from "vite"; +import vue from "@vitejs/plugin-vue"; -export default defineConfig({ - plugins: [vue()], - server: { - host: '0.0.0.0', - strictPort: true, - port: 5173, - proxy: { - '/api': { - target: 'http://localhost:8000', - changeOrigin: true, - rewrite: p => p.replace(/^\/api/, '') - } - } - } -}) +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + const target = env.VITE_API_TARGET || "http://localhost:8000"; + return { + plugins: [vue()], + server: { + host: "0.0.0.0", + strictPort: true, + port: 5173, + proxy: { + "/api": { + target, + changeOrigin: true, + }, + }, + }, + }; +});