2026-06-20 22:21:34 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 集成工具生成 - 3 步流程状态机
|
|
|
|
|
|
*
|
|
|
|
|
|
* 流程:
|
|
|
|
|
|
* 步骤① 发现资源 → POST /discover
|
|
|
|
|
|
* 步骤② 预览工具 → POST /preview + GET /tools(冲突比对)→ POST /generate
|
|
|
|
|
|
* 步骤③ 生成草稿 → POST /integration-tools(持久化,事务型)
|
|
|
|
|
|
*
|
2026-07-11 21:53:27 +08:00
|
|
|
|
* 持久化刷新:persist 不在 composable 内刷新列表,交由组件控制以保持分页状态同步
|
|
|
|
|
|
*
|
2026-06-20 22:21:34 +08:00
|
|
|
|
* 数据流:
|
|
|
|
|
|
* discover 返回 resources → preview 返回 tools → generate 返回 draft.tool_configs
|
|
|
|
|
|
* 前端按步骤②勾选项过滤草稿,persist 仅持久化勾选项
|
|
|
|
|
|
*
|
|
|
|
|
|
* 冲突处理:
|
|
|
|
|
|
* 后端 override_existing 硬编码 False,冲突 slug 仅能跳过
|
|
|
|
|
|
* 前端比对 GET /tools 已有 slug,冲突项禁用勾选
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { ref, reactive, computed } from 'vue'
|
|
|
|
|
|
import {
|
|
|
|
|
|
getIntegrationToolListApi,
|
|
|
|
|
|
discoverIntegrationToolsApi,
|
|
|
|
|
|
previewIntegrationToolsApi,
|
|
|
|
|
|
generateIntegrationToolsApi,
|
|
|
|
|
|
persistIntegrationToolsApi
|
|
|
|
|
|
} from '@/apis/external-systems/integration_generator_api'
|
|
|
|
|
|
import { getSystemListApi } from '@/apis/external-systems/system_api'
|
|
|
|
|
|
import { getEnvironmentListApi } from '@/apis/external-systems/environment_api'
|
|
|
|
|
|
import { getToolListApi, deleteToolApi } from '@/apis/external-systems/tool_api'
|
|
|
|
|
|
import { getVendorSourceTypesApi } from '@/apis/external-systems/vendor_integration_api'
|
|
|
|
|
|
import { unwrap } from './utils'
|
|
|
|
|
|
|
|
|
|
|
|
// 步骤常量
|
|
|
|
|
|
const STEP_DISCOVER = 0
|
|
|
|
|
|
const STEP_PREVIEW = 1
|
|
|
|
|
|
const STEP_GENERATE = 2
|
|
|
|
|
|
|
|
|
|
|
|
export function useIntegrationGenerator() {
|
|
|
|
|
|
// ===== 列表状态(已持久化的集成工具) =====
|
|
|
|
|
|
const toolList = ref([])
|
|
|
|
|
|
const listLoading = ref(false)
|
|
|
|
|
|
const listTotal = ref(0)
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 弹窗与步骤 =====
|
|
|
|
|
|
const modalVisible = ref(false)
|
|
|
|
|
|
const currentStep = ref(STEP_DISCOVER)
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 表单状态 =====
|
|
|
|
|
|
const form = reactive({
|
|
|
|
|
|
system_id: null,
|
|
|
|
|
|
env_key: undefined,
|
|
|
|
|
|
source_type: undefined,
|
|
|
|
|
|
discovery_options: {}
|
|
|
|
|
|
})
|
|
|
|
|
|
const discoveryOptionsStr = ref('{}')
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 选项数据 =====
|
|
|
|
|
|
const systemOptions = ref([])
|
|
|
|
|
|
const environmentOptions = ref([])
|
|
|
|
|
|
const sourceTypeOptions = ref([])
|
|
|
|
|
|
const existingToolSlugs = ref(new Set())
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 流程结果 =====
|
|
|
|
|
|
const discoverResult = ref(null)
|
|
|
|
|
|
const previewResult = ref(null)
|
|
|
|
|
|
const draftResult = ref(null)
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 选中的工具 slug 列表 =====
|
|
|
|
|
|
const selectedToolSlugs = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 加载状态 =====
|
|
|
|
|
|
const discoverLoading = ref(false)
|
|
|
|
|
|
const previewLoading = ref(false)
|
|
|
|
|
|
const generateLoading = ref(false)
|
|
|
|
|
|
const persistLoading = ref(false)
|
|
|
|
|
|
const deleteLoading = ref(false)
|
|
|
|
|
|
const optionsLoading = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 错误状态(组件 watch 后显示 message.error) =====
|
|
|
|
|
|
const error = ref(null)
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 计算属性 =====
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 预览工具列表(带冲突标记)
|
|
|
|
|
|
* 冲突定义:preview 返回的 slug 已存在于 GET /tools 结果中
|
|
|
|
|
|
*/
|
|
|
|
|
|
const previewTools = computed(() => {
|
|
|
|
|
|
const tools = previewResult.value?.tools || []
|
|
|
|
|
|
return tools.map((tool) => ({
|
|
|
|
|
|
...tool,
|
|
|
|
|
|
is_conflict: existingToolSlugs.value.has(tool.slug)
|
|
|
|
|
|
}))
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const previewStats = computed(() => {
|
|
|
|
|
|
const total = previewTools.value.length
|
|
|
|
|
|
const conflictCount = previewTools.value.filter((t) => t.is_conflict).length
|
|
|
|
|
|
const newCount = total - conflictCount
|
|
|
|
|
|
return { total, newCount, conflictCount }
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 草稿列表(按步骤②勾选过滤)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 后端 GenerateToolsDraftOutput 结构:
|
|
|
|
|
|
* { system_id, source_type, draft: { tool_configs, override_existing } }
|
|
|
|
|
|
* 前端按勾选项过滤草稿后展示与持久化
|
|
|
|
|
|
*/
|
|
|
|
|
|
const draftTools = computed(() => {
|
|
|
|
|
|
const configs = draftResult.value?.draft?.tool_configs || []
|
|
|
|
|
|
return configs.filter((config) => selectedToolSlugs.value.includes(config.slug))
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const hasUnpersistedDrafts = computed(
|
|
|
|
|
|
() => currentStep.value === STEP_GENERATE && draftTools.value.length > 0
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 列表加载 =====
|
|
|
|
|
|
|
|
|
|
|
|
async function fetchToolList(params = {}) {
|
|
|
|
|
|
listLoading.value = true
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = unwrap(await getIntegrationToolListApi(params))
|
2026-07-11 21:53:27 +08:00
|
|
|
|
toolList.value = data?.items ?? []
|
|
|
|
|
|
listTotal.value = data?.total ?? toolList.value.length
|
2026-06-20 22:21:34 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
listLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 选项加载 =====
|
|
|
|
|
|
|
|
|
|
|
|
async function loadInitData() {
|
|
|
|
|
|
optionsLoading.value = true
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
try {
|
|
|
|
|
|
const [systemsRes, sourceTypesRes] = await Promise.all([
|
|
|
|
|
|
getSystemListApi({ limit: 500 }),
|
|
|
|
|
|
getVendorSourceTypesApi()
|
|
|
|
|
|
])
|
|
|
|
|
|
const systemsData = unwrap(systemsRes)
|
|
|
|
|
|
systemOptions.value = systemsData?.items || systemsData || []
|
|
|
|
|
|
// 后端返回 { items: [{ source_type, operations }], total },
|
|
|
|
|
|
// a-select options 需要 { label, value } 格式
|
|
|
|
|
|
const sourceTypesData = unwrap(sourceTypesRes)
|
|
|
|
|
|
const sourceTypeItems = sourceTypesData?.items || []
|
|
|
|
|
|
sourceTypeOptions.value = sourceTypeItems.map((item) => ({
|
|
|
|
|
|
label: item.source_type,
|
|
|
|
|
|
value: item.source_type
|
|
|
|
|
|
}))
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
optionsLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadEnvironments(systemId) {
|
|
|
|
|
|
if (!systemId) {
|
|
|
|
|
|
environmentOptions.value = []
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = unwrap(await getEnvironmentListApi({ system_id: systemId, limit: 100 }))
|
|
|
|
|
|
environmentOptions.value = data?.items || data || []
|
|
|
|
|
|
} catch (e) {
|
2026-07-11 21:53:27 +08:00
|
|
|
|
environmentOptions.value = []
|
|
|
|
|
|
throw e
|
2026-06-20 22:21:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadExistingToolSlugs(systemId) {
|
|
|
|
|
|
if (!systemId) {
|
|
|
|
|
|
existingToolSlugs.value = new Set()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = unwrap(await getToolListApi({ system_id: systemId, limit: 500 }))
|
|
|
|
|
|
const tools = data?.items || data || []
|
|
|
|
|
|
existingToolSlugs.value = new Set(tools.map((t) => t.slug))
|
|
|
|
|
|
} catch (e) {
|
2026-07-11 21:53:27 +08:00
|
|
|
|
existingToolSlugs.value = new Set()
|
|
|
|
|
|
throw e
|
2026-06-20 22:21:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function handleSystemChange(systemId) {
|
|
|
|
|
|
form.env_key = undefined
|
2026-07-11 21:53:27 +08:00
|
|
|
|
error.value = null
|
|
|
|
|
|
const [envResult, slugsResult] = await Promise.allSettled([
|
|
|
|
|
|
loadEnvironments(systemId),
|
|
|
|
|
|
loadExistingToolSlugs(systemId)
|
|
|
|
|
|
])
|
|
|
|
|
|
const errors = []
|
|
|
|
|
|
if (envResult.status === 'rejected') {
|
|
|
|
|
|
errors.push(envResult.reason)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (slugsResult.status === 'rejected') {
|
|
|
|
|
|
errors.push(slugsResult.reason)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (errors.length > 0) {
|
|
|
|
|
|
error.value =
|
|
|
|
|
|
errors.length === 1
|
|
|
|
|
|
? errors[0]
|
|
|
|
|
|
: new Error(errors.map((e) => e.message || String(e)).join(';'))
|
|
|
|
|
|
}
|
2026-06-20 22:21:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 流程请求构造 =====
|
|
|
|
|
|
|
|
|
|
|
|
function parseDiscoveryOptions() {
|
|
|
|
|
|
const raw = discoveryOptionsStr.value?.trim()
|
|
|
|
|
|
if (!raw) {
|
2026-07-11 21:53:27 +08:00
|
|
|
|
// 清空时统一发送空对象,避免下游 handler 对 null 处理不一致
|
|
|
|
|
|
form.discovery_options = {}
|
2026-06-20 22:21:34 +08:00
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
form.discovery_options = JSON.parse(raw)
|
|
|
|
|
|
return true
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
error.value = new Error('发现选项必须为合法 JSON')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildFlowRequest() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
system_id: form.system_id,
|
|
|
|
|
|
// 使用 ?? 而非 ||,避免空字符串/0 被误转 null;
|
|
|
|
|
|
// discovery_options 为空对象 {} 时应保留(语义为"无特殊选项"),不应转 null
|
|
|
|
|
|
source_type: form.source_type ?? null,
|
|
|
|
|
|
env_key: form.env_key ?? null,
|
|
|
|
|
|
discovery_options: form.discovery_options ?? null
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 字段校验规则(对齐后端 ExternalToolItemRequest 约束)
|
|
|
|
|
|
const SLUG_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_-]{0,127}$/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 持久化前字段级校验(对齐后端 ExternalToolItemRequest 约束)
|
|
|
|
|
|
* - slug: 正则 ^[a-zA-Z_][a-zA-Z0-9_-]{0,127}$,长度 1-128
|
|
|
|
|
|
* - name: 长度 1-128
|
|
|
|
|
|
* - timeout: 1-300
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {string|null} 首个错误信息,null 表示通过
|
|
|
|
|
|
*/
|
|
|
|
|
|
function validateToolsBeforePersist(tools) {
|
|
|
|
|
|
for (const tool of tools) {
|
|
|
|
|
|
if (!tool.slug || !SLUG_PATTERN.test(tool.slug)) {
|
|
|
|
|
|
return `工具 slug 格式不合法:${tool.slug || '(空)'}(需匹配 ^[a-zA-Z_][a-zA-Z0-9_-]{0,127}$)`
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!tool.name || tool.name.length > 128) {
|
|
|
|
|
|
return `工具 name 长度需在 1-128 之间:${tool.name || '(空)'}`
|
|
|
|
|
|
}
|
2026-07-11 21:53:27 +08:00
|
|
|
|
if (
|
|
|
|
|
|
tool.timeout === undefined ||
|
|
|
|
|
|
tool.timeout === null ||
|
|
|
|
|
|
tool.timeout < 1 ||
|
|
|
|
|
|
tool.timeout > 300
|
|
|
|
|
|
) {
|
2026-06-20 22:21:34 +08:00
|
|
|
|
return `工具 timeout 需在 1-300 之间:${tool.timeout}`
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 3 步流程方法 =====
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 步骤①:发现资源
|
|
|
|
|
|
* 成功且有资源 → 自动进入步骤②并加载预览
|
|
|
|
|
|
* 成功但无资源 → 停留步骤①展示空状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function discover() {
|
|
|
|
|
|
if (!form.system_id) {
|
|
|
|
|
|
error.value = new Error('请选择系统')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!parseDiscoveryOptions()) return false
|
|
|
|
|
|
|
|
|
|
|
|
discoverLoading.value = true
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
try {
|
|
|
|
|
|
discoverResult.value = unwrap(await discoverIntegrationToolsApi(buildFlowRequest()))
|
|
|
|
|
|
const resources = discoverResult.value?.resources || []
|
|
|
|
|
|
if (resources.length === 0) {
|
|
|
|
|
|
// 无资源,停留步骤①
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
// 进入步骤②,加载预览工具列表
|
|
|
|
|
|
// 先加载预览,成功后再推进步骤,避免 preview 失败时步骤已推进
|
|
|
|
|
|
const previewOk = await preview()
|
|
|
|
|
|
if (previewOk) {
|
|
|
|
|
|
currentStep.value = STEP_PREVIEW
|
|
|
|
|
|
}
|
|
|
|
|
|
return previewOk
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e
|
|
|
|
|
|
return false
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
discoverLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 步骤②:预览工具列表
|
|
|
|
|
|
* 调用 POST /preview 获取工具列表
|
|
|
|
|
|
* 默认勾选所有「新增」项,冲突项不可勾选
|
|
|
|
|
|
*
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function preview() {
|
|
|
|
|
|
previewLoading.value = true
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
try {
|
|
|
|
|
|
previewResult.value = unwrap(await previewIntegrationToolsApi(buildFlowRequest()))
|
|
|
|
|
|
// 默认勾选所有非冲突项
|
|
|
|
|
|
const tools = previewResult.value?.tools || []
|
|
|
|
|
|
const existing = existingToolSlugs.value
|
|
|
|
|
|
selectedToolSlugs.value = tools.filter((t) => !existing.has(t.slug)).map((t) => t.slug)
|
|
|
|
|
|
return true
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e
|
|
|
|
|
|
return false
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
previewLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 步骤② → 步骤③:生成草稿
|
|
|
|
|
|
* 调用 POST /generate 生成全量草稿(不持久化)
|
|
|
|
|
|
* 前端按步骤②勾选项过滤草稿列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function generate() {
|
|
|
|
|
|
if (selectedToolSlugs.value.length === 0) {
|
|
|
|
|
|
error.value = new Error('请至少选择 1 个工具')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
generateLoading.value = true
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
try {
|
|
|
|
|
|
draftResult.value = unwrap(await generateIntegrationToolsApi(buildFlowRequest()))
|
|
|
|
|
|
currentStep.value = STEP_GENERATE
|
|
|
|
|
|
return true
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e
|
|
|
|
|
|
return false
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
generateLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 步骤③:持久化草稿(事务型全量成功/失败)
|
2026-07-11 21:53:27 +08:00
|
|
|
|
* 调用 POST /integration-tools,body 含 system_id + 过滤后的 tools 列表 + source_type
|
|
|
|
|
|
* 成功 → 关闭弹窗,返回后端实际持久化数量(affected);列表刷新交由组件控制,
|
|
|
|
|
|
* 以保持分页状态同步(与 deleteTool 的刷新模式一致)
|
2026-06-20 22:21:34 +08:00
|
|
|
|
* 失败 → 错误提示,支持重试
|
|
|
|
|
|
*
|
2026-07-11 21:53:27 +08:00
|
|
|
|
* 后端 PersistGeneratedToolsOutput:{ persisted: [...], affected: int, failed: int, errors: [...] }
|
|
|
|
|
|
* source_type 作为三级兜底(tool.source_type → request.source_type → system.source_type),
|
|
|
|
|
|
* 透传 form.source_type 保证与 discover/preview/generate 流程一致
|
2026-06-20 22:21:34 +08:00
|
|
|
|
*/
|
|
|
|
|
|
async function persist() {
|
|
|
|
|
|
if (draftTools.value.length === 0) {
|
|
|
|
|
|
error.value = new Error('持久化工具列表不能为空')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
// 字段级校验前置(对齐后端 ExternalToolItemRequest 约束)
|
|
|
|
|
|
const validateError = validateToolsBeforePersist(draftTools.value)
|
|
|
|
|
|
if (validateError) {
|
|
|
|
|
|
error.value = new Error(validateError)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
persistLoading.value = true
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await persistIntegrationToolsApi({
|
|
|
|
|
|
system_id: form.system_id,
|
2026-07-11 21:53:27 +08:00
|
|
|
|
tools: draftTools.value,
|
|
|
|
|
|
source_type: form.source_type ?? null
|
2026-06-20 22:21:34 +08:00
|
|
|
|
})
|
|
|
|
|
|
// 使用后端返回的 affected 字段,而非前端计算的 length
|
|
|
|
|
|
const data = unwrap(response)
|
|
|
|
|
|
const affected = data?.affected ?? draftTools.value.length
|
|
|
|
|
|
resetFlow()
|
|
|
|
|
|
modalVisible.value = false
|
|
|
|
|
|
return affected
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e
|
|
|
|
|
|
return false
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
persistLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 删除已持久化的工具
|
|
|
|
|
|
* 调用 DELETE /tools/{tool_id},system_id 为可选 query
|
|
|
|
|
|
* 成功 → 返回后端响应,由组件刷新列表
|
|
|
|
|
|
* 失败 → 设置 error,返回 false
|
|
|
|
|
|
*/
|
2026-07-11 21:53:27 +08:00
|
|
|
|
async function deleteTool(toolId, systemId) {
|
2026-06-20 22:21:34 +08:00
|
|
|
|
deleteLoading.value = true
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
try {
|
2026-07-11 21:53:27 +08:00
|
|
|
|
const result = unwrap(await deleteToolApi(toolId, systemId))
|
2026-06-20 22:21:34 +08:00
|
|
|
|
return result
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e
|
|
|
|
|
|
return false
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
deleteLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 弹窗控制 =====
|
|
|
|
|
|
|
|
|
|
|
|
function openModal() {
|
|
|
|
|
|
resetFlow()
|
|
|
|
|
|
modalVisible.value = true
|
|
|
|
|
|
loadInitData()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 关闭弹窗:若有未持久化草稿,返回 false 由组件触发二次确认
|
|
|
|
|
|
*/
|
|
|
|
|
|
function closeModal() {
|
|
|
|
|
|
if (hasUnpersistedDrafts.value) {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
modalVisible.value = false
|
|
|
|
|
|
resetFlow()
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function forceClose() {
|
|
|
|
|
|
modalVisible.value = false
|
|
|
|
|
|
resetFlow()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function back() {
|
|
|
|
|
|
if (currentStep.value > STEP_DISCOVER) {
|
|
|
|
|
|
currentStep.value--
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 重置 =====
|
|
|
|
|
|
|
|
|
|
|
|
function resetFlow() {
|
|
|
|
|
|
currentStep.value = STEP_DISCOVER
|
|
|
|
|
|
form.system_id = null
|
|
|
|
|
|
form.env_key = undefined
|
|
|
|
|
|
form.source_type = undefined
|
|
|
|
|
|
form.discovery_options = {}
|
|
|
|
|
|
discoveryOptionsStr.value = '{}'
|
|
|
|
|
|
discoverResult.value = null
|
|
|
|
|
|
previewResult.value = null
|
|
|
|
|
|
draftResult.value = null
|
|
|
|
|
|
selectedToolSlugs.value = []
|
|
|
|
|
|
environmentOptions.value = []
|
|
|
|
|
|
existingToolSlugs.value = new Set()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
// 列表
|
|
|
|
|
|
toolList,
|
|
|
|
|
|
listLoading,
|
|
|
|
|
|
listTotal,
|
|
|
|
|
|
fetchToolList,
|
|
|
|
|
|
// 弹窗与步骤
|
|
|
|
|
|
modalVisible,
|
|
|
|
|
|
currentStep,
|
|
|
|
|
|
// 表单
|
|
|
|
|
|
form,
|
|
|
|
|
|
discoveryOptionsStr,
|
|
|
|
|
|
// 选项
|
|
|
|
|
|
systemOptions,
|
|
|
|
|
|
environmentOptions,
|
|
|
|
|
|
sourceTypeOptions,
|
|
|
|
|
|
existingToolSlugs,
|
|
|
|
|
|
optionsLoading,
|
|
|
|
|
|
// 流程结果
|
|
|
|
|
|
discoverResult,
|
|
|
|
|
|
previewResult,
|
|
|
|
|
|
draftResult,
|
|
|
|
|
|
previewTools,
|
|
|
|
|
|
previewStats,
|
|
|
|
|
|
draftTools,
|
|
|
|
|
|
hasUnpersistedDrafts,
|
|
|
|
|
|
// 选中
|
|
|
|
|
|
selectedToolSlugs,
|
|
|
|
|
|
// 加载状态
|
|
|
|
|
|
discoverLoading,
|
|
|
|
|
|
previewLoading,
|
|
|
|
|
|
generateLoading,
|
|
|
|
|
|
persistLoading,
|
|
|
|
|
|
deleteLoading,
|
|
|
|
|
|
// 错误
|
|
|
|
|
|
error,
|
|
|
|
|
|
// 方法
|
|
|
|
|
|
loadInitData,
|
|
|
|
|
|
handleSystemChange,
|
|
|
|
|
|
discover,
|
|
|
|
|
|
preview,
|
|
|
|
|
|
generate,
|
|
|
|
|
|
persist,
|
|
|
|
|
|
deleteTool,
|
|
|
|
|
|
openModal,
|
|
|
|
|
|
closeModal,
|
|
|
|
|
|
forceClose,
|
|
|
|
|
|
back,
|
|
|
|
|
|
resetFlow
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|