mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: knowledge workflow
This commit is contained in:
parent
107fc44049
commit
b886d8d458
|
|
@ -0,0 +1,8 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: __init__.py.py
|
||||
@date:2025/11/11 10:06
|
||||
@desc:
|
||||
"""
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: i_data_source_local_node.py
|
||||
@date:2025/11/11 10:06
|
||||
@desc:
|
||||
"""
|
||||
from abc import abstractmethod
|
||||
from typing import Type
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from application.flow.i_step_node import INode, NodeResult
|
||||
|
||||
|
||||
class DataSourceLocalNodeParamsSerializer(serializers.Serializer):
|
||||
file_format = serializers.ListField(child=serializers.CharField)
|
||||
max_file_number = serializers.IntegerField(required=True, label=_("Number of uploaded files"))
|
||||
file_max_size = serializers.IntegerField(required=True, label=_("Upload file size"))
|
||||
|
||||
|
||||
class IDataSourceLocalNode(INode):
|
||||
type = 'data-source-local-node'
|
||||
|
||||
@abstractmethod
|
||||
def get_form_class(self):
|
||||
pass
|
||||
|
||||
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
|
||||
return DataSourceLocalNodeParamsSerializer
|
||||
|
||||
def _run(self):
|
||||
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
|
||||
|
||||
def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult:
|
||||
pass
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: __init__.py.py
|
||||
@date:2025/11/11 10:08
|
||||
@desc:
|
||||
"""
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: base_data_source_local_node.py
|
||||
@date:2025/11/11 10:30
|
||||
@desc:
|
||||
"""
|
||||
from application.flow.i_step_node import NodeResult
|
||||
from application.flow.step_node.data_source_local_node.i_data_source_local_node import IDataSourceLocalNode
|
||||
from common import forms
|
||||
from common.forms import BaseForm
|
||||
|
||||
|
||||
class BaseDataSourceLocalNodeForm(BaseForm):
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
|
||||
class BaseDataSourceLocalNode(IDataSourceLocalNode):
|
||||
def save_context(self, details, workflow_manage):
|
||||
pass
|
||||
|
||||
def get_form_class(self):
|
||||
return BaseDataSourceLocalNodeForm()
|
||||
|
||||
def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult:
|
||||
pass
|
||||
|
|
@ -8,11 +8,13 @@ from django.db.models import QuerySet
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from application.flow.step_node import get_node
|
||||
from common.exception.app_exception import AppApiException
|
||||
from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow
|
||||
from knowledge.serializers.knowledge import KnowledgeModelSerializer
|
||||
from system_manage.models import AuthTargetType
|
||||
from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer
|
||||
from tools.models import Tool
|
||||
|
||||
|
||||
class KnowledgeWorkflowModelSerializer(serializers.ModelSerializer):
|
||||
|
|
@ -22,6 +24,20 @@ class KnowledgeWorkflowModelSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class KnowledgeWorkflowSerializer(serializers.Serializer):
|
||||
class Form(serializers.Serializer):
|
||||
type = serializers.CharField(required=True, label=_('type'))
|
||||
id = serializers.CharField(required=True, label=_('type'))
|
||||
|
||||
def get_form_list(self):
|
||||
self.is_valid(raise_exception=True)
|
||||
if self.data.get('type') == 'local':
|
||||
node = get_node(self.data.get('id'))
|
||||
return node.get_form_class()().to_form_list()
|
||||
elif self.data.get('type') == 'tool':
|
||||
tool = QuerySet(Tool).filter(id=self.data.get("id")).first()
|
||||
# todo 调用工具数据源的函数获取表单列表
|
||||
return None
|
||||
|
||||
class Create(serializers.Serializer):
|
||||
user_id = serializers.UUIDField(required=True, label=_('user id'))
|
||||
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
|
||||
|
|
|
|||
|
|
@ -12,10 +12,16 @@ from common.log.log import log
|
|||
from common.result import result
|
||||
from knowledge.api.knowledge_workflow import KnowledgeWorkflowApi
|
||||
from knowledge.serializers.common import get_knowledge_operation_object
|
||||
from knowledge.serializers.knowledge import KnowledgeSerializer
|
||||
from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer
|
||||
|
||||
|
||||
class KnowledgeWorkflowFormView(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
def get(self):
|
||||
return result.success(KnowledgeWorkflowSerializer.Form().get_form_list())
|
||||
|
||||
|
||||
class KnowledgeWorkflowView(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ export enum WorkflowType {
|
|||
VariableAggregationNode = 'variable-aggregation-node',
|
||||
VideoUnderstandNode = 'video-understand-node',
|
||||
ParameterExtractionNode = 'parameter-extraction-node',
|
||||
DataSourceLocalNode = 'data-source-local-node',
|
||||
}
|
||||
export enum WorkflowKind {
|
||||
DataSource = 'data-source',
|
||||
}
|
||||
export enum WorkflowMode {
|
||||
// 应用工作流
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<template>
|
||||
<div v-show="show" class="workflow-dropdown-menu border border-r-6 white-bg" :style="{ width: activeName === 'base' ? '400px':'640px' }">
|
||||
<div
|
||||
v-show="show"
|
||||
class="workflow-dropdown-menu border border-r-6 white-bg"
|
||||
:style="{ width: activeName === 'base' ? '400px' : '640px' }"
|
||||
>
|
||||
<el-tabs v-model="activeName" class="workflow-dropdown-tabs" @tab-change="handleClick">
|
||||
<div
|
||||
v-show="activeName === 'base'"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
const props = defineProps<{
|
||||
workflow: any
|
||||
}>()
|
||||
const source_node_list = computed(() => {})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -301,7 +301,7 @@ const publish = () => {
|
|||
?.validate()
|
||||
.then(() => {
|
||||
const workflow = getGraphData()
|
||||
const workflowInstance = new WorkFlowInstance(workflow)
|
||||
const workflowInstance = new WorkFlowInstance(workflow, WorkflowMode.Knowledge)
|
||||
try {
|
||||
workflowInstance.is_valid()
|
||||
} catch (e: any) {
|
||||
|
|
@ -384,7 +384,7 @@ const clickShowDebug = () => {
|
|||
?.validate()
|
||||
.then(() => {
|
||||
const graphData = getGraphData()
|
||||
const workflow = new WorkFlowInstance(graphData)
|
||||
const workflow = new WorkFlowInstance(graphData, WorkflowMode.Knowledge)
|
||||
try {
|
||||
workflow.is_valid()
|
||||
detail.value = {
|
||||
|
|
@ -396,6 +396,7 @@ const clickShowDebug = () => {
|
|||
|
||||
showDebug.value = true
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
MsgError(e.toString())
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { WorkflowKind } from './../../enums/application'
|
||||
import Components from '@/components'
|
||||
import ElementPlus from 'element-plus'
|
||||
import * as ElementPlusIcons from '@element-plus/icons-vue'
|
||||
|
|
@ -414,9 +415,11 @@ class AppNodeModel extends HtmlResize.model {
|
|||
const { id, x, y, width } = this
|
||||
const showNode = this.properties.showNode === undefined ? true : this.properties.showNode
|
||||
const anchors: any = []
|
||||
|
||||
if (![WorkflowType.Base as string, WorkflowType.KnowledgeBase as string].includes(this.type)) {
|
||||
if (![WorkflowType.Start, WorkflowType.LoopStartNode.toString()].includes(this.type)) {
|
||||
if (
|
||||
![WorkflowType.Start, WorkflowType.LoopStartNode.toString()].includes(this.type) &&
|
||||
this.properties.kind != WorkflowKind.DataSource
|
||||
) {
|
||||
anchors.push({
|
||||
x: x - width / 2 + 10,
|
||||
y: showNode ? y : y - 15,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { WorkflowKind } from './../../enums/application'
|
||||
import { WorkflowType, WorkflowMode } from '@/enums/application'
|
||||
import { t } from '@/locales'
|
||||
|
||||
|
|
@ -79,6 +80,25 @@ export const knowledgeBaseNode = {
|
|||
user_input_field_list: [],
|
||||
},
|
||||
}
|
||||
export const dataSourceLocalNode = {
|
||||
id: WorkflowType.DataSourceLocalNode,
|
||||
type: WorkflowType.DataSourceLocalNode,
|
||||
x: 360,
|
||||
y: 2761.3875,
|
||||
text: t('views.applicationWorkflow.nodes.dataSourceLocalNode.text', '本地文件'),
|
||||
label: t('views.applicationWorkflow.nodes.dataSourceLocalNode.label', '本地文件'),
|
||||
properties: {
|
||||
kind: WorkflowKind.DataSource,
|
||||
height: 728.375,
|
||||
stepName: t('views.applicationWorkflow.nodes.dataSourceLocalNode.label', '本地文件'),
|
||||
input_field_list: [],
|
||||
node_data: {},
|
||||
config: {},
|
||||
showNode: true,
|
||||
user_input_config: {},
|
||||
user_input_field_list: [],
|
||||
},
|
||||
}
|
||||
/**
|
||||
* 说明
|
||||
* type 与 nodes 文件对应
|
||||
|
|
@ -641,6 +661,10 @@ export const loopBreakNode = {
|
|||
}
|
||||
|
||||
export const knowledgeMenuNodes = [
|
||||
{
|
||||
label: t('views.applicationWorkflow.nodes.classify.dataSource', '数据源'),
|
||||
list: [dataSourceLocalNode],
|
||||
},
|
||||
{
|
||||
label: t('views.applicationWorkflow.nodes.classify.aiCapability'),
|
||||
list: [
|
||||
|
|
@ -868,7 +892,6 @@ export const compareList = [
|
|||
{ value: 'start_with', label: 'startWith' },
|
||||
{ value: 'end_with', label: 'endWith' },
|
||||
]
|
||||
|
||||
export const nodeDict: any = {
|
||||
[WorkflowType.AiChat]: aiChatNode,
|
||||
[WorkflowType.SearchKnowledge]: searchKnowledgeNode,
|
||||
|
|
@ -903,6 +926,7 @@ export const nodeDict: any = {
|
|||
[WorkflowType.ParameterExtractionNode]: parameterExtractionNode,
|
||||
[WorkflowType.VariableAggregationNode]: variableAggregationNode,
|
||||
[WorkflowType.KnowledgeBase]: knowledgeBaseNode,
|
||||
[WorkflowType.DataSourceLocalNode]: dataSourceLocalNode,
|
||||
}
|
||||
|
||||
export function isWorkFlow(type: string | undefined) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { WorkflowKind } from './../../enums/application'
|
||||
import { WorkflowType, WorkflowMode } from '@/enums/application'
|
||||
|
||||
import { t } from '@/locales'
|
||||
|
|
@ -43,7 +44,9 @@ const loop_end_nodes: Array<string> = [
|
|||
]
|
||||
const end_nodes_dict = {
|
||||
[WorkflowMode.Application]: end_nodes,
|
||||
[WorkflowMode.Knowledge]: end_nodes,
|
||||
[WorkflowMode.ApplicationLoop]: loop_end_nodes,
|
||||
[WorkflowMode.KnowledgeLoop]: loop_end_nodes,
|
||||
}
|
||||
|
||||
export class WorkFlowInstance {
|
||||
|
|
@ -63,8 +66,10 @@ export class WorkFlowInstance {
|
|||
* 校验开始节点
|
||||
*/
|
||||
private is_valid_start_node() {
|
||||
const start_node_list = this.nodes.filter((item) =>
|
||||
[WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id),
|
||||
const start_node_list = this.nodes.filter(
|
||||
(item) =>
|
||||
[WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) ||
|
||||
item.properties.kind == WorkflowKind.DataSource,
|
||||
)
|
||||
if (start_node_list.length == 0) {
|
||||
throw t('views.applicationWorkflow.validate.startNodeRequired')
|
||||
|
|
@ -77,6 +82,10 @@ export class WorkFlowInstance {
|
|||
* 校验基本信息节点
|
||||
*/
|
||||
private is_valid_base_node() {
|
||||
console.log(this.workflowModel)
|
||||
if (this.workflowModel == WorkflowMode.Knowledge) {
|
||||
return
|
||||
}
|
||||
const start_node_list = this.nodes.filter((item) => item.id === WorkflowType.Base)
|
||||
if (start_node_list.length == 0) {
|
||||
throw t('views.applicationWorkflow.validate.baseNodeRequired')
|
||||
|
|
@ -106,8 +115,10 @@ export class WorkFlowInstance {
|
|||
* @returns
|
||||
*/
|
||||
get_start_node() {
|
||||
const start_node_list = this.nodes.filter((item) =>
|
||||
[WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id),
|
||||
const start_node_list = this.nodes.filter(
|
||||
(item) =>
|
||||
[WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) ||
|
||||
item.properties.kind == WorkflowKind.DataSource,
|
||||
)
|
||||
return start_node_list[0]
|
||||
}
|
||||
|
|
@ -143,9 +154,26 @@ export class WorkFlowInstance {
|
|||
|
||||
private is_valid_work_flow() {
|
||||
this.workFlowNodes = []
|
||||
this._is_valid_work_flow()
|
||||
if (this.workflowModel == WorkflowMode.Knowledge) {
|
||||
const start_node_list = this.nodes.filter(
|
||||
(item) =>
|
||||
[WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) ||
|
||||
item.properties.kind == WorkflowKind.DataSource,
|
||||
)
|
||||
start_node_list.forEach((startNode) => {
|
||||
this._is_valid_work_flow(startNode)
|
||||
})
|
||||
} else {
|
||||
this._is_valid_work_flow()
|
||||
}
|
||||
|
||||
const notInWorkFlowNodes = this.nodes
|
||||
.filter((node: any) => node.id !== WorkflowType.Start && node.id !== WorkflowType.Base)
|
||||
.filter(
|
||||
(node: any) =>
|
||||
node.id !== WorkflowType.Start &&
|
||||
node.id !== WorkflowType.Base &&
|
||||
node.id !== WorkflowType.KnowledgeBase,
|
||||
)
|
||||
.filter((node) => !this.workFlowNodes.includes(node))
|
||||
if (notInWorkFlowNodes.length > 0) {
|
||||
throw `${t('views.applicationWorkflow.validate.notInWorkFlowNode')}:${notInWorkFlowNodes.map((node) => node.properties.stepName).join(',')}`
|
||||
|
|
@ -175,7 +203,9 @@ export class WorkFlowInstance {
|
|||
if (
|
||||
node.type !== WorkflowType.Base &&
|
||||
node.type !== WorkflowType.Start &&
|
||||
node.type !== WorkflowType.LoopStartNode
|
||||
node.type !== WorkflowType.LoopStartNode &&
|
||||
node.type !== WorkflowType.KnowledgeBase &&
|
||||
node.properties.kind !== WorkflowKind.DataSource
|
||||
) {
|
||||
if (!this.edges.some((edge) => edge.targetNodeId === node.id)) {
|
||||
throw `${t('views.applicationWorkflow.validate.notInWorkFlowNode')}:${node.properties.stepName}`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<el-avatar shape="square" style="background: #14c0ff">
|
||||
<img src="@/assets/workflow/icon_condition.svg" style="width: 75%" alt="" />
|
||||
</el-avatar>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import DataSourceWebNodeVue from './index.vue'
|
||||
import { AppNode, AppNodeModel } from '@/workflow/common/app-node'
|
||||
class DataSourceWebNode extends AppNode {
|
||||
constructor(props: any) {
|
||||
super(props, DataSourceWebNodeVue)
|
||||
}
|
||||
}
|
||||
export default {
|
||||
type: 'data-source-local-node',
|
||||
model: AppNodeModel,
|
||||
view: DataSourceWebNode,
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
<template>
|
||||
<NodeContainer :nodeModel="nodeModel">
|
||||
<h5 class="title-decoration-1 mb-8">{{ $t('views.applicationWorkflow.nodeSetting') }}</h5>
|
||||
<el-card shadow="never" class="card-never">
|
||||
<el-form
|
||||
@submit.prevent
|
||||
:model="form_data"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item
|
||||
:label="
|
||||
$t(
|
||||
'views.applicationWorkflow.nodes.dataSourceLocalNode.fileFormat.label',
|
||||
'支持的文件格式',
|
||||
)
|
||||
"
|
||||
:rules="{
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: $t(
|
||||
'views.applicationWorkflow.nodes.dataSourceLocalNode.fileFormat.message',
|
||||
'请选择文件格式',
|
||||
),
|
||||
trigger: 'change',
|
||||
}"
|
||||
>
|
||||
<el-select
|
||||
v-model="form_data.file_format"
|
||||
:placeholder="
|
||||
$t(
|
||||
'views.applicationWorkflow.nodes.dataSourceLocalNode.fileFormat.placeholder',
|
||||
'请选择文件格式',
|
||||
)
|
||||
"
|
||||
style="width: 240px"
|
||||
clearable
|
||||
multiple
|
||||
>
|
||||
<template #label="{ label, value }">
|
||||
<span>{{ label }} </span>
|
||||
</template>
|
||||
<el-option
|
||||
v-for="item in file_format_list"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="
|
||||
$t(
|
||||
'views.applicationWorkflow.nodes.dataSourceLocalNode.maxFileNumber.label',
|
||||
'每次上传最大文件数',
|
||||
)
|
||||
"
|
||||
:rules="{
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: $t(
|
||||
'views.applicationWorkflow.nodes.dataSourceLocalNode.maxFileNumber.placeholder',
|
||||
'请输入最大文件数',
|
||||
),
|
||||
trigger: 'change',
|
||||
}"
|
||||
>
|
||||
<el-slider v-model="form_data.max_file_number" show-input />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="
|
||||
$t(
|
||||
'views.applicationWorkflow.nodes.dataSourceLocalNode.maxFileNumber.label',
|
||||
'上传的每个文档最大(MB)',
|
||||
)
|
||||
"
|
||||
:rules="{
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: $t(
|
||||
'views.applicationWorkflow.nodes.dataSourceLocalNode.maxFileNumber.placeholder',
|
||||
'上传的每个文档最大(MB) 必填',
|
||||
),
|
||||
trigger: 'change',
|
||||
}"
|
||||
>
|
||||
<el-slider v-model="form_data.file_max_size" show-input />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</NodeContainer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import { computed } from 'vue'
|
||||
import { set } from 'lodash'
|
||||
|
||||
const props = defineProps<{ nodeModel: any }>()
|
||||
const file_format_list = [
|
||||
{ label: 'TXT', value: '.txt' },
|
||||
{ label: 'DOCX', value: '.docx' },
|
||||
{ label: 'PDF', value: '.pdf' },
|
||||
{ label: 'HTML', value: '.html' },
|
||||
{ label: 'XLS', value: '.xls' },
|
||||
{ label: 'XLSX', value: '.xlsx' },
|
||||
{ label: 'ZIP', value: '.zip' },
|
||||
{ label: 'CSV', value: '.csv' },
|
||||
]
|
||||
const form = {
|
||||
file_format: [],
|
||||
max_file_number: 50,
|
||||
file_max_size: 100,
|
||||
}
|
||||
|
||||
const form_data = computed({
|
||||
get: () => {
|
||||
if (props.nodeModel.properties.node_data) {
|
||||
return props.nodeModel.properties.node_data
|
||||
} else {
|
||||
set(props.nodeModel.properties, 'node_data', form)
|
||||
}
|
||||
return props.nodeModel.properties.node_data
|
||||
},
|
||||
set: (value) => {
|
||||
set(props.nodeModel.properties, 'node_data', value)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import DataSourceWebNodeVue from './index.vue'
|
||||
import { AppNode, AppNodeModel } from '@/workflow/common/app-node'
|
||||
class DataSourceWebNode extends AppNode {
|
||||
constructor(props: any) {
|
||||
super(props, DataSourceWebNodeVue)
|
||||
}
|
||||
}
|
||||
export default {
|
||||
type: 'data-source-web-node',
|
||||
model: AppNodeModel,
|
||||
view: DataSourceWebNode,
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<NodeContainer :nodeModel="nodeModel">
|
||||
<h5 class="title-decoration-1 mb-8">{{ $t('views.applicationWorkflow.nodeSetting') }}</h5>
|
||||
<el-card shadow="never" class="card-never">
|
||||
<el-form
|
||||
@submit.prevent
|
||||
:model="form_data"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item
|
||||
:label="$t('views.problem.relateParagraph.selectDocument')"
|
||||
:rules="{
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: $t('views.chatLog.documentPlaceholder'),
|
||||
trigger: 'change',
|
||||
}"
|
||||
>
|
||||
<NodeCascader
|
||||
ref="nodeCascaderRef"
|
||||
:nodeModel="nodeModel"
|
||||
class="w-full"
|
||||
:placeholder="$t('views.chatLog.documentPlaceholder')"
|
||||
v-model="form_data.document_list"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</NodeContainer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import { computed } from 'vue'
|
||||
import { set } from 'lodash'
|
||||
import NodeCascader from '@/workflow/common/NodeCascader.vue'
|
||||
|
||||
const props = defineProps<{ nodeModel: any }>()
|
||||
|
||||
const form = {
|
||||
document_list: ['start-node', 'document'],
|
||||
}
|
||||
|
||||
const form_data = computed({
|
||||
get: () => {
|
||||
if (props.nodeModel.properties.node_data) {
|
||||
return props.nodeModel.properties.node_data
|
||||
} else {
|
||||
set(props.nodeModel.properties, 'node_data', form)
|
||||
}
|
||||
return props.nodeModel.properties.node_data
|
||||
},
|
||||
set: (value) => {
|
||||
set(props.nodeModel.properties, 'node_data', value)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
Loading…
Reference in New Issue