import { ref, reactive, computed } from 'vue' import { getToolListApi, getToolDetailApi, createToolApi, updateToolApi, deleteToolApi, getEnabledToolsApi, getToolOptionsApi, exportToolsApi, exportToolWsdlApi, importToolsApi, importToolsFromWsdlApi, executeToolApi, batchExecuteToolsApi, batchCloneToolsApi, cloneToolApi, getToolVersionsApi, compareToolVersionsApi, rollbackToolVersionApi, getToolParameterTemplateApi, getToolExecutionTrendApi, getToolAssetsApi } from '@/apis/external-systems/tool_api' import { getEnvironmentListApi } from '@/apis/external-systems/environment_api' import { getAccessRuleListApi } from '@/apis/external-systems/access_rule_api' import { unwrap } from './utils' /** * 工具管理 composable * 封装工具列表/详情/CRUD/执行/批量/克隆/版本/参数模板/执行趋势/依赖资产/访问规则的 API 调用与状态管理 * * 字段契约对齐 backend/server/routers/external_systems/tool_router.py: * - list_tools: query 用 offset/limit(非 skip),limit 默认 100 最大 500 * - execute: body 用 tool_slug/env_key/arguments(非 tool_id/environment_id/parameters) * - batch_execute: body 用 executions: [{tool_slug, system_id, env_key, arguments}] * - clone: body 用 system_id(非 target_system_id) * - batch_clone: body 用 source_tool_ids(非 tool_ids) * - execution_trend: query 用 interval/env_key(非 granularity/environment_id) */ export function useExternalTool() { // ===== 列表 ===== const list = ref([]) const listLoading = ref(false) const listError = ref(null) const total = ref(0) const pagination = reactive({ current: 1, pageSize: 100 }) const filters = reactive({ system_id: undefined, category: undefined, adapter_type: undefined, enabled: undefined, keyword: '' }) // ===== 详情 ===== const detail = ref(null) const detailLoading = ref(false) const detailError = ref(null) // ===== 版本 ===== const versions = ref([]) const versionsLoading = ref(false) const versionsError = ref(null) const compareResult = ref(null) const compareLoading = ref(false) const compareError = ref(null) // ===== 参数模板 ===== const parameterTemplate = ref(null) const parameterTemplateLoading = ref(false) const parameterTemplateError = ref(null) // ===== 执行趋势 ===== const executionTrend = ref(null) const executionTrendLoading = ref(false) const executionTrendError = ref(null) // ===== 依赖资产 ===== const assets = ref(null) const assetsLoading = ref(false) const assetsError = ref(null) // ===== 访问规则(详情页访问规则 Tab)===== const accessRules = ref([]) const accessRulesLoading = ref(false) const accessRulesError = ref(null) // ===== 选项与启用列表 ===== const options = ref(null) const optionsLoading = ref(false) const enabledTools = ref([]) const enabledToolsLoading = ref(false) // ===== 环境列表(用于执行趋势/执行弹窗的环境筛选)===== const environments = ref([]) const environmentsLoading = ref(false) // ===== 操作状态 ===== const submitting = ref(false) const submitError = ref(null) const executing = ref(false) const executeResult = ref(null) const executeError = ref(null) const cloning = ref(false) const batchCloning = ref(false) const batchExecuting = ref(false) const importing = ref(false) const importResult = ref(null) const rollingBack = ref(false) const isEmpty = computed(() => !list.value?.length) // ===== 列表 ===== async function fetchList() { listLoading.value = true listError.value = null try { const params = { offset: (pagination.current - 1) * pagination.pageSize, limit: pagination.pageSize, ...filters } // 清理 undefined 字段 Object.keys(params).forEach((k) => { if (params[k] === undefined || params[k] === '' || params[k] === null) { delete params[k] } }) const res = unwrap(await getToolListApi(params)) list.value = res?.items || [] total.value = res?.total ?? list.value.length } catch (err) { listError.value = err list.value = [] total.value = 0 } finally { listLoading.value = false } } function setFilter(newFilters) { Object.assign(filters, newFilters) pagination.current = 1 } function resetFilters() { filters.system_id = undefined filters.category = undefined filters.adapter_type = undefined filters.enabled = undefined filters.keyword = '' pagination.current = 1 } function setPagination({ page, pageSize }) { if (pageSize) pagination.pageSize = pageSize if (page) pagination.current = page } // ===== 详情 ===== async function fetchDetail(id) { detailLoading.value = true detailError.value = null detail.value = null try { detail.value = unwrap(await getToolDetailApi(id)) || null } catch (err) { detailError.value = err } finally { detailLoading.value = false } } // ===== CRUD ===== async function create(data) { submitting.value = true submitError.value = null try { return unwrap(await createToolApi(data)) } catch (err) { submitError.value = err throw err } finally { submitting.value = false } } async function update(id, data) { submitting.value = true submitError.value = null try { return unwrap(await updateToolApi(id, data)) } catch (err) { submitError.value = err throw err } finally { submitting.value = false } } async function remove(id) { submitError.value = null try { await deleteToolApi(id) return true } catch (err) { submitError.value = err throw err } } // ===== 克隆 ===== async function clone(id, data) { cloning.value = true submitError.value = null try { return unwrap(await cloneToolApi(id, data)) } catch (err) { submitError.value = err throw err } finally { cloning.value = false } } async function batchClone(data) { batchCloning.value = true submitError.value = null try { return unwrap(await batchCloneToolsApi(data)) } catch (err) { submitError.value = err throw err } finally { batchCloning.value = false } } // ===== 执行 ===== async function execute(data) { executing.value = true executeError.value = null executeResult.value = null try { executeResult.value = unwrap(await executeToolApi(data)) || null return executeResult.value } catch (err) { executeError.value = err throw err } finally { executing.value = false } } async function batchExecute(data) { batchExecuting.value = true submitError.value = null try { return unwrap(await batchExecuteToolsApi(data)) } catch (err) { submitError.value = err throw err } finally { batchExecuting.value = false } } // ===== 版本 ===== async function fetchVersions(id, params) { versionsLoading.value = true versionsError.value = null try { const res = unwrap(await getToolVersionsApi(id, params)) versions.value = res?.items || res || [] } catch (err) { versionsError.value = err versions.value = [] } finally { versionsLoading.value = false } } async function compareVersions(id, v1, v2) { compareLoading.value = true compareError.value = null compareResult.value = null try { compareResult.value = unwrap(await compareToolVersionsApi(id, v1, v2)) || null return compareResult.value } catch (err) { compareError.value = err throw err } finally { compareLoading.value = false } } async function rollbackVersion(id, v) { rollingBack.value = true submitError.value = null try { return unwrap(await rollbackToolVersionApi(id, v)) } catch (err) { submitError.value = err throw err } finally { rollingBack.value = false } } // ===== 参数模板 ===== async function fetchParameterTemplate(id) { parameterTemplateLoading.value = true parameterTemplateError.value = null parameterTemplate.value = null try { parameterTemplate.value = unwrap(await getToolParameterTemplateApi(id)) || null return parameterTemplate.value } catch (err) { parameterTemplateError.value = err throw err } finally { parameterTemplateLoading.value = false } } // ===== 执行趋势 ===== async function fetchExecutionTrend(id, params) { executionTrendLoading.value = true executionTrendError.value = null try { executionTrend.value = unwrap(await getToolExecutionTrendApi(id, params)) || null return executionTrend.value } catch (err) { executionTrendError.value = err } finally { executionTrendLoading.value = false } } // ===== 依赖资产 ===== async function fetchAssets(id) { assetsLoading.value = true assetsError.value = null try { assets.value = unwrap(await getToolAssetsApi(id)) || null return assets.value } catch (err) { assetsError.value = err } finally { assetsLoading.value = false } } // ===== 访问规则(按 tool_slug 查询,供详情页访问规则 Tab 使用)===== async function fetchAccessRules(toolSlug, params = {}) { accessRulesLoading.value = true accessRulesError.value = null try { const res = unwrap(await getAccessRuleListApi({ tool_slug: toolSlug, ...params })) accessRules.value = res?.items || [] return accessRules.value } catch (err) { accessRulesError.value = err accessRules.value = [] } finally { accessRulesLoading.value = false } } // ===== 选项与启用列表 ===== async function fetchOptions(params) { optionsLoading.value = true try { options.value = unwrap(await getToolOptionsApi(params)) || null return options.value } catch { options.value = null } finally { optionsLoading.value = false } } async function fetchEnabledTools(params) { enabledToolsLoading.value = true try { const res = unwrap(await getEnabledToolsApi(params)) enabledTools.value = res?.items || res || [] return enabledTools.value } catch { enabledTools.value = [] } finally { enabledToolsLoading.value = false } } // ===== 环境列表(用于执行趋势/执行弹窗的环境筛选)===== async function fetchEnvironments(params) { environmentsLoading.value = true try { const res = unwrap(await getEnvironmentListApi(params)) environments.value = res?.items || [] return environments.value } catch { environments.value = [] } finally { environmentsLoading.value = false } } // ===== 导入 ===== async function importTools(data) { importing.value = true importResult.value = null try { importResult.value = unwrap(await importToolsApi(data)) || null return importResult.value } catch (err) { submitError.value = err throw err } finally { importing.value = false } } async function importFromWsdl(data) { importing.value = true importResult.value = null try { importResult.value = unwrap(await importToolsFromWsdlApi(data)) || null return importResult.value } catch (err) { submitError.value = err throw err } finally { importing.value = false } } // ===== 导出(blob)===== async function exportTools(params) { try { const response = await exportToolsApi(params) return response } catch (err) { submitError.value = err throw err } } async function exportWsdl(params) { try { const response = await exportToolWsdlApi(params) return response } catch (err) { submitError.value = err throw err } } // ===== 重置 ===== function resetDetail() { detail.value = null detailError.value = null detailLoading.value = false } function resetVersions() { versions.value = [] versionsError.value = null compareResult.value = null compareError.value = null } function resetExecute() { executeResult.value = null executeError.value = null } function resetErrors() { listError.value = null detailError.value = null versionsError.value = null compareError.value = null parameterTemplateError.value = null executionTrendError.value = null assetsError.value = null accessRulesError.value = null submitError.value = null executeError.value = null } return { // 列表 list, listLoading, listError, total, pagination, filters, isEmpty, fetchList, setFilter, resetFilters, setPagination, // 详情 detail, detailLoading, detailError, fetchDetail, resetDetail, // CRUD create, update, remove, submitting, submitError, // 克隆 clone, batchClone, cloning, batchCloning, // 执行 execute, batchExecute, executing, batchExecuting, executeResult, executeError, resetExecute, // 版本 versions, versionsLoading, versionsError, fetchVersions, compareResult, compareLoading, compareError, compareVersions, rollbackVersion, rollingBack, resetVersions, // 参数模板 parameterTemplate, parameterTemplateLoading, parameterTemplateError, fetchParameterTemplate, // 执行趋势 executionTrend, executionTrendLoading, executionTrendError, fetchExecutionTrend, // 依赖资产 assets, assetsLoading, assetsError, fetchAssets, // 访问规则 accessRules, accessRulesLoading, accessRulesError, fetchAccessRules, // 选项与启用 options, optionsLoading, fetchOptions, enabledTools, enabledToolsLoading, fetchEnabledTools, // 环境(用于执行趋势/执行弹窗筛选) environments, environmentsLoading, fetchEnvironments, // 导入 importTools, importFromWsdl, importing, importResult, // 导出 exportTools, exportWsdl, // 重置 resetErrors } }