deotalandAi/apps/frontend/src/stores/agents.js

611 lines
15 KiB
JavaScript

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import {
getAgents,
getAgentById,
createAgent,
updateAgent,
deleteAgent,
bindDevice,
unbindDevice,
verifyBinding,
clearAgentCache
} from '../services/agentService.js'
// 智能体状态常量
export const AGENT_STATUS = {
ACTIVE: 'active',
INACTIVE: 'inactive',
ARCHIVED: 'archived'
}
// 智能体角色常量
export const AGENT_ROLES = {
ASSISTANT: 'assistant',
CONSULTANT: 'consultant',
CREATIVE: 'creative',
EDUCATOR: 'educator',
CUSTOM: 'custom'
}
// 语音模型常量
export const VOICE_MODELS = {
XIAOYI: 'xiaoyi',
XIAOMING: 'xiaoming',
XIAOLI: 'xiaoli',
XIAOWEI: 'xiaowei',
CUSTOM: 'custom'
}
export const useAgentStore = defineStore('agents', () => {
// ==================== 状态定义 ====================
// 智能体列表状态
const agents = ref([])
const currentAgent = ref(null)
// 列表查询状态
const loading = ref(false)
const error = ref(null)
const searchQuery = ref('')
const statusFilter = ref('')
const deviceBoundFilter = ref(null)
const sortBy = ref('updatedAt')
const sortOrder = ref('desc') // 'asc' | 'desc'
// 分页状态
const pagination = ref({
page: 1,
pageSize: 12,
total: 0,
totalPages: 0,
hasNext: false,
hasPrev: false
})
// 设备绑定状态
const bindingStates = ref(new Map()) // agentId -> { loading, error, verifying }
// 缓存状态
const lastFetchTime = ref(null)
const cacheExpiry = ref(5 * 60 * 1000) // 5分钟
// ==================== 计算属性 ====================
// 过滤后的智能体列表
const filteredAgents = computed(() => {
let filtered = [...agents.value]
// 搜索过滤
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase()
filtered = filtered.filter(agent =>
agent.name.toLowerCase().includes(query) ||
agent.description.toLowerCase().includes(query)
)
}
// 状态过滤
if (statusFilter.value) {
filtered = filtered.filter(agent => agent.status === statusFilter.value)
}
// 设备绑定过滤
if (deviceBoundFilter.value !== null) {
filtered = filtered.filter(agent =>
deviceBoundFilter.value ? agent.device.bound : !agent.device.bound
)
}
// 排序
filtered.sort((a, b) => {
let aVal = a[sortBy.value]
let bVal = b[sortBy.value]
// 处理日期排序
if (sortBy.value === 'created_at' || sortBy.value === 'updatedAt') {
aVal = new Date(aVal)
bVal = new Date(bVal)
}
if (sortOrder.value === 'asc') {
return aVal < bVal ? -1 : aVal > bVal ? 1 : 0
} else {
return aVal > bVal ? -1 : aVal < bVal ? 1 : 0
}
})
return filtered
})
// 已绑定的智能体数量
const boundAgentsCount = computed(() => {
return agents.value.filter(agent => agent.device.bound).length
})
// 活跃智能体数量
const activeAgentsCount = computed(() => {
return agents.value.filter(agent => agent.status === AGENT_STATUS.ACTIVE).length
})
// 缓存是否有效
const isCacheValid = computed(() => {
if (!lastFetchTime.value) return false
return Date.now() - lastFetchTime.value < cacheExpiry.value
})
// 当前页的智能体(考虑分页)
const paginatedAgents = computed(() => {
const startIndex = (pagination.value.page - 1) * pagination.value.pageSize
const endIndex = startIndex + pagination.value.pageSize
return filteredAgents.value.slice(startIndex, endIndex)
})
// ==================== 基础方法 ====================
/**
* 获取智能体列表
* @param {Object} options - 查询选项
*/
const fetchAgents = async (options = {}) => {
// 检查缓存
if (isCacheValid.value && agents.value.length > 0 && !options.forceRefresh) {
return agents.value
}
loading.value = true
error.value = null
try {
const queryOptions = {
page: pagination.value.page,
pageSize: pagination.value.pageSize,
search: searchQuery.value,
status: statusFilter.value,
deviceBound: deviceBoundFilter.value,
...options
}
const result = await getAgents(queryOptions)
agents.value = result.agents
pagination.value = result.pagination
lastFetchTime.value = Date.now()
return result.agents
} catch (err) {
error.value = err.message || '获取智能体列表失败'
console.error('Failed to fetch agents:', err)
return []
} finally {
loading.value = false
}
}
/**
* 获取单个智能体详情
* @param {string} id - 智能体ID
*/
const fetchAgentById = async (id) => {
if (!id) return null
loading.value = true
error.value = null
try {
const agent = await getAgentById(id)
// 更新列表中的智能体(如果存在)
const index = agents.value.findIndex(a => a.id === id)
if (index !== -1) {
agents.value[index] = agent
} else {
agents.value.push(agent)
}
currentAgent.value = agent
return agent
} catch (err) {
error.value = err.message || '获取智能体详情失败'
console.error('Failed to fetch agent by id:', err)
return null
} finally {
loading.value = false
}
}
/**
* 创建智能体
* @param {Object} agentData - 智能体数据
*/
const createNewAgent = async (agentData) => {
loading.value = true
error.value = null
try {
const newAgent = await createAgent(agentData)
// 添加到列表开头
agents.value.unshift(newAgent)
pagination.value.total += 1
pagination.value.totalPages = Math.ceil(pagination.value.total / pagination.value.pageSize)
// 清除缓存
clearCache()
return newAgent
} catch (err) {
error.value = err.message || '创建智能体失败'
console.error('Failed to create agent:', err)
throw err
} finally {
loading.value = false
}
}
/**
* 更新智能体
* @param {string} id - 智能体ID
* @param {Object} updateData - 更新数据
*/
const updateAgentById = async (id, updateData) => {
loading.value = true
error.value = null
try {
const updatedAgent = await updateAgent(id, updateData)
// 更新列表中的智能体
const index = agents.value.findIndex(a => a.id === id)
if (index !== -1) {
agents.value[index] = updatedAgent
}
// 更新当前选中的智能体
if (currentAgent.value && currentAgent.value.id === id) {
currentAgent.value = updatedAgent
}
// 清除缓存
clearCache()
return updatedAgent
} catch (err) {
error.value = err.message || '更新智能体失败'
console.error('Failed to update agent:', err)
throw err
} finally {
loading.value = false
}
}
/**
* 删除智能体
* @param {string} id - 智能体ID
*/
const deleteAgentById = async (id) => {
loading.value = true
error.value = null
try {
await deleteAgent(id)
// 从列表中移除
const index = agents.value.findIndex(a => a.id === id)
if (index !== -1) {
agents.value.splice(index, 1)
pagination.value.total -= 1
pagination.value.totalPages = Math.ceil(pagination.value.total / pagination.value.pageSize)
}
// 清除当前选中的智能体
if (currentAgent.value && currentAgent.value.id === id) {
currentAgent.value = null
}
// 清除缓存
clearCache()
return true
} catch (err) {
error.value = err.message || '删除智能体失败'
console.error('Failed to delete agent:', err)
throw err
} finally {
loading.value = false
}
}
// ==================== 设备绑定相关方法 ====================
/**
* 绑定设备
* @param {string} id - 智能体ID
* @param {Object} deviceInfo - 设备信息
*/
const bindAgentDevice = async (id, deviceInfo) => {
const bindingState = bindingStates.value.get(id) || { loading: false, error: null, verifying: false }
bindingStates.value.set(id, { ...bindingState, loading: true, error: null })
try {
const result = await bindDevice(id, deviceInfo)
// 更新智能体设备状态
const agent = agents.value.find(a => a.id === id)
if (agent) {
agent.device = result.device
agent.updatedAt = new Date().toISOString()
}
if (currentAgent.value && currentAgent.value.id === id) {
currentAgent.value.device = result.device
currentAgent.value.updatedAt = new Date().toISOString()
}
return result
} catch (err) {
const errorMsg = err.message || '设备绑定失败'
bindingStates.value.set(id, { ...bindingState, loading: false, error: errorMsg })
console.error('Failed to bind device:', err)
throw err
} finally {
bindingStates.value.set(id, { ...bindingStates.value.get(id), loading: false })
}
}
/**
* 解绑设备
* @param {string} id - 智能体ID
*/
const unbindAgentDevice = async (id) => {
const bindingState = bindingStates.value.get(id) || { loading: false, error: null, verifying: false }
bindingStates.value.set(id, { ...bindingState, loading: true, error: null })
try {
const result = await unbindDevice(id)
// 更新智能体设备状态
const agent = agents.value.find(a => a.id === id)
if (agent) {
agent.device = { bound: false }
agent.updatedAt = new Date().toISOString()
}
if (currentAgent.value && currentAgent.value.id === id) {
currentAgent.value.device = { bound: false }
currentAgent.value.updatedAt = new Date().toISOString()
}
return result
} catch (err) {
const errorMsg = err.message || '设备解绑失败'
bindingStates.value.set(id, { ...bindingState, loading: false, error: errorMsg })
console.error('Failed to unbind device:', err)
throw err
} finally {
bindingStates.value.set(id, { ...bindingStates.value.get(id), loading: false })
}
}
/**
* 验证设备绑定
* @param {string} id - 智能体ID
* @param {string} verificationCode - 验证码
*/
const verifyAgentBinding = async (id, verificationCode) => {
const bindingState = bindingStates.value.get(id) || { loading: false, error: null, verifying: false }
bindingStates.value.set(id, { ...bindingState, verifying: true, error: null })
try {
const result = await verifyBinding(id, verificationCode)
return result
} catch (err) {
const errorMsg = err.message || '验证失败'
bindingStates.value.set(id, { ...bindingState, verifying: false, error: errorMsg })
console.error('Failed to verify binding:', err)
throw err
} finally {
bindingStates.value.set(id, { ...bindingStates.value.get(id), verifying: false })
}
}
// ==================== 过滤和搜索方法 ====================
/**
* 设置搜索查询
* @param {string} query - 搜索关键词
*/
const setSearchQuery = (query) => {
searchQuery.value = query
pagination.value.page = 1 // 重置到第一页
}
/**
* 设置状态过滤
* @param {string} status - 状态筛选
*/
const setStatusFilter = (status) => {
statusFilter.value = status
pagination.value.page = 1 // 重置到第一页
}
/**
* 设置设备绑定过滤
* @param {boolean} deviceBound - 设备绑定筛选
*/
const setDeviceBoundFilter = (deviceBound) => {
deviceBoundFilter.value = deviceBound
pagination.value.page = 1 // 重置到第一页
}
/**
* 设置排序
* @param {string} sortBy - 排序字段
* @param {string} order - 排序方向
*/
const setSorting = (sortByField, order = 'desc') => {
sortBy.value = sortByField
sortOrder.value = order
}
/**
* 设置分页
* @param {number} page - 页码
* @param {number} pageSize - 每页数量
*/
const setPagination = (page, pageSize) => {
pagination.value.page = page
pagination.value.pageSize = pageSize
}
/**
* 清除所有过滤条件
*/
const clearFilters = () => {
searchQuery.value = ''
statusFilter.value = ''
deviceBoundFilter.value = null
sortBy.value = 'updatedAt'
sortOrder.value = 'desc'
pagination.value.page = 1
}
// ==================== 工具方法 ====================
/**
* 获取设备绑定状态
* @param {string} agentId - 智能体ID
*/
const getBindingState = (agentId) => {
return bindingStates.value.get(agentId) || { loading: false, error: null, verifying: false }
}
/**
* 清除错误状态
*/
const clearError = () => {
error.value = null
}
/**
* 清除设备绑定错误
* @param {string} agentId - 智能体ID
*/
const clearBindingError = (agentId) => {
const state = bindingStates.value.get(agentId)
if (state) {
bindingStates.value.set(agentId, { ...state, error: null })
}
}
/**
* 清除缓存
*/
const clearCache = async () => {
try {
await clearAgentCache()
lastFetchTime.value = null
console.log('Agent store cache cleared')
} catch (err) {
console.error('Failed to clear cache:', err)
}
}
/**
* 刷新数据
*/
const refresh = async () => {
await fetchAgents({ forceRefresh: true })
}
/**
* 重置状态
*/
const reset = () => {
agents.value = []
currentAgent.value = null
loading.value = false
error.value = null
searchQuery.value = ''
statusFilter.value = ''
deviceBoundFilter.value = null
sortBy.value = 'updatedAt'
sortOrder.value = 'desc'
pagination.value = {
page: 1,
pageSize: 12,
total: 0,
totalPages: 0,
hasNext: false,
hasPrev: false
}
bindingStates.value.clear()
lastFetchTime.value = null
}
return {
// 状态
agents,
currentAgent,
loading,
error,
searchQuery,
statusFilter,
deviceBoundFilter,
sortBy,
sortOrder,
pagination,
bindingStates,
lastFetchTime,
cacheExpiry,
// 计算属性
filteredAgents,
boundAgentsCount,
activeAgentsCount,
isCacheValid,
paginatedAgents,
// 基础方法
fetchAgents,
fetchAgentById,
createNewAgent,
updateAgentById,
deleteAgentById,
// 设备绑定方法
bindAgentDevice,
unbindAgentDevice,
verifyAgentBinding,
// 过滤和搜索方法
setSearchQuery,
setStatusFilter,
setDeviceBoundFilter,
setSorting,
setPagination,
clearFilters,
// 工具方法
getBindingState,
clearError,
clearBindingError,
clearCache,
refresh,
reset
}
}, {
persist: {
key: 'agents',
storage: localStorage,
paths: ['searchQuery', 'statusFilter', 'deviceBoundFilter', 'sortBy', 'sortOrder', 'pagination.page', 'pagination.pageSize']
}
})