ForcePilot/web/src/stores/user.js
Wenjie Zhang 01ea590f76 feat(agent): 实现多部门智能体配置管理功能
新增部门级智能体配置管理,支持创建、切换、编辑和删除配置
添加运行时配置中间件,支持动态加载模型、工具和知识库
重构智能体上下文处理,支持部门ID和配置切换
实现前端配置管理界面,支持配置的增删改查和设为默认
添加后端API和数据库模型支持多配置管理
2026-01-24 10:56:31 +08:00

404 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useAgentStore } from './agent'
export const useUserStore = defineStore('user', () => {
// 状态
const token = ref(localStorage.getItem('user_token') || '')
const userId = ref(null)
const username = ref('')
const userIdLogin = ref('')
const phoneNumber = ref('')
const avatar = ref('')
const userRole = ref('')
const departmentId = ref(null)
const departmentName = ref('')
// 计算属性
const isLoggedIn = computed(() => !!token.value)
const isAdmin = computed(() => userRole.value === 'admin' || userRole.value === 'superadmin')
const isSuperAdmin = computed(() => userRole.value === 'superadmin')
// 动作
async function login(credentials) {
try {
const formData = new FormData()
// 支持user_id或phone_number登录
formData.append('username', credentials.loginId) // 使用loginId作为通用登录标识
formData.append('password', credentials.password)
const response = await fetch('/api/auth/token', {
method: 'POST',
body: formData
})
if (!response.ok) {
const error = await response.json()
// 如果是423锁定状态码抛出包含状态码的错误
if (response.status === 423) {
const lockError = new Error(error.detail || '账户被锁定')
lockError.status = 423
lockError.headers = response.headers
throw lockError
}
throw new Error(error.detail || '登录失败')
}
const data = await response.json()
// 更新状态
token.value = data.access_token
userId.value = data.user_id
username.value = data.username
userIdLogin.value = data.user_id_login
phoneNumber.value = data.phone_number || ''
avatar.value = data.avatar || ''
userRole.value = data.role
departmentId.value = data.department_id || null
departmentName.value = data.department_name || ''
// 只保存 token 到本地存储
localStorage.setItem('user_token', data.access_token)
return true
} catch (error) {
console.error('登录错误:', error)
throw error
}
}
function logout() {
// 清除状态
token.value = ''
userId.value = null
username.value = ''
userIdLogin.value = ''
phoneNumber.value = ''
avatar.value = ''
userRole.value = ''
departmentId.value = null
departmentName.value = ''
// 清除 agentStore 状态,确保重新登录时能正确加载数据
const agentStore = useAgentStore()
agentStore.reset()
// 只清除 token
localStorage.removeItem('user_token')
}
async function initialize(admin) {
try {
const response = await fetch('/api/auth/initialize', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(admin)
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.detail || '初始化管理员失败')
}
const data = await response.json()
// 更新状态
token.value = data.access_token
userId.value = data.user_id
username.value = data.username
userIdLogin.value = data.user_id_login
phoneNumber.value = data.phone_number || ''
avatar.value = data.avatar || ''
userRole.value = data.role
departmentId.value = data.department_id || null
departmentName.value = data.department_name || ''
// 只保存 token 到本地存储
localStorage.setItem('user_token', data.access_token)
return true
} catch (error) {
console.error('初始化管理员错误:', error)
throw error
}
}
async function checkFirstRun() {
try {
const response = await fetch('/api/auth/check-first-run')
const data = await response.json()
return data.first_run
} catch (error) {
console.error('检查首次运行状态错误:', error)
return false
}
}
// 用于API请求的授权头
function getAuthHeaders() {
return {
Authorization: `Bearer ${token.value}`
}
}
// 用户管理功能
async function getUsers() {
try {
const response = await fetch('/api/auth/users', {
headers: {
...getAuthHeaders()
}
})
if (!response.ok) {
throw new Error('获取用户列表失败')
}
return await response.json()
} catch (error) {
console.error('获取用户列表错误:', error)
throw error
}
}
async function createUser(userData) {
try {
const response = await fetch('/api/auth/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...getAuthHeaders()
},
body: JSON.stringify(userData)
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.detail || '创建用户失败')
}
return await response.json()
} catch (error) {
console.error('创建用户错误:', error)
throw error
}
}
async function updateUser(userId, userData) {
try {
const response = await fetch(`/api/auth/users/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
...getAuthHeaders()
},
body: JSON.stringify(userData)
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.detail || '更新用户失败')
}
return await response.json()
} catch (error) {
console.error('更新用户错误:', error)
throw error
}
}
async function deleteUser(userId) {
try {
const response = await fetch(`/api/auth/users/${userId}`, {
method: 'DELETE',
headers: {
...getAuthHeaders()
}
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.detail || '删除用户失败')
}
return await response.json()
} catch (error) {
console.error('删除用户错误:', error)
throw error
}
}
// 验证用户名并生成user_id
async function validateUsernameAndGenerateUserId(username) {
try {
const response = await fetch('/api/auth/validate-username', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...getAuthHeaders()
},
body: JSON.stringify({ username })
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.detail || '用户名验证失败')
}
return await response.json()
} catch (error) {
console.error('用户名验证错误:', error)
throw error
}
}
// 上传头像
async function uploadAvatar(file) {
try {
const formData = new FormData()
formData.append('file', file)
const response = await fetch('/api/auth/upload-avatar', {
method: 'POST',
headers: {
...getAuthHeaders()
},
body: formData
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.detail || '头像上传失败')
}
const data = await response.json()
// 更新本地头像状态
avatar.value = data.avatar_url
return data
} catch (error) {
console.error('头像上传错误:', error)
throw error
}
}
// 获取当前用户信息
async function getCurrentUser() {
try {
const response = await fetch('/api/auth/me', {
headers: {
...getAuthHeaders()
}
})
if (!response.ok) {
throw new Error('获取用户信息失败')
}
const userData = await response.json()
// 更新本地状态
userId.value = userData.id
username.value = userData.username
userIdLogin.value = userData.user_id
phoneNumber.value = userData.phone_number || ''
avatar.value = userData.avatar || ''
userRole.value = userData.role
departmentId.value = userData.department_id || null
departmentName.value = userData.department_name || ''
return userData
} catch (error) {
console.error('获取用户信息错误:', error)
throw error
}
}
// 更新个人资料
async function updateProfile(profileData) {
try {
const response = await fetch('/api/auth/profile', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
...getAuthHeaders()
},
body: JSON.stringify(profileData)
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.detail || '更新个人资料失败')
}
const userData = await response.json()
// 更新本地状态
if (typeof userData.username === 'string') {
username.value = userData.username
}
if (typeof userData.phone_number !== 'undefined') {
phoneNumber.value = userData.phone_number || ''
}
return userData
} catch (error) {
console.error('更新个人资料错误:', error)
throw error
}
}
return {
// 状态
token,
userId,
username,
userIdLogin,
phoneNumber,
avatar,
userRole,
departmentId,
departmentName,
// 计算属性
isLoggedIn,
isAdmin,
isSuperAdmin,
// 方法
login,
logout,
initialize,
checkFirstRun,
getAuthHeaders,
getUsers,
createUser,
updateUser,
deleteUser,
validateUsernameAndGenerateUserId,
uploadAvatar,
getCurrentUser,
updateProfile
}
})
// 检查当前用户是否有管理员权限
export const checkAdminPermission = () => {
const userStore = useUserStore()
if (!userStore.isAdmin) {
throw new Error('需要管理员权限')
}
return true
}
// 检查当前用户是否有超级管理员权限
export const checkSuperAdminPermission = () => {
const userStore = useUserStore()
return userStore.isSuperAdmin
}