优化小程序登录流程,增加用户协议和隐私政策的勾选机制,确保用户主动同意后方可登录,符合审核要求。同时,增强错误处理逻辑,提升用户体验和系统稳定性。新增用户协议和隐私政策页面,更新相关样式以改善界面交互。

This commit is contained in:
乘风
2026-02-10 15:03:31 +08:00
parent 3ca7d38318
commit 90a77da2da
35 changed files with 697 additions and 108 deletions

View File

@@ -3,4 +3,5 @@
# VITE_API_BASE_URL=http://localhost:3006
VITE_API_BASE_URL=http://localhost:8080
# VITE_API_BASE_URL=https://soulapi.quwanzhi.com
# VITE_API_BASE_URL=https://soul.quwanzhi.com

View File

@@ -0,0 +1,28 @@
/**
* 管理端 JWT 本地存储localStorage与 soul-api JWT 鉴权配合
*/
const ADMIN_TOKEN_KEY = 'admin_token'
export function getAdminToken(): string | null {
try {
return localStorage.getItem(ADMIN_TOKEN_KEY)
} catch {
return null
}
}
export function setAdminToken(token: string): void {
try {
localStorage.setItem(ADMIN_TOKEN_KEY, token)
} catch {
// ignore
}
}
export function clearAdminToken(): void {
try {
localStorage.removeItem(ADMIN_TOKEN_KEY)
} catch {
// ignore
}
}

View File

@@ -1,9 +1,11 @@
/**
* 统一 API 请求封装
* 规则API 路径与现网完全一致,仅通过 baseUrl 区分环境Next 或未来 Gin
* 无缝切换:仅修改 VITE_API_BASE_URL 即可切换后端
* 鉴权:管理端使用 JWT自动带 Authorization: Bearer <token>token 存 localStorage
*/
import { getAdminToken } from './auth'
/** 未设置环境变量时使用的默认 API 地址(零配置部署) */
const DEFAULT_API_BASE = 'https://soulapi.quwanzhi.com'
@@ -24,7 +26,7 @@ export type RequestInitWithBody = RequestInit & { data?: unknown }
/**
* 发起请求。path 为与现网一致的 API 路径(如 /api/admin、/api/orders
* 自动带上 credentials: 'include' 以支持 Cookie 鉴权(与现有 Next 一致)
* 若有 admin_tokenJWT则自动带 Authorization: Bearercredentials: 'include' 保留以兼容需 Cookie 的接口
*/
export async function request<T = unknown>(
path: string,
@@ -33,6 +35,10 @@ export async function request<T = unknown>(
const { data, ...init } = options
const url = apiUrl(path)
const headers = new Headers(init.headers as HeadersInit)
const token = getAdminToken()
if (token) {
headers.set('Authorization', `Bearer ${token}`)
}
if (data !== undefined && data !== null && !headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json')
}

View File

@@ -10,6 +10,7 @@ import {
BookOpen,
} from 'lucide-react'
import { get, post } from '@/api/client'
import { clearAdminToken } from '@/api/auth'
const menuItems = [
{ icon: LayoutDashboard, label: '数据概览', href: '/dashboard' },
@@ -52,7 +53,12 @@ export function AdminLayout() {
}, [mounted, navigate])
const handleLogout = async () => {
await post('/api/admin/logout', {})
clearAdminToken()
try {
await post('/api/admin/logout', {})
} catch {
// 忽略登出接口失败,本地已清 token
}
navigate('/login', { replace: true })
}

View File

@@ -4,6 +4,7 @@ import { Lock, User, ShieldCheck } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { post } from '@/api/client'
import { setAdminToken } from '@/api/auth'
export function LoginPage() {
const navigate = useNavigate()
@@ -16,11 +17,16 @@ export function LoginPage() {
setError('')
setLoading(true)
try {
const data = await post<{ success?: boolean; error?: string }>('/api/admin', {
const data = await post<{
success?: boolean
error?: string
token?: string
}>('/api/admin', {
username: username.trim(),
password,
})
if (data?.success !== false) {
if (data?.success !== false && data?.token) {
setAdminToken(data.token)
navigate('/dashboard', { replace: true })
return
}