support self-check

This commit is contained in:
锦恢 2025-07-03 13:48:58 +08:00
parent 9294275874
commit dcabd47a20
11 changed files with 104 additions and 89 deletions

View File

@ -1,15 +1,15 @@
<template>
<el-dialog v-model="showDiagram" width="800px" append-to-body class="no-padding-dialog">
<template #title>
<template #header>
<div style="display: flex; align-items: center;">
<span>Tool Diagram</span>
&ensp;
<el-button size="small" type="primary" @click="() => context.reset()">重置</el-button>
<el-button size="small" type="primary" @click="() => context.reset()">{{ t("reset") }}</el-button>
<!-- 自检程序弹出表单 -->
<el-popover placement="top" width="350" trigger="click" v-model:visible="testFormVisible">
<template #reference>
<el-button size="small" type="primary">
开启自检程序
{{ t('start-auto-detect') }}
</el-button>
</template>
@ -20,9 +20,9 @@
<span style="opacity: 0.7;">enableXmlWrapper</span>
</div>
<div style="text-align: right;">
<el-button size="small" @click="testFormVisible = false">取消</el-button>
<el-button size="small" @click="testFormVisible = false">{{ t("cancel") }}</el-button>
<el-button size="small" type="primary" @click="onTestConfirm">
确认
{{ t("confirm") }}
</el-button>
</div>
</el-popover>
@ -44,8 +44,10 @@ import { nextTick, provide, ref } from 'vue';
import Diagram from './diagram.vue';
import { makeNodeTest, topoSortParallel, type DiagramContext, type DiagramState } from './diagram';
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
const showDiagram = ref(true);
const { t } = useI18n();
const caption = ref('');
const showCaption = ref(false);

View File

@ -327,81 +327,85 @@ function renderSvg() {
.attr('font-weight', 600)
.text(d => d.labels?.[0]?.text || 'Tool');
//
nodeGroupEnter.append('g')
.attr('class', 'node-status')
.each(function (d) {
const status = state.dataView.get(d.id)?.status || 'waiting';
const g = d3.select(this);
if (status === 'running') {
// +
g.append('circle')
.attr('cx', d.width / 2 - 32)
.attr('cy', d.height - 16)
.attr('r', 6) //
.attr('fill', 'none')
.attr('stroke', 'var(--main-color)') // 使
.attr('stroke-width', 3)
.attr('stroke-dasharray', 20)
.attr('stroke-dashoffset', 0)
.append('animateTransform')
.attr('attributeName', 'transform')
.attr('attributeType', 'XML')
.attr('type', 'rotate')
.attr('from', `0 ${(d.width / 2 - 32)} ${(d.height - 16)}`)
.attr('to', `360 ${(d.width / 2 - 32)} ${(d.height - 16)}`)
.attr('dur', '1s')
.attr('repeatCount', 'indefinite');
g.append('text')
.attr('x', d.width / 2 - 16)
.attr('y', d.height - 12)
.attr('font-size', 13)
.attr('fill', 'var(--main-color)')
.text('running');
} else if (status === 'waiting') {
g.append('circle')
.attr('cx', d.width / 2 - 32)
.attr('cy', d.height - 16)
.attr('r', 6)
.attr('fill', 'none')
.attr('stroke', '#bdbdbd')
.attr('stroke-width', 3);
g.append('text')
.attr('x', d.width / 2 - 16)
.attr('y', d.height - 12)
.attr('font-size', 13)
.attr('fill', '#bdbdbd')
.text('waiting');
} else if (status === 'success') {
g.append('circle')
.attr('cx', d.width / 2 - 32)
.attr('cy', d.height - 16)
.attr('r', 6) // waiting
.attr('fill', 'none')
.attr('stroke', '#4caf50')
.attr('stroke-width', 3);
g.append('text')
.attr('x', d.width / 2 - 16)
.attr('y', d.height - 12)
.attr('font-size', 13)
.attr('fill', '#4caf50')
.text('success');
} else if (status === 'error') {
g.append('circle')
.attr('cx', d.width / 2 - 32)
.attr('cy', d.height - 16)
.attr('r', 6) // waiting
.attr('fill', 'none')
.attr('stroke', '#f44336')
.attr('stroke-width', 3);
g.append('text')
.attr('x', d.width / 2 - 16)
.attr('y', d.height - 12)
.attr('font-size', 13)
.attr('fill', '#f44336')
.text('error');
}
});
nodeGroupEnter.append('g').attr('class', 'node-status');
// enter+update
const nodeStatusGroup = nodeGroup.merge(nodeGroupEnter).select('.node-status');
//
nodeStatusGroup.each(function (d) {
const g = d3.select(this);
g.selectAll('*').remove(); //
const status = state.dataView.get(d.id)?.status || 'waiting';
if (status === 'running') {
g.append('circle')
.attr('cx', d.width / 2 - 32)
.attr('cy', d.height - 16)
.attr('r', 6)
.attr('fill', 'none')
.attr('stroke', 'var(--main-color)')
.attr('stroke-width', 3)
.attr('stroke-dasharray', 20)
.attr('stroke-dashoffset', 0)
.append('animateTransform')
.attr('attributeName', 'transform')
.attr('attributeType', 'XML')
.attr('type', 'rotate')
.attr('from', `0 ${(d.width / 2 - 32)} ${(d.height - 16)}`)
.attr('to', `360 ${(d.width / 2 - 32)} ${(d.height - 16)}`)
.attr('dur', '1s')
.attr('repeatCount', 'indefinite');
g.append('text')
.attr('x', d.width / 2 - 16)
.attr('y', d.height - 12)
.attr('font-size', 13)
.attr('fill', 'var(--main-color)')
.text('running');
} else if (status === 'waiting') {
g.append('circle')
.attr('cx', d.width / 2 - 32)
.attr('cy', d.height - 16)
.attr('r', 6)
.attr('fill', 'none')
.attr('stroke', '#bdbdbd')
.attr('stroke-width', 3);
g.append('text')
.attr('x', d.width / 2 - 16)
.attr('y', d.height - 12)
.attr('font-size', 13)
.attr('fill', '#bdbdbd')
.text('waiting');
} else if (status === 'success') {
g.append('circle')
.attr('cx', d.width / 2 - 32)
.attr('cy', d.height - 16)
.attr('r', 6)
.attr('fill', 'none')
.attr('stroke', '#4caf50')
.attr('stroke-width', 3);
g.append('text')
.attr('x', d.width / 2 - 16)
.attr('y', d.height - 12)
.attr('font-size', 13)
.attr('fill', '#4caf50')
.text('success');
} else if (status === 'error') {
g.append('circle')
.attr('cx', d.width / 2 - 32)
.attr('cy', d.height - 16)
.attr('r', 6)
.attr('fill', 'none')
.attr('stroke', '#f44336')
.attr('stroke-width', 3);
g.append('text')
.attr('x', d.width / 2 - 16)
.attr('y', d.height - 12)
.attr('font-size', 13)
.attr('fill', '#f44336')
.text('error');
}
});
// enter
nodeGroupEnter
.transition()

View File

@ -184,5 +184,6 @@
"export-filename": "اسم ملف التصدير",
"how-to-use": "كيفية الاستخدام؟",
"is-required": "هو حقل مطلوب",
"edit-ai-mook-prompt": "تحرير إشارات AI Mook"
"edit-ai-mook-prompt": "تحرير إشارات AI Mook",
"start-auto-detect": "بدء عملية الفحص الذاتي"
}

View File

@ -184,5 +184,6 @@
"export-filename": "Exportdateiname",
"how-to-use": "Wie benutzt man?",
"is-required": "ist ein Pflichtfeld",
"edit-ai-mook-prompt": "AI Mook-Prompts bearbeiten"
"edit-ai-mook-prompt": "AI Mook-Prompts bearbeiten",
"start-auto-detect": "Selbsttest starten"
}

View File

@ -184,5 +184,6 @@
"export-filename": "Export filename",
"how-to-use": "How to use?",
"is-required": "is a required field",
"edit-ai-mook-prompt": "Edit AI Mook prompts"
"edit-ai-mook-prompt": "Edit AI Mook prompts",
"start-auto-detect": "Start self-check"
}

View File

@ -184,5 +184,6 @@
"export-filename": "Nom du fichier d'exportation",
"how-to-use": "Comment utiliser ?",
"is-required": "est un champ obligatoire",
"edit-ai-mook-prompt": "Modifier les invites AI Mook"
"edit-ai-mook-prompt": "Modifier les invites AI Mook",
"start-auto-detect": "Démarrer l'autovérification"
}

View File

@ -184,5 +184,6 @@
"export-filename": "エクスポートファイル名",
"how-to-use": "使用方法",
"is-required": "は必須フィールドです",
"edit-ai-mook-prompt": "AI Mookプロンプトを編集"
"edit-ai-mook-prompt": "AI Mookプロンプトを編集",
"start-auto-detect": "自己診断を開始"
}

View File

@ -184,5 +184,6 @@
"export-filename": "내보내기 파일 이름",
"how-to-use": "사용 방법?",
"is-required": "는 필수 필드입니다",
"edit-ai-mook-prompt": "AI Mook 프롬프트 편집"
"edit-ai-mook-prompt": "AI Mook 프롬프트 편집",
"start-auto-detect": "자체 점검 시작"
}

View File

@ -184,5 +184,6 @@
"export-filename": "Имя файла экспорта",
"how-to-use": "Как использовать?",
"is-required": "является обязательным полем",
"edit-ai-mook-prompt": "Редактировать подсказки AI Mook"
"edit-ai-mook-prompt": "Редактировать подсказки AI Mook",
"start-auto-detect": "Запустить самопроверку"
}

View File

@ -184,5 +184,6 @@
"export-filename": "导出文件名",
"how-to-use": "如何使用?",
"is-required": "是必填字段",
"edit-ai-mook-prompt": "编辑 AI Mook 提示词"
"edit-ai-mook-prompt": "编辑 AI Mook 提示词",
"start-auto-detect": "开启自检程序"
}

View File

@ -184,5 +184,6 @@
"export-filename": "導出文件名",
"how-to-use": "如何使用?",
"is-required": "是必填欄位",
"edit-ai-mook-prompt": "編輯AI Mook提示詞"
"edit-ai-mook-prompt": "編輯AI Mook提示詞",
"start-auto-detect": "開啟自檢程序"
}