diff --git a/README.md b/README.md index 93bc7ae81..372803d56 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,8 @@ docker run -d --name=maxkb --restart=always -p 8080:8080 -v C:/maxkb:/var/lib/po - 你也可以通过 [1Panel 应用商店](https://apps.fit2cloud.com/1panel) 快速部署 MaxKB; - 如果是内网环境,推荐使用 [离线安装包](https://community.fit2cloud.com/#/products/maxkb/downloads) 进行安装部署; -- MaxKB 产品版本分为社区版和专业版,详情请参见:[MaxKB 产品版本对比](https://maxkb.cn/pricing.html)。 +- MaxKB 产品版本分为社区版和专业版,详情请参见:[MaxKB 产品版本对比](https://maxkb.cn/pricing.html); +- 如果您需要向团队介绍 MaxKB,可以使用这个 [官方 PPT 材料](https://maxkb.cn/download/introduce-maxkb_202411.pdf)。 如你有更多问题,可以查看使用手册,或者通过论坛与我们交流。 diff --git a/apps/application/flow/i_step_node.py b/apps/application/flow/i_step_node.py index 3d30758a2..360c46bb5 100644 --- a/apps/application/flow/i_step_node.py +++ b/apps/application/flow/i_step_node.py @@ -32,7 +32,7 @@ def write_context(step_variable: Dict, global_variable: Dict, node, workflow): if workflow.is_result(node, NodeResult(step_variable, global_variable)) and 'answer' in step_variable: answer = step_variable['answer'] yield answer - workflow.append_answer(answer) + node.answer_text = answer if global_variable is not None: for key in global_variable: workflow.context[key] = global_variable[key] diff --git a/apps/application/flow/step_node/document_extract_node/i_document_extract_node.py b/apps/application/flow/step_node/document_extract_node/i_document_extract_node.py index d030e0078..ceda39444 100644 --- a/apps/application/flow/step_node/document_extract_node/i_document_extract_node.py +++ b/apps/application/flow/step_node/document_extract_node/i_document_extract_node.py @@ -9,12 +9,7 @@ from common.util.field_message import ErrMessage class DocumentExtractNodeSerializer(serializers.Serializer): - # 需要查询的数据集id列表 - file_list = serializers.ListField(required=True, child=serializers.UUIDField(required=True), - error_messages=ErrMessage.list("数据集id列表")) - - def is_valid(self, *, raise_exception=False): - super().is_valid(raise_exception=True) + document_list = serializers.ListField(required=False, error_messages=ErrMessage.list("文档")) class IDocumentExtractNode(INode): @@ -24,7 +19,9 @@ class IDocumentExtractNode(INode): return DocumentExtractNodeSerializer def _run(self): - return self.execute(**self.flow_params_serializer.data) + res = self.workflow_manage.get_reference_field(self.node_params_serializer.data.get('document_list')[0], + self.node_params_serializer.data.get('document_list')[1:]) + return self.execute(document=res, **self.flow_params_serializer.data) - def execute(self, file_list, **kwargs) -> NodeResult: + def execute(self, document, **kwargs) -> NodeResult: pass diff --git a/apps/application/flow/step_node/document_extract_node/impl/base_document_extract_node.py b/apps/application/flow/step_node/document_extract_node/impl/base_document_extract_node.py index bf900ffe2..176230d2d 100644 --- a/apps/application/flow/step_node/document_extract_node/impl/base_document_extract_node.py +++ b/apps/application/flow/step_node/document_extract_node/impl/base_document_extract_node.py @@ -1,11 +1,34 @@ # coding=utf-8 +from django.db.models import QuerySet +from application.flow.i_step_node import NodeResult from application.flow.step_node.document_extract_node.i_document_extract_node import IDocumentExtractNode +from dataset.models import File class BaseDocumentExtractNode(IDocumentExtractNode): - def execute(self, file_list, **kwargs): - pass + def execute(self, document, **kwargs): + self.context['document_list'] = document + content = '' + spliter = '\n-----------------------------------\n' + if len(document) > 0: + for doc in document: + file = QuerySet(File).filter(id=doc['file_id']).first() + file_type = doc['name'].split('.')[-1] + if file_type.lower() in ['txt', 'md', 'csv', 'html']: + content += spliter + doc['name'] + '\n' + file.get_byte().tobytes().decode('utf-8') + + + return NodeResult({'content': content}, {}) def get_details(self, index: int, **kwargs): - pass + return { + 'name': self.node.properties.get('stepName'), + "index": index, + 'run_time': self.context.get('run_time'), + 'type': self.node.type, + 'content': self.context.get('content'), + 'status': self.status, + 'err_message': self.err_message, + 'document_list': self.context.get('document_list') + } diff --git a/apps/application/flow/step_node/image_understand_step_node/i_image_understand_node.py b/apps/application/flow/step_node/image_understand_step_node/i_image_understand_node.py index 4c15ad8cd..26fb431d0 100644 --- a/apps/application/flow/step_node/image_understand_step_node/i_image_understand_node.py +++ b/apps/application/flow/step_node/image_understand_step_node/i_image_understand_node.py @@ -18,7 +18,7 @@ class ImageUnderstandNodeSerializer(serializers.Serializer): is_result = serializers.BooleanField(required=False, error_messages=ErrMessage.boolean('是否返回内容')) - image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片仅1张")) + image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片")) class IImageUnderstandNode(INode): diff --git a/apps/application/flow/step_node/image_understand_step_node/impl/base_image_understand_node.py b/apps/application/flow/step_node/image_understand_step_node/impl/base_image_understand_node.py index e6a88a9f1..046d6f783 100644 --- a/apps/application/flow/step_node/image_understand_step_node/impl/base_image_understand_node.py +++ b/apps/application/flow/step_node/image_understand_step_node/impl/base_image_understand_node.py @@ -25,7 +25,7 @@ def _write_context(node_variable: Dict, workflow_variable: Dict, node: INode, wo node.context['question'] = node_variable['question'] node.context['run_time'] = time.time() - node.context['start_time'] if workflow.is_result(node, NodeResult(node_variable, workflow_variable)): - workflow.answer += answer + node.answer_text = answer def write_context_stream(node_variable: Dict, workflow_variable: Dict, node: INode, workflow): diff --git a/apps/application/flow/step_node/start_node/impl/base_start_node.py b/apps/application/flow/step_node/start_node/impl/base_start_node.py index 9ac7e2aef..6388e4dfd 100644 --- a/apps/application/flow/step_node/start_node/impl/base_start_node.py +++ b/apps/application/flow/step_node/start_node/impl/base_start_node.py @@ -52,8 +52,12 @@ class BaseStartStepNode(IStarNode): """ 开始节点 初始化全局变量 """ - return NodeResult({'question': question, 'image': self.workflow_manage.image_list}, - workflow_variable) + node_variable = { + 'question': question, + 'image': self.workflow_manage.image_list, + 'document': self.workflow_manage.document_list + } + return NodeResult(node_variable, workflow_variable) def get_details(self, index: int, **kwargs): global_fields = [] diff --git a/apps/application/flow/workflow_manage.py b/apps/application/flow/workflow_manage.py index 39c42aa24..0cd92e8e1 100644 --- a/apps/application/flow/workflow_manage.py +++ b/apps/application/flow/workflow_manage.py @@ -240,16 +240,20 @@ class NodeChunk: class WorkflowManage: def __init__(self, flow: Flow, params, work_flow_post_handler: WorkFlowPostHandler, base_to_response: BaseToResponse = SystemToResponse(), form_data=None, image_list=None, + document_list=None, start_node_id=None, start_node_data=None, chat_record=None): if form_data is None: form_data = {} if image_list is None: image_list = [] + if document_list is None: + document_list = [] self.start_node = None self.start_node_result_future = None self.form_data = form_data self.image_list = image_list + self.document_list = document_list self.params = params self.flow = flow self.lock = threading.Lock() @@ -511,7 +515,7 @@ class WorkflowManage: if index == 0: result.append(answer.get('content')) continue - if answer.get('type') != answer_text_list[index - 1]: + if answer.get('type') != answer_text_list[index - 1].get('type'): result.append(answer.get('content')) else: result[-1] += answer.get('content') diff --git a/apps/application/migrations/0011_application_model_params_setting.py b/apps/application/migrations/0011_application_model_params_setting.py index 440b94df5..656b54775 100644 --- a/apps/application/migrations/0011_application_model_params_setting.py +++ b/apps/application/migrations/0011_application_model_params_setting.py @@ -13,6 +13,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='application', name='model_params_setting', - field=models.JSONField(default={}, verbose_name='模型参数相关设置'), + field=models.JSONField(default=dict, verbose_name='模型参数相关设置'), ), ] diff --git a/apps/application/migrations/0017_application_tts_model_params_setting.py b/apps/application/migrations/0017_application_tts_model_params_setting.py index 43428841f..3276ca632 100644 --- a/apps/application/migrations/0017_application_tts_model_params_setting.py +++ b/apps/application/migrations/0017_application_tts_model_params_setting.py @@ -13,6 +13,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='application', name='tts_model_params_setting', - field=models.JSONField(default={}, verbose_name='模型参数相关设置'), + field=models.JSONField(default=dict, verbose_name='模型参数相关设置'), ), ] diff --git a/apps/application/migrations/0019_application_file_upload_enable_and_more.py b/apps/application/migrations/0019_application_file_upload_enable_and_more.py index f59a4990c..f8c33c2c1 100644 --- a/apps/application/migrations/0019_application_file_upload_enable_and_more.py +++ b/apps/application/migrations/0019_application_file_upload_enable_and_more.py @@ -23,7 +23,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='application', name='file_upload_setting', - field=models.JSONField(default={}, verbose_name='文件上传相关设置'), + field=models.JSONField(default=dict, verbose_name='文件上传相关设置'), ), migrations.AddField( model_name='chatrecord', diff --git a/apps/application/models/application.py b/apps/application/models/application.py index 5df928c4d..c90d1325c 100644 --- a/apps/application/models/application.py +++ b/apps/application/models/application.py @@ -48,8 +48,8 @@ class Application(AppModelMixin): model = models.ForeignKey(Model, on_delete=models.SET_NULL, db_constraint=False, blank=True, null=True) dataset_setting = models.JSONField(verbose_name="数据集参数设置", default=get_dataset_setting_dict) model_setting = models.JSONField(verbose_name="模型参数相关设置", default=get_model_setting_dict) - model_params_setting = models.JSONField(verbose_name="模型参数相关设置", default={}) - tts_model_params_setting = models.JSONField(verbose_name="模型参数相关设置", default={}) + model_params_setting = models.JSONField(verbose_name="模型参数相关设置", default=dict) + tts_model_params_setting = models.JSONField(verbose_name="模型参数相关设置", default=dict) problem_optimization = models.BooleanField(verbose_name="问题优化", default=False) icon = models.CharField(max_length=256, verbose_name="应用icon", default="/ui/favicon.ico") work_flow = models.JSONField(verbose_name="工作流数据", default=dict) @@ -67,7 +67,7 @@ class Application(AppModelMixin): tts_type = models.CharField(verbose_name="语音播放类型", max_length=20, default="BROWSER") clean_time = models.IntegerField(verbose_name="清理时间", default=180) file_upload_enable = models.BooleanField(verbose_name="文件上传是否启用", default=False) - file_upload_setting = models.JSONField(verbose_name="文件上传相关设置", default={}) + file_upload_setting = models.JSONField(verbose_name="文件上传相关设置", default=dict) @staticmethod diff --git a/apps/application/serializers/chat_message_serializers.py b/apps/application/serializers/chat_message_serializers.py index 455ef6a67..919cb71cf 100644 --- a/apps/application/serializers/chat_message_serializers.py +++ b/apps/application/serializers/chat_message_serializers.py @@ -230,7 +230,8 @@ class ChatMessageSerializer(serializers.Serializer): client_id = serializers.CharField(required=True, error_messages=ErrMessage.char("客户端id")) client_type = serializers.CharField(required=True, error_messages=ErrMessage.char("客户端类型")) form_data = serializers.DictField(required=False, error_messages=ErrMessage.char("全局变量")) - image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片仅1张")) + image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片")) + document_list = serializers.ListField(required=False, error_messages=ErrMessage.list("文档")) def is_valid_application_workflow(self, *, raise_exception=False): self.is_valid_intraday_access_num() @@ -322,6 +323,7 @@ class ChatMessageSerializer(serializers.Serializer): client_type = self.data.get('client_type') form_data = self.data.get('form_data') image_list = self.data.get('image_list') + document_list = self.data.get('document_list') user_id = chat_info.application.user_id chat_record_id = self.data.get('chat_record_id') chat_record = None @@ -336,7 +338,7 @@ class ChatMessageSerializer(serializers.Serializer): 'client_id': client_id, 'client_type': client_type, 'user_id': user_id}, WorkFlowPostHandler(chat_info, client_id, client_type), - base_to_response, form_data, image_list, self.data.get('runtime_node_id'), + base_to_response, form_data, image_list, document_list, self.data.get('runtime_node_id'), self.data.get('node_data'), chat_record) r = work_flow_manage.run() return r diff --git a/apps/application/views/chat_views.py b/apps/application/views/chat_views.py index 586787b20..790277860 100644 --- a/apps/application/views/chat_views.py +++ b/apps/application/views/chat_views.py @@ -132,6 +132,8 @@ class ChatView(APIView): 'image_list': request.data.get( 'image_list') if 'image_list' in request.data else [], + 'document_list': request.data.get( + 'document_list') if 'document_list' in request.data else [], 'client_type': request.auth.client_type, 'runtime_node_id': request.data.get('runtime_node_id', None), 'node_data': request.data.get('node_data', {}), diff --git a/ui/package.json b/ui/package.json index e6067f69c..3203bd466 100644 --- a/ui/package.json +++ b/ui/package.json @@ -17,6 +17,7 @@ "@ctrl/tinycolor": "^4.1.0", "@logicflow/core": "^1.2.27", "@logicflow/extension": "^1.2.27", + "@antv/layout": "^0.3.1", "@vueuse/core": "^10.9.0", "@wecom/jssdk": "^2.1.0", "axios": "^0.28.0", diff --git a/ui/src/api/type/application.ts b/ui/src/api/type/application.ts index 5d4971f7f..11b630f13 100644 --- a/ui/src/api/type/application.ts +++ b/ui/src/api/type/application.ts @@ -39,7 +39,8 @@ interface chatType { record_id: string chat_id: string vote_status: string - status?: number + status?: number, + execution_details: any[] } export class ChatRecordManage { diff --git a/ui/src/components/ai-chat/component/answer-content/index.vue b/ui/src/components/ai-chat/component/answer-content/index.vue index f02fd74dd..1a75737bb 100644 --- a/ui/src/components/ai-chat/component/answer-content/index.vue +++ b/ui/src/components/ai-chat/component/answer-content/index.vue @@ -1,14 +1,20 @@ diff --git a/ui/src/components/ai-chat/component/chat-input-operate/index.vue b/ui/src/components/ai-chat/component/chat-input-operate/index.vue index 8ae880d5c..520621a91 100644 --- a/ui/src/components/ai-chat/component/chat-input-operate/index.vue +++ b/ui/src/components/ai-chat/component/chat-input-operate/index.vue @@ -20,10 +20,12 @@
+ @@ -126,6 +128,13 @@ const localLoading = computed({ emit('update:loading', v) } }) + + +const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp'] +const documentExtensions = ['pdf', 'docx', 'txt', 'xls', 'xlsx', 'md', 'html', 'csv'] +const videoExtensions = ['mp4', 'avi', 'mov', 'mkv', 'flv'] +const audioExtensions = ['mp3', 'wav', 'aac', 'flac'] + const uploadFile = async (file: any, fileList: any) => { const { maxFiles, fileLimit } = props.applicationDetails.file_upload_setting if (fileList.length > maxFiles) { @@ -141,7 +150,18 @@ const uploadFile = async (file: any, fileList: any) => { const formData = new FormData() for (const file of fileList) { formData.append('file', file.raw, file.name) - uploadFileList.value.push(file) + // + const extension = file.name.split('.').pop().toLowerCase() // 获取文件后缀名并转为小写 + + if (imageExtensions.includes(extension)) { + uploadImageList.value.push(file) + } else if (documentExtensions.includes(extension)) { + uploadDocumentList.value.push(file) + } else if (videoExtensions.includes(extension)) { + // videos.push(file) + } else if (audioExtensions.includes(extension)) { + // audios.push(file) + } } if (!chatId_context.value) { @@ -158,21 +178,29 @@ const uploadFile = async (file: any, fileList: any) => { ) .then((response) => { fileList.splice(0, fileList.length) - uploadFileList.value.forEach((file: any) => { + uploadImageList.value.forEach((file: any) => { const f = response.data.filter((f: any) => f.name === file.name) if (f.length > 0) { file.url = f[0].url file.file_id = f[0].file_id } }) - console.log(uploadFileList.value) + uploadDocumentList.value.forEach((file: any) => { + const f = response.data.filter((f: any) => f.name === file.name) + if (f.length > 0) { + file.url = f[0].url + file.file_id = f[0].file_id + } + }) + console.log(uploadDocumentList.value, uploadImageList.value) }) } const recorderTime = ref(0) const startRecorderTime = ref(false) const recorderLoading = ref(false) const inputValue = ref('') -const uploadFileList = ref>([]) +const uploadImageList = ref>([]) +const uploadDocumentList = ref>([]) const mediaRecorderStatus = ref(true) // 定义响应式引用 const mediaRecorder = ref(null) @@ -289,15 +317,20 @@ const handleTimeChange = () => { handleTimeChange() }, 1000) } + function sendChatHandle(event: any) { if (!event.ctrlKey) { // 如果没有按下组合键ctrl,则会阻止默认事件 event.preventDefault() if (!isDisabledChart.value && !props.loading && !event.isComposing) { if (inputValue.value.trim()) { - props.sendMessage(inputValue.value, { image_list: uploadFileList.value }) + props.sendMessage(inputValue.value, { + image_list: uploadImageList.value, + document_list: uploadDocumentList.value + }) inputValue.value = '' - uploadFileList.value = [] + uploadImageList.value = [] + uploadDocumentList.value = [] quickInputRef.value.textareaStyle.height = '45px' } } diff --git a/ui/src/components/ai-chat/component/question-content/index.vue b/ui/src/components/ai-chat/component/question-content/index.vue index fcbac8bc9..ef49c3788 100644 --- a/ui/src/components/ai-chat/component/question-content/index.vue +++ b/ui/src/components/ai-chat/component/question-content/index.vue @@ -22,10 +22,19 @@ diff --git a/ui/src/components/ai-chat/index.vue b/ui/src/components/ai-chat/index.vue index b2b94a5f1..5ce521ff0 100644 --- a/ui/src/components/ai-chat/index.vue +++ b/ui/src/components/ai-chat/index.vue @@ -370,7 +370,7 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean, other_para */ function getSourceDetail(row: any) { logApi.getRecordDetail(id || props.appId, row.chat_id, row.record_id, loading).then((res) => { - const exclude_keys = ['answer_text', 'id'] + const exclude_keys = ['answer_text', 'id', 'answer_text_list'] Object.keys(res.data).forEach((key) => { if (!exclude_keys.includes(key)) { row[key] = res.data[key] diff --git a/ui/src/components/dynamics-form/items/table/TableCheckbox.vue b/ui/src/components/dynamics-form/items/table/TableCheckbox.vue index 12db31a49..29641ac24 100644 --- a/ui/src/components/dynamics-form/items/table/TableCheckbox.vue +++ b/ui/src/components/dynamics-form/items/table/TableCheckbox.vue @@ -72,7 +72,7 @@ const props = defineProps<{ modelValue?: Array }>() const rowTemp = ref() -const evalF = (text: string, row: any) => { +const evalF: (text: string, row: any) => string = (text: string, row: any) => { rowTemp.value = row return eval(text) } @@ -167,8 +167,8 @@ watch( const activeText = computed(() => { if (props.modelValue) { - const rows = option_list.value.filter( - (f: any) => props.modelValue?.includes(f[valueField.value]) + const rows = option_list.value.filter((f: any) => + props.modelValue?.includes(f[valueField.value]) ) if (rows) { if (rows.length > 3) { diff --git a/ui/src/components/icons/index.ts b/ui/src/components/icons/index.ts index 35e94fafd..16c337c9b 100644 --- a/ui/src/components/icons/index.ts +++ b/ui/src/components/icons/index.ts @@ -1228,6 +1228,28 @@ export const iconMap: any = { ]) } }, + + 'app-beautify': { + iconReader: () => { + return h('i', [ + h( + 'svg', + { + style: { height: '100%', width: '100%' }, + viewBox: '0 0 1024 1024', + version: '1.1', + xmlns: 'http://www.w3.org/2000/svg' + }, + [ + h('path', { + d: 'M739.6864 689.92l4.2496 3.584 136.4992 135.936a34.1504 34.1504 0 0 1-43.9296 51.968l-4.1984-3.584-136.5504-135.936a34.1504 34.1504 0 0 1 43.9296-51.968zM663.4496 151.552a34.1504 34.1504 0 0 1 51.2512 30.464l-5.9392 216.6272 156.4672 146.1248a34.1504 34.1504 0 0 1-8.6528 55.808l-4.8128 1.792-202.8032 61.0816-87.4496 197.12a34.1504 34.1504 0 0 1-56.32 9.216l-3.2768-4.096-119.5008-178.432-209.9712-24.064a34.1504 34.1504 0 0 1-26.1632-50.176l2.7648-4.3008 129.28-171.7248-42.5472-212.3776a34.1504 34.1504 0 0 1 40.448-40.1408l4.6592 1.3312 198.912 72.3456z m-18.6368 89.7536l-144.5376 83.968a34.1504 34.1504 0 0 1-28.8256 2.56L314.5728 270.592l33.792 167.8848c1.4848 7.68 0.3584 15.5136-3.1744 22.3232l-3.072 4.9152-102.656 136.2944 166.4 19.1488c8.2944 0.9216 15.872 4.864 21.4016 10.9568l3.072 3.9424 93.8496 140.032 68.7104-154.7776a34.1504 34.1504 0 0 1 16.7936-17.0496l4.608-1.792 160.9216-48.4864-124.2624-116.0192a34.1504 34.1504 0 0 1-10.4448-20.0704l-0.3584-5.7856 4.6592-170.9056z', + fill: 'currentColor' + }) + ] + ) + ]) + } + } // 'app-history-outlined': { // iconReader: () => { // return h('i', [ diff --git a/ui/src/components/markdown/EchartsRander.vue b/ui/src/components/markdown/EchartsRander.vue index 6e3d2e87d..f008cdaa0 100644 --- a/ui/src/components/markdown/EchartsRander.vue +++ b/ui/src/components/markdown/EchartsRander.vue @@ -7,12 +7,7 @@ import { onMounted, nextTick, watch, onBeforeUnmount, ref } from 'vue' import * as echarts from 'echarts' const tmp = ref() -const props = defineProps({ - option: { - type: String, - required: true - } -}) +const props = defineProps<{ option: string }>() const chartsRef = ref() const style = ref({ diff --git a/ui/src/components/markdown/FormRander.vue b/ui/src/components/markdown/FormRander.vue index dcff8f512..7bea3c7ed 100644 --- a/ui/src/components/markdown/FormRander.vue +++ b/ui/src/components/markdown/FormRander.vue @@ -20,7 +20,7 @@ import { computed, ref } from 'vue' import DynamicsForm from '@/components/dynamics-form/index.vue' const props = defineProps<{ form_setting: string - sendMessage: (question: string, type: 'old' | 'new', other_params_data?: any) => void + sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void }>() const form_setting_data = computed(() => { if (props.form_setting) { @@ -68,11 +68,13 @@ const submit = () => { dynamicsFormRef.value?.validate().then(() => { _submit.value = true const setting = JSON.parse(props.form_setting) - props.sendMessage('', 'old', { - runtime_node_id: setting.runtime_node_id, - chat_record_id: setting.chat_record_id, - node_data: form_data.value - }) + if (props.sendMessage) { + props.sendMessage('', 'old', { + runtime_node_id: setting.runtime_node_id, + chat_record_id: setting.chat_record_id, + node_data: form_data.value + }) + } }) } diff --git a/ui/src/components/markdown/MdRenderer.vue b/ui/src/components/markdown/MdRenderer.vue index f8faf3b2d..3843d3b7d 100644 --- a/ui/src/components/markdown/MdRenderer.vue +++ b/ui/src/components/markdown/MdRenderer.vue @@ -15,7 +15,7 @@ :option="item.content" > diff --git a/ui/src/workflow/common/NodeContainer.vue b/ui/src/workflow/common/NodeContainer.vue index 59df25c68..ac83fc4d7 100644 --- a/ui/src/workflow/common/NodeContainer.vue +++ b/ui/src/workflow/common/NodeContainer.vue @@ -51,7 +51,7 @@
-
+
+ + + @@ -30,5 +33,8 @@ function fitView() { props.lf?.resetTranslate() props.lf?.fitView() } +const layout = () => { + props.lf?.extension.dagre.layout() +} diff --git a/ui/src/workflow/common/app-node.ts b/ui/src/workflow/common/app-node.ts index f720ad422..3009039a9 100644 --- a/ui/src/workflow/common/app-node.ts +++ b/ui/src/workflow/common/app-node.ts @@ -137,6 +137,26 @@ class AppNode extends HtmlResize.view { } class AppNodeModel extends HtmlResize.model { + refreshDeges() { + // 更新节点连接边的path + this.incoming.edges.forEach((edge: any) => { + // 调用自定义的更新方案 + edge.updatePathByAnchor() + }) + this.outgoing.edges.forEach((edge: any) => { + edge.updatePathByAnchor() + }) + } + set_position(position: { x?: number; y?: number }) { + const { x, y } = position + if (x) { + this.x = x + } + if (y) { + this.y = y + } + this.refreshDeges() + } getResizeOutlineStyle() { const style = super.getResizeOutlineStyle() style.stroke = 'none' diff --git a/ui/src/workflow/index.vue b/ui/src/workflow/index.vue index 3d9daaa22..2ba201cbb 100644 --- a/ui/src/workflow/index.vue +++ b/ui/src/workflow/index.vue @@ -5,13 +5,14 @@