diff --git a/ui/src/assets/workflow/icon_image-generate.svg b/ui/src/assets/workflow/icon_image-generate.svg
new file mode 100644
index 00000000000..e59aa52c419
--- /dev/null
+++ b/ui/src/assets/workflow/icon_image-generate.svg
@@ -0,0 +1,5 @@
+
diff --git a/ui/src/assets/workflow/icon_image-understand.svg b/ui/src/assets/workflow/icon_image-understand.svg
new file mode 100644
index 00000000000..72126d84092
--- /dev/null
+++ b/ui/src/assets/workflow/icon_image-understand.svg
@@ -0,0 +1,4 @@
+
diff --git a/ui/src/assets/workflow/icon_image_to_video.svg b/ui/src/assets/workflow/icon_image_to_video.svg
new file mode 100644
index 00000000000..1cfa2a8c65f
--- /dev/null
+++ b/ui/src/assets/workflow/icon_image_to_video.svg
@@ -0,0 +1,5 @@
+
diff --git a/ui/src/assets/workflow/icon_question.svg b/ui/src/assets/workflow/icon_question.svg
new file mode 100644
index 00000000000..afa97360f1e
--- /dev/null
+++ b/ui/src/assets/workflow/icon_question.svg
@@ -0,0 +1,3 @@
+
diff --git a/ui/src/assets/workflow/icon_speech_to_text.svg b/ui/src/assets/workflow/icon_speech_to_text.svg
new file mode 100644
index 00000000000..4bc269ff007
--- /dev/null
+++ b/ui/src/assets/workflow/icon_speech_to_text.svg
@@ -0,0 +1,10 @@
+
diff --git a/ui/src/assets/workflow/icon_text_to_speech.svg b/ui/src/assets/workflow/icon_text_to_speech.svg
new file mode 100644
index 00000000000..05c5bfa15cd
--- /dev/null
+++ b/ui/src/assets/workflow/icon_text_to_speech.svg
@@ -0,0 +1,10 @@
+
diff --git a/ui/src/assets/workflow/icon_text_to_video.svg b/ui/src/assets/workflow/icon_text_to_video.svg
new file mode 100644
index 00000000000..2de496fd6c6
--- /dev/null
+++ b/ui/src/assets/workflow/icon_text_to_video.svg
@@ -0,0 +1,5 @@
+
diff --git a/ui/src/assets/workflow/icon_video-understand.svg b/ui/src/assets/workflow/icon_video-understand.svg
new file mode 100644
index 00000000000..14f32ae2b02
--- /dev/null
+++ b/ui/src/assets/workflow/icon_video-understand.svg
@@ -0,0 +1,3 @@
+
diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md
index b17e56c4fc2..7fab00daeef 100644
--- a/ui/src/components/COMPONENT_README.md
+++ b/ui/src/components/COMPONENT_README.md
@@ -206,7 +206,9 @@ Element Plus 使用 `ElOnlyChild` 处理浮层触发器。`el-tooltip`、`el-pop
基于 `md-editor-v3` 的全局自动注册组件。三个组件统一使用 User Store 中的当前语言,并为繁体中文
加载 `zh-TW` 语言包;底层组件的 Props 和事件通过 `$attrs` 透传。`MdEditor` 默认关闭 Prettier,
并透传 `defFooters` 插槽;`MdEditorMagnify` 在无工具栏编辑器的页脚提供放大按钮,弹窗中点击确定
-才会写回内容并触发 `submitDialog`,取消或关闭弹窗会丢弃本次弹窗编辑;`MdPreview` 默认关闭代码
+才会写回内容并触发 `submitDialog`,取消或关闭弹窗会丢弃本次弹窗编辑。组件位于配置了字段和规则的
+`el-form-item` 中时,会像 `el-input` 一样按 `change`、`blur` 触发规则校验;未配置字段或规则时跳过,
+传入 `:validate-event="false"` 可关闭自动校验。`MdPreview` 默认关闭代码
折叠。Admin 和 Chat 入口统一调用
`configureMarkdownEditor()`,将代码高亮、全屏、KaTeX、图片裁剪、Mermaid、ECharts 和 Prettier
配置为本地实例,避免运行时加载 CDN 资源;`md-editor-v3` v6 的图标已随依赖打包,不再维护或加载
diff --git a/ui/src/components/global/markdown-editor/MdEditorMagnify.vue b/ui/src/components/global/markdown-editor/MdEditorMagnify.vue
index 6b8ec2ae68a..cc932bf0402 100644
--- a/ui/src/components/global/markdown-editor/MdEditorMagnify.vue
+++ b/ui/src/components/global/markdown-editor/MdEditorMagnify.vue
@@ -1,18 +1,28 @@
-
+
diff --git a/ui/src/components/global/markdown-editor/md-editor.scss b/ui/src/components/global/markdown-editor/md-editor.scss
index a4279f38df6..d79d0b095c5 100644
--- a/ui/src/components/global/markdown-editor/md-editor.scss
+++ b/ui/src/components/global/markdown-editor/md-editor.scss
@@ -9,7 +9,7 @@
font-weight: 400;
line-height: 22px;
font-family: var(--mk-font-family) !important;
- height: 150px;
+ height: 130px;
.cm-content {
color: var(--mk-N900);
diff --git a/ui/src/styles/tailwind.css b/ui/src/styles/tailwind.css
index 95ed1e014f1..57c10aa1d6d 100644
--- a/ui/src/styles/tailwind.css
+++ b/ui/src/styles/tailwind.css
@@ -7,6 +7,7 @@
--color-success: var(--mk-success);
--color-warning: var(--mk-warning);
--color-purple: var(--mk-purple);
+ --color-blue: var(--mk-blue);
--color-N100: var(--mk-N100);
--color-N300: var(--mk-N300);
--color-N350: var(--mk-N350);
diff --git a/ui/src/styles/variables.scss b/ui/src/styles/variables.scss
index bf2f7de578d..31637bc82b1 100644
--- a/ui/src/styles/variables.scss
+++ b/ui/src/styles/variables.scss
@@ -18,6 +18,7 @@
// );
--mk-purple: #7f3bf5;
+ --mk-blue: #14c0ff;
// 用于背景底色。
--mk-N100: #f5f6f7;
--mk-N100-rgb: 245 246 247;
diff --git a/ui/src/views/workflow/application/ApplicationWorkflowView.vue b/ui/src/views/workflow/application/ApplicationWorkflowView.vue
index d56d52cf0da..253a1a85942 100644
--- a/ui/src/views/workflow/application/ApplicationWorkflowView.vue
+++ b/ui/src/views/workflow/application/ApplicationWorkflowView.vue
@@ -140,56 +140,22 @@ function loadApplicationDetail() {
}
/* 发布工作流 */
-// const canPublish = computed(() => perm.application.workspace.publish(applicationId))
-// function getValidationMessage(error: unknown) {
-// if (typeof error === 'string') return error
-// if (error instanceof Error) return error.message
-// if (!error || typeof error !== 'object') return '工作流校验失败'
-
-// const validationError = error as {
-// errMessage?: unknown
-// node?: { properties?: { stepName?: string } }
-// }
-// const stepName = validationError.node?.properties?.stepName
-// const message =
-// typeof validationError.errMessage === 'string' ? validationError.errMessage : '工作流配置不完整'
-
-// return stepName ? `${stepName} 节点,${message}` : message
-// }
-
-// function validateWorkflow() {
-// const graphData = getGraphData()
-// if (!graphData) return Promise.resolve(undefined)
-
-// return (workflowRef.value?.validate() ?? Promise.resolve([]))
-// .then(() => {
-// type ValidationGraph = ConstructorParameters[0]
-// new WorkFlowInstance(graphData as unknown as ValidationGraph).is_valid()
-// return graphData
-// })
-// .catch((error: unknown) => {
-// MsgError(getValidationMessage(error))
-// return undefined
-// })
-// }
-// function handlePublish() {
-// validateWorkflow().then((graphData) => {
-// if (!graphData) return
-
-// publishing.value = true
-// return saveApplication(graphData)
-// .then(() => ApplicationApi.putApplicationPublish(applicationId))
-// .then((application) => {
-// applicationDetail.value = application
-// saveTime.value = application.update_time || saveTime.value
-// MsgSuccess('发布成功')
-// })
-// .finally(() => {
-// publishing.value = false
-// })
-// })
-// }
+function handlePublish() {
+ workflowRef.value?.validate().then(() => {
+ // publishing.value = true
+ // return saveApplication(graphData)
+ // .then(() => ApplicationApi.putApplicationPublish(applicationId))
+ // .then((application) => {
+ // applicationDetail.value = application
+ // saveTime.value = application.update_time || saveTime.value
+ // MsgSuccess('发布成功')
+ // })
+ // .finally(() => {
+ // publishing.value = false
+ // })
+ })
+}
/* 自动保存 */
// const AUTO_SAVE_INTERVAL = 60_000
@@ -274,15 +240,9 @@ onMounted(() => {
/>
保存
调试
-
+
+
+
+
+ 系统提示词
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+ 用户提示词
+
+
+
+
+
+
+
+
+
+
+
+
+ 历史聊天记录
+
+
+
+
+
+
+
+
+
+
+
+ 视觉
+
+
+
+
+
+
+ 选择图片
+
+
+
+ {{ fileTooltip }}
+
+
+
+
+
+
+
-
+
+
+
+ 选择视频
+
+
+
+ {{ fileTooltip }}
+
+
+
+
+
+
+
+
-
+
-
+
+
-
+
+
+
+ 返回内容
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/workflow-canvas/nodes/base-node/component/file-upload/index.vue b/ui/src/workflow-canvas/nodes/base-node/component/file-upload/index.vue
index 6edfebf2787..0e697f299b7 100644
--- a/ui/src/workflow-canvas/nodes/base-node/component/file-upload/index.vue
+++ b/ui/src/workflow-canvas/nodes/base-node/component/file-upload/index.vue
@@ -27,7 +27,7 @@ function changeEnabled(value: boolean | number | string) {
文件上传
-
+
diff --git a/ui/src/workflow-canvas/nodes/base-node/component/long-term-memory/index.vue b/ui/src/workflow-canvas/nodes/base-node/component/long-term-memory/index.vue
index 9ca31ac783b..d70bc3aed39 100644
--- a/ui/src/workflow-canvas/nodes/base-node/component/long-term-memory/index.vue
+++ b/ui/src/workflow-canvas/nodes/base-node/component/long-term-memory/index.vue
@@ -36,7 +36,7 @@ function openSettings() {
content="开启后,从开启时间记录新对话并按周期生成记忆,可通过 {{开始.memory}} 变量在系统提示词中调用。关闭后,将清空对话用户的长期记忆,再次开启将重新从开启时点开始累积。"
placement="right"
>
-
+
diff --git a/ui/src/workflow-canvas/nodes/image-generate-node/index.vue b/ui/src/workflow-canvas/nodes/image-generate-node/index.vue
index ddcc6946b7c..3f0f4da6812 100644
--- a/ui/src/workflow-canvas/nodes/image-generate-node/index.vue
+++ b/ui/src/workflow-canvas/nodes/image-generate-node/index.vue
@@ -1,26 +1,30 @@
+
+
+
+ 节点设置
+
+
+
+
+
+ 图生视频模型
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提示词(正向)
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提示词(负向)
+
+
+
+
+
+
+
+
+
+
+
+
+ 首帧图片
+
+
+ {{ fileTooltip }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 尾帧图片
+
+
+ {{ fileTooltip }}
+
+
+
+
+
+
+
+
+
+
+
+ 返回内容
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/workflow-canvas/nodes/image-understand-node/index.vue b/ui/src/workflow-canvas/nodes/image-understand-node/index.vue
index 8438cb71676..2f51b532e7e 100644
--- a/ui/src/workflow-canvas/nodes/image-understand-node/index.vue
+++ b/ui/src/workflow-canvas/nodes/image-understand-node/index.vue
@@ -1,31 +1,41 @@
diff --git a/ui/src/workflow-canvas/nodes/tool-lib-node/index.vue b/ui/src/workflow-canvas/nodes/tool-lib-node/index.vue
index 8ea1419fcb8..1800ab3c6d0 100644
--- a/ui/src/workflow-canvas/nodes/tool-lib-node/index.vue
+++ b/ui/src/workflow-canvas/nodes/tool-lib-node/index.vue
@@ -30,10 +30,12 @@ const model = getModel()
const formRef = useTemplateRef('formRef')
-const toolLibNodeData = (model.properties.node_data ?? {}) as Partial
-const shouldInitializeResult = toolLibNodeData.is_result === undefined
-if (!Array.isArray(toolLibNodeData.input_field_list)) toolLibNodeData.input_field_list = []
-model.properties.node_data = toolLibNodeData as ToolLibNodeForm
+const savedForm = model.properties.node_data as Partial | undefined
+model.properties.node_data = {
+ ...savedForm,
+ input_field_list: Array.isArray(savedForm?.input_field_list) ? savedForm.input_field_list : [],
+ is_result: savedForm ? savedForm.is_result : false,
+}
const formData = computed({
get: () => model.properties.node_data as ToolLibNodeForm,
@@ -84,7 +86,7 @@ function refreshToolFields() {
}
onMounted(() => {
- if (shouldInitializeResult) formData.value.is_result = isLastNode(model)
+ if (formData.value.is_result === undefined && isLastNode(model)) formData.value.is_result = true
refreshToolFields()
model.validate = validate
})
diff --git a/ui/src/workflow-canvas/nodes/video-understand-node/index.ts b/ui/src/workflow-canvas/nodes/video-understand-node/index.ts
new file mode 100644
index 00000000000..d386e05204c
--- /dev/null
+++ b/ui/src/workflow-canvas/nodes/video-understand-node/index.ts
@@ -0,0 +1,15 @@
+import VideoUnderstandNodeVue from './index.vue'
+import { WorkflowNodeModel, WorkflowNodeView } from '@/workflow-canvas/core/workflow-node'
+import { WorkflowNodeType } from '@/workflow-canvas/types.ts'
+
+class VideoUnderstandNodeView extends WorkflowNodeView {
+ constructor(props: ConstructorParameters[0]) {
+ super(props, VideoUnderstandNodeVue)
+ }
+}
+
+export default {
+ type: WorkflowNodeType.VideoUnderstandNode,
+ model: WorkflowNodeModel,
+ view: VideoUnderstandNodeView,
+}
diff --git a/ui/src/workflow-canvas/nodes/video-understand-node/index.vue b/ui/src/workflow-canvas/nodes/video-understand-node/index.vue
new file mode 100644
index 00000000000..abaf52fb70c
--- /dev/null
+++ b/ui/src/workflow-canvas/nodes/video-understand-node/index.vue
@@ -0,0 +1,302 @@
+
+
+
+
+ 节点设置
+
+
+
+
+
+ 视觉模型
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 系统提示词
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 用户提示词
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 历史聊天记录
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 选择视频
+
+
+
+ {{ fileTooltip }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 返回内容
+
+
+
+
+
+
+
+
+
+
+
+