590 lines
14 KiB
JavaScript
590 lines
14 KiB
JavaScript
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
|
||
}
|
||
}
|