611 lines
15 KiB
JavaScript
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']
|
|
}
|
|
}) |