diff --git a/index.html b/index.html
index 8700dfc..5657928 100644
--- a/index.html
+++ b/index.html
@@ -7,7 +7,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
-
Crypto AI - 加密货币AI服务平台
+ Crypto.AI - AI Agent for Web3
diff --git a/src/router/index.ts b/src/router/index.ts
index ded0995..50d4b5e 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,5 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
+import { useUserStore } from '../stores/user'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@@ -18,13 +19,54 @@ const router = createRouter({
path: '/ai-agent',
name: 'ai-agent',
component: () => import('../views/AIAgentView.vue'),
+ meta: { requiresVIP: true },
},
{
path: '/ai-feed',
name: 'ai-feed',
component: () => import('../views/AIFeedView.vue'),
},
+ {
+ path: '/login',
+ name: 'login',
+ component: () => import('../views/LoginView.vue'),
+ meta: { requiresGuest: true },
+ },
+ {
+ path: '/register',
+ name: 'register',
+ component: () => import('../views/RegisterView.vue'),
+ meta: { requiresGuest: true },
+ },
],
})
+// 路由守卫
+router.beforeEach((to, from, next) => {
+ const userStore = useUserStore()
+ const isAuthenticated = userStore.isAuthenticated
+ const userInfo = userStore.userInfo
+
+ // 如果页面需要未登录状态(如登录、注册页面)
+ if (to.meta.requiresGuest && isAuthenticated) {
+ next('/')
+ return
+ }
+
+ // 如果页面需要VIP权限
+ if (to.meta.requiresVIP) {
+ if (!isAuthenticated) {
+ // 未登录用户,可以直接访问页面,在页面内会显示登录提示
+ next()
+ return
+ } else if (userInfo && userInfo.level < 1) {
+ // 已登录但不是VIP用户,可以直接访问页面,在页面内会显示升级提示
+ next()
+ return
+ }
+ }
+
+ next()
+})
+
export default router
diff --git a/src/services/api.ts b/src/services/api.ts
new file mode 100644
index 0000000..180a8c2
--- /dev/null
+++ b/src/services/api.ts
@@ -0,0 +1,99 @@
+import { useUserStore } from '../stores/user'
+
+// 根据环境选择API基础URL
+const apiBaseUrl =
+ import.meta.env.MODE === 'development' ? 'http://127.0.0.1:8000' : 'https://api.ibtc.work'
+
+// 用户认证相关API
+export const authApi = {
+ // 发送验证码
+ async sendVerificationCode(mail: string) {
+ const response = await fetch(`${apiBaseUrl}/user/send-verification-code`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ mail }),
+ })
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}))
+ throw new Error(errorData.detail || `请求失败: ${response.status}`)
+ }
+
+ return await response.json()
+ },
+
+ // 用户注册
+ async register(data: {
+ mail: string
+ nickname: string
+ password: string
+ verification_code: string
+ }) {
+ const response = await fetch(`${apiBaseUrl}/user/register`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ })
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}))
+ throw new Error(errorData.detail || `请求失败: ${response.status}`)
+ }
+
+ return await response.json()
+ },
+
+ // 用户登录
+ async login(data: { mail: string; password: string }) {
+ const response = await fetch(`${apiBaseUrl}/user/login`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ })
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}))
+ throw new Error(errorData.detail || `请求失败: ${response.status}`)
+ }
+
+ const authData = await response.json()
+
+ // 使用Pinia存储登录信息
+ const userStore = useUserStore()
+ userStore.setAuth(authData)
+
+ return authData
+ },
+}
+
+// 创建带有认证头的请求函数
+export function createAuthenticatedFetch() {
+ const userStore = useUserStore()
+
+ return async function authenticatedFetch(url: string, options: RequestInit = {}) {
+ const headers = {
+ ...options.headers,
+ Authorization: userStore.authHeader,
+ }
+
+ const response = await fetch(url, {
+ ...options,
+ headers,
+ })
+
+ // 处理401错误(未授权)
+ if (response.status === 401) {
+ userStore.logout()
+ window.location.href = '/login'
+ throw new Error('登录已过期,请重新登录')
+ }
+
+ return response
+ }
+}
diff --git a/src/stores/user.ts b/src/stores/user.ts
new file mode 100644
index 0000000..5eaea4d
--- /dev/null
+++ b/src/stores/user.ts
@@ -0,0 +1,68 @@
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+
+export interface UserInfo {
+ id: number
+ mail: string
+ nickname: string
+ level: number
+ create_time: string
+}
+
+export interface AuthResponse {
+ access_token: string
+ token_type: string
+ expires_in: number
+ user_info: UserInfo
+}
+
+export const useUserStore = defineStore('user', () => {
+ // 状态
+ const accessToken = ref(localStorage.getItem('access_token'))
+ const userInfo = ref(null)
+ const isAuthenticated = computed(() => !!accessToken.value)
+
+ // 从本地存储中恢复用户信息
+ try {
+ const savedUserInfo = localStorage.getItem('user_info')
+ if (savedUserInfo) {
+ userInfo.value = JSON.parse(savedUserInfo)
+ }
+ } catch (error) {
+ console.error('Failed to parse user info from localStorage:', error)
+ }
+
+ // 设置用户信息和令牌
+ function setAuth(authResponse: AuthResponse) {
+ accessToken.value = authResponse.access_token
+ userInfo.value = authResponse.user_info
+
+ // 保存到本地存储
+ localStorage.setItem('access_token', authResponse.access_token)
+ localStorage.setItem('user_info', JSON.stringify(authResponse.user_info))
+ }
+
+ // 登出
+ function logout() {
+ accessToken.value = null
+ userInfo.value = null
+
+ // 清除本地存储
+ localStorage.removeItem('access_token')
+ localStorage.removeItem('user_info')
+ }
+
+ // 获取认证头
+ const authHeader = computed(() => {
+ return accessToken.value ? `Bearer ${accessToken.value}` : ''
+ })
+
+ return {
+ accessToken,
+ userInfo,
+ isAuthenticated,
+ authHeader,
+ setAuth,
+ logout,
+ }
+})
diff --git a/src/views/AIAgentView.vue b/src/views/AIAgentView.vue
index 4f727d3..f87f051 100644
--- a/src/views/AIAgentView.vue
+++ b/src/views/AIAgentView.vue
@@ -1,5 +1,15 @@
@@ -100,70 +118,85 @@ onMounted(() => {
-
-
+
+
+
+
需要登录
+
请登录或注册账号以查看AI分析内容
+
+ 登录
+ 注册
+
+
-
- {{ error }}
-
-
-
-