mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
Merge branch 'main' of https://github.com/maxkb-dev/maxkb
This commit is contained in:
commit
1e1e98e155
|
|
@ -40,6 +40,10 @@ def write_context(step_variable: Dict, global_variable: Dict, node, workflow):
|
|||
node.context['run_time'] = time.time() - node.context['start_time']
|
||||
|
||||
|
||||
def is_interrupt(node, step_variable: Dict, global_variable: Dict):
|
||||
return node.type == 'form-node' and not node.context.get('is_submit', False)
|
||||
|
||||
|
||||
class WorkFlowPostHandler:
|
||||
def __init__(self, chat_info, client_id, client_type):
|
||||
self.chat_info = chat_info
|
||||
|
|
@ -57,7 +61,7 @@ class WorkFlowPostHandler:
|
|||
answer_tokens = sum([row.get('answer_tokens') for row in details.values() if
|
||||
'answer_tokens' in row and row.get('answer_tokens') is not None])
|
||||
answer_text_list = workflow.get_answer_text_list()
|
||||
answer_text = '\n\n'.join(answer_text_list)
|
||||
answer_text = '\n\n'.join(answer['content'] for answer in answer_text_list)
|
||||
if workflow.chat_record is not None:
|
||||
chat_record = workflow.chat_record
|
||||
chat_record.answer_text = answer_text
|
||||
|
|
@ -91,10 +95,11 @@ class WorkFlowPostHandler:
|
|||
|
||||
class NodeResult:
|
||||
def __init__(self, node_variable: Dict, workflow_variable: Dict,
|
||||
_write_context=write_context):
|
||||
_write_context=write_context, _is_interrupt=is_interrupt):
|
||||
self._write_context = _write_context
|
||||
self.node_variable = node_variable
|
||||
self.workflow_variable = workflow_variable
|
||||
self._is_interrupt = _is_interrupt
|
||||
|
||||
def write_context(self, node, workflow):
|
||||
return self._write_context(self.node_variable, self.workflow_variable, node, workflow)
|
||||
|
|
@ -102,6 +107,14 @@ class NodeResult:
|
|||
def is_assertion_result(self):
|
||||
return 'branch_id' in self.node_variable
|
||||
|
||||
def is_interrupt_exec(self, current_node):
|
||||
"""
|
||||
是否中断执行
|
||||
@param current_node:
|
||||
@return:
|
||||
"""
|
||||
return self._is_interrupt(current_node, self.node_variable, self.workflow_variable)
|
||||
|
||||
|
||||
class ReferenceAddressSerializer(serializers.Serializer):
|
||||
node_id = serializers.CharField(required=True, error_messages=ErrMessage.char("节点id"))
|
||||
|
|
@ -139,14 +152,18 @@ class INode:
|
|||
pass
|
||||
|
||||
def get_answer_text(self):
|
||||
return self.answer_text
|
||||
if self.answer_text is None:
|
||||
return None
|
||||
return {'content': self.answer_text, 'runtime_node_id': self.runtime_node_id,
|
||||
'chat_record_id': self.workflow_params['chat_record_id']}
|
||||
|
||||
def __init__(self, node, workflow_params, workflow_manage, up_node_id_list=None):
|
||||
def __init__(self, node, workflow_params, workflow_manage, up_node_id_list=None,
|
||||
get_node_params=lambda node: node.properties.get('node_data')):
|
||||
# 当前步骤上下文,用于存储当前步骤信息
|
||||
self.status = 200
|
||||
self.err_message = ''
|
||||
self.node = node
|
||||
self.node_params = node.properties.get('node_data')
|
||||
self.node_params = get_node_params(node)
|
||||
self.workflow_params = workflow_params
|
||||
self.workflow_manage = workflow_manage
|
||||
self.node_params_serializer = None
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ class ApplicationNodeSerializer(serializers.Serializer):
|
|||
user_input_field_list = serializers.ListField(required=False, error_messages=ErrMessage.uuid("用户输入字段"))
|
||||
image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片"))
|
||||
document_list = serializers.ListField(required=False, error_messages=ErrMessage.list("文档"))
|
||||
child_node = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.dict("子节点"))
|
||||
node_data = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.dict("表单数据"))
|
||||
|
||||
|
||||
class IApplicationNode(INode):
|
||||
|
|
@ -55,5 +57,5 @@ class IApplicationNode(INode):
|
|||
message=str(question), **kwargs)
|
||||
|
||||
def execute(self, application_id, message, chat_id, chat_record_id, stream, re_chat, client_id, client_type,
|
||||
app_document_list=None, app_image_list=None, **kwargs) -> NodeResult:
|
||||
app_document_list=None, app_image_list=None, child_node=None, node_data=None, **kwargs) -> NodeResult:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -2,19 +2,25 @@
|
|||
import json
|
||||
import time
|
||||
import uuid
|
||||
from typing import List, Dict
|
||||
from typing import Dict
|
||||
|
||||
from application.flow.i_step_node import NodeResult, INode
|
||||
from application.flow.step_node.application_node.i_application_node import IApplicationNode
|
||||
from application.models import Chat
|
||||
from common.handle.impl.response.openai_to_response import OpenaiToResponse
|
||||
|
||||
|
||||
def string_to_uuid(input_str):
|
||||
return str(uuid.uuid5(uuid.NAMESPACE_DNS, input_str))
|
||||
|
||||
|
||||
def _is_interrupt_exec(node, node_variable: Dict, workflow_variable: Dict):
|
||||
return node_variable.get('is_interrupt_exec', False)
|
||||
|
||||
|
||||
def _write_context(node_variable: Dict, workflow_variable: Dict, node: INode, workflow, answer: str):
|
||||
result = node_variable.get('result')
|
||||
node.context['child_node'] = node_variable['child_node']
|
||||
node.context['is_interrupt_exec'] = node_variable['is_interrupt_exec']
|
||||
node.context['message_tokens'] = result.get('usage', {}).get('prompt_tokens', 0)
|
||||
node.context['answer_tokens'] = result.get('usage', {}).get('completion_tokens', 0)
|
||||
node.context['answer'] = answer
|
||||
|
|
@ -36,17 +42,34 @@ def write_context_stream(node_variable: Dict, workflow_variable: Dict, node: INo
|
|||
response = node_variable.get('result')
|
||||
answer = ''
|
||||
usage = {}
|
||||
node_child_node = {}
|
||||
is_interrupt_exec = False
|
||||
for chunk in response:
|
||||
# 先把流转成字符串
|
||||
response_content = chunk.decode('utf-8')[6:]
|
||||
response_content = json.loads(response_content)
|
||||
choices = response_content.get('choices')
|
||||
if choices and isinstance(choices, list) and len(choices) > 0:
|
||||
content = choices[0].get('delta', {}).get('content', '')
|
||||
answer += content
|
||||
yield content
|
||||
content = response_content.get('content', '')
|
||||
runtime_node_id = response_content.get('runtime_node_id', '')
|
||||
chat_record_id = response_content.get('chat_record_id', '')
|
||||
child_node = response_content.get('child_node')
|
||||
node_type = response_content.get('node_type')
|
||||
real_node_id = response_content.get('real_node_id')
|
||||
node_is_end = response_content.get('node_is_end', False)
|
||||
if node_type == 'form-node':
|
||||
is_interrupt_exec = True
|
||||
answer += content
|
||||
node_child_node = {'runtime_node_id': runtime_node_id, 'chat_record_id': chat_record_id,
|
||||
'child_node': child_node}
|
||||
yield {'content': content,
|
||||
'node_type': node_type,
|
||||
'runtime_node_id': runtime_node_id, 'chat_record_id': chat_record_id,
|
||||
'child_node': child_node,
|
||||
'real_node_id': real_node_id,
|
||||
'node_is_end': node_is_end}
|
||||
usage = response_content.get('usage', {})
|
||||
node_variable['result'] = {'usage': usage}
|
||||
node_variable['is_interrupt_exec'] = is_interrupt_exec
|
||||
node_variable['child_node'] = node_child_node
|
||||
_write_context(node_variable, workflow_variable, node, workflow, answer)
|
||||
|
||||
|
||||
|
|
@ -64,6 +87,11 @@ def write_context(node_variable: Dict, workflow_variable: Dict, node: INode, wor
|
|||
|
||||
|
||||
class BaseApplicationNode(IApplicationNode):
|
||||
def get_answer_text(self):
|
||||
if self.answer_text is None:
|
||||
return None
|
||||
return {'content': self.answer_text, 'runtime_node_id': self.runtime_node_id,
|
||||
'chat_record_id': self.workflow_params['chat_record_id'], 'child_node': self.context.get('child_node')}
|
||||
|
||||
def save_context(self, details, workflow_manage):
|
||||
self.context['answer'] = details.get('answer')
|
||||
|
|
@ -72,7 +100,7 @@ class BaseApplicationNode(IApplicationNode):
|
|||
self.answer_text = details.get('answer')
|
||||
|
||||
def execute(self, application_id, message, chat_id, chat_record_id, stream, re_chat, client_id, client_type,
|
||||
app_document_list=None, app_image_list=None,
|
||||
app_document_list=None, app_image_list=None, child_node=None, node_data=None,
|
||||
**kwargs) -> NodeResult:
|
||||
from application.serializers.chat_message_serializers import ChatMessageSerializer
|
||||
# 生成嵌入应用的chat_id
|
||||
|
|
@ -85,6 +113,14 @@ class BaseApplicationNode(IApplicationNode):
|
|||
app_document_list = []
|
||||
if app_image_list is None:
|
||||
app_image_list = []
|
||||
runtime_node_id = None
|
||||
record_id = None
|
||||
child_node_value = None
|
||||
if child_node is not None:
|
||||
runtime_node_id = child_node.get('runtime_node_id')
|
||||
record_id = child_node.get('chat_record_id')
|
||||
child_node_value = child_node.get('child_node')
|
||||
|
||||
response = ChatMessageSerializer(
|
||||
data={'chat_id': current_chat_id, 'message': message,
|
||||
're_chat': re_chat,
|
||||
|
|
@ -94,16 +130,20 @@ class BaseApplicationNode(IApplicationNode):
|
|||
'client_type': client_type,
|
||||
'document_list': app_document_list,
|
||||
'image_list': app_image_list,
|
||||
'form_data': kwargs}).chat(base_to_response=OpenaiToResponse())
|
||||
'runtime_node_id': runtime_node_id,
|
||||
'chat_record_id': record_id,
|
||||
'child_node': child_node_value,
|
||||
'node_data': node_data,
|
||||
'form_data': kwargs}).chat()
|
||||
if response.status_code == 200:
|
||||
if stream:
|
||||
content_generator = response.streaming_content
|
||||
return NodeResult({'result': content_generator, 'question': message}, {},
|
||||
_write_context=write_context_stream)
|
||||
_write_context=write_context_stream, _is_interrupt=_is_interrupt_exec)
|
||||
else:
|
||||
data = json.loads(response.content)
|
||||
return NodeResult({'result': data, 'question': message}, {},
|
||||
_write_context=write_context)
|
||||
_write_context=write_context, _is_interrupt=_is_interrupt_exec)
|
||||
|
||||
def get_details(self, index: int, **kwargs):
|
||||
global_fields = []
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from common.util.field_message import ErrMessage
|
|||
class FormNodeParamsSerializer(serializers.Serializer):
|
||||
form_field_list = serializers.ListField(required=True, error_messages=ErrMessage.list("表单配置"))
|
||||
form_content_format = serializers.CharField(required=True, error_messages=ErrMessage.char('表单输出内容'))
|
||||
form_data = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.dict("表单数据"))
|
||||
|
||||
|
||||
class IFormNode(INode):
|
||||
|
|
@ -29,5 +30,5 @@ class IFormNode(INode):
|
|||
def _run(self):
|
||||
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
|
||||
|
||||
def execute(self, form_field_list, form_content_format, **kwargs) -> NodeResult:
|
||||
def execute(self, form_field_list, form_content_format, form_data, **kwargs) -> NodeResult:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -42,7 +42,12 @@ class BaseFormNode(IFormNode):
|
|||
for key in form_data:
|
||||
self.context[key] = form_data[key]
|
||||
|
||||
def execute(self, form_field_list, form_content_format, **kwargs) -> NodeResult:
|
||||
def execute(self, form_field_list, form_content_format, form_data, **kwargs) -> NodeResult:
|
||||
if form_data is not None:
|
||||
self.context['is_submit'] = True
|
||||
self.context['form_data'] = form_data
|
||||
else:
|
||||
self.context['is_submit'] = False
|
||||
form_setting = {"form_field_list": form_field_list, "runtime_node_id": self.runtime_node_id,
|
||||
"chat_record_id": self.flow_params_serializer.data.get("chat_record_id"),
|
||||
"is_submit": self.context.get("is_submit", False)}
|
||||
|
|
@ -63,7 +68,8 @@ class BaseFormNode(IFormNode):
|
|||
form = f'<form_rander>{json.dumps(form_setting)}</form_rander>'
|
||||
prompt_template = PromptTemplate.from_template(form_content_format, template_format='jinja2')
|
||||
value = prompt_template.format(form=form)
|
||||
return value
|
||||
return {'content': value, 'runtime_node_id': self.runtime_node_id,
|
||||
'chat_record_id': self.workflow_params['chat_record_id']}
|
||||
|
||||
def get_details(self, index: int, **kwargs):
|
||||
form_content_format = self.context.get('form_content_format')
|
||||
|
|
|
|||
|
|
@ -244,15 +244,15 @@ class WorkflowManage:
|
|||
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):
|
||||
start_node_data=None, chat_record=None, child_node=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_id = start_node_id
|
||||
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
|
||||
|
|
@ -270,6 +270,7 @@ class WorkflowManage:
|
|||
self.base_to_response = base_to_response
|
||||
self.chat_record = chat_record
|
||||
self.await_future_map = {}
|
||||
self.child_node = child_node
|
||||
if start_node_id is not None:
|
||||
self.load_node(chat_record, start_node_id, start_node_data)
|
||||
else:
|
||||
|
|
@ -290,11 +291,17 @@ class WorkflowManage:
|
|||
for node_details in sorted(chat_record.details.values(), key=lambda d: d.get('index')):
|
||||
node_id = node_details.get('node_id')
|
||||
if node_details.get('runtime_node_id') == start_node_id:
|
||||
self.start_node = self.get_node_cls_by_id(node_id, node_details.get('up_node_id_list'))
|
||||
self.start_node.valid_args(self.start_node.node_params, self.start_node.workflow_params)
|
||||
self.start_node.save_context(node_details, self)
|
||||
node_result = NodeResult({**start_node_data, 'form_data': start_node_data, 'is_submit': True}, {})
|
||||
self.start_node_result_future = NodeResultFuture(node_result, None)
|
||||
def get_node_params(n):
|
||||
is_result = False
|
||||
if n.type == 'application-node':
|
||||
is_result = True
|
||||
return {**n.properties.get('node_data'), 'form_data': start_node_data, 'node_data': start_node_data,
|
||||
'child_node': self.child_node, 'is_result': is_result}
|
||||
|
||||
self.start_node = self.get_node_cls_by_id(node_id, node_details.get('up_node_id_list'),
|
||||
get_node_params=get_node_params)
|
||||
self.start_node.valid_args(
|
||||
{**self.start_node.node_params, 'form_data': start_node_data}, self.start_node.workflow_params)
|
||||
self.node_context.append(self.start_node)
|
||||
continue
|
||||
|
||||
|
|
@ -306,7 +313,7 @@ class WorkflowManage:
|
|||
|
||||
def run(self):
|
||||
if self.params.get('stream'):
|
||||
return self.run_stream(self.start_node, self.start_node_result_future)
|
||||
return self.run_stream(self.start_node, None)
|
||||
return self.run_block()
|
||||
|
||||
def run_block(self):
|
||||
|
|
@ -352,9 +359,19 @@ class WorkflowManage:
|
|||
break
|
||||
yield chunk
|
||||
finally:
|
||||
details = self.get_runtime_details()
|
||||
message_tokens = sum([row.get('message_tokens') for row in details.values() if
|
||||
'message_tokens' in row and row.get('message_tokens') is not None])
|
||||
answer_tokens = sum([row.get('answer_tokens') for row in details.values() if
|
||||
'answer_tokens' in row and row.get('answer_tokens') is not None])
|
||||
self.work_flow_post_handler.handler(self.params['chat_id'], self.params['chat_record_id'],
|
||||
self.answer,
|
||||
self)
|
||||
yield self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
|
||||
self.params['chat_record_id'],
|
||||
'',
|
||||
[],
|
||||
'', True, message_tokens, answer_tokens, {})
|
||||
|
||||
def run_chain_async(self, current_node, node_result_future):
|
||||
future = executor.submit(self.run_chain, current_node, node_result_future)
|
||||
|
|
@ -423,6 +440,8 @@ class WorkflowManage:
|
|||
|
||||
def hand_event_node_result(self, current_node, node_result_future):
|
||||
node_chunk = NodeChunk()
|
||||
real_node_id = current_node.runtime_node_id
|
||||
child_node = {}
|
||||
try:
|
||||
current_result = node_result_future.result()
|
||||
result = current_result.write_context(current_node, self)
|
||||
|
|
@ -430,21 +449,38 @@ class WorkflowManage:
|
|||
if self.is_result(current_node, current_result):
|
||||
self.node_chunk_manage.add_node_chunk(node_chunk)
|
||||
for r in result:
|
||||
content = r
|
||||
child_node = {}
|
||||
node_is_end = False
|
||||
if isinstance(r, dict):
|
||||
content = r.get('content')
|
||||
child_node = {'runtime_node_id': r.get('runtime_node_id'),
|
||||
'chat_record_id': r.get('chat_record_id')
|
||||
, 'child_node': r.get('child_node')}
|
||||
real_node_id = r.get('real_node_id')
|
||||
node_is_end = r.get('node_is_end')
|
||||
chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
|
||||
self.params['chat_record_id'],
|
||||
current_node.id,
|
||||
current_node.up_node_id_list,
|
||||
r, False, 0, 0,
|
||||
content, False, 0, 0,
|
||||
{'node_type': current_node.type,
|
||||
'view_type': current_node.view_type})
|
||||
'runtime_node_id': current_node.runtime_node_id,
|
||||
'view_type': current_node.view_type,
|
||||
'child_node': child_node,
|
||||
'node_is_end': node_is_end,
|
||||
'real_node_id': real_node_id})
|
||||
node_chunk.add_chunk(chunk)
|
||||
chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
|
||||
self.params['chat_record_id'],
|
||||
current_node.id,
|
||||
current_node.up_node_id_list,
|
||||
'', False, 0, 0, {'node_is_end': True,
|
||||
'runtime_node_id': current_node.runtime_node_id,
|
||||
'node_type': current_node.type,
|
||||
'view_type': current_node.view_type})
|
||||
'view_type': current_node.view_type,
|
||||
'child_node': child_node,
|
||||
'real_node_id': real_node_id})
|
||||
node_chunk.end(chunk)
|
||||
else:
|
||||
list(result)
|
||||
|
|
@ -461,8 +497,12 @@ class WorkflowManage:
|
|||
current_node.id,
|
||||
current_node.up_node_id_list,
|
||||
str(e), False, 0, 0,
|
||||
{'node_is_end': True, 'node_type': current_node.type,
|
||||
'view_type': current_node.view_type})
|
||||
{'node_is_end': True,
|
||||
'runtime_node_id': current_node.runtime_node_id,
|
||||
'node_type': current_node.type,
|
||||
'view_type': current_node.view_type,
|
||||
'child_node': {},
|
||||
'real_node_id': real_node_id})
|
||||
if not self.node_chunk_manage.contains(node_chunk):
|
||||
self.node_chunk_manage.add_node_chunk(node_chunk)
|
||||
node_chunk.end(chunk)
|
||||
|
|
@ -554,9 +594,9 @@ class WorkflowManage:
|
|||
else:
|
||||
if len(result) > 0:
|
||||
exec_index = len(result) - 1
|
||||
content = result[exec_index]
|
||||
result[exec_index] += answer_text if len(
|
||||
content) == 0 else ('\n\n' + answer_text)
|
||||
content = result[exec_index]['content']
|
||||
result[exec_index]['content'] += answer_text['content'] if len(
|
||||
content) == 0 else ('\n\n' + answer_text['content'])
|
||||
else:
|
||||
answer_text = node.get_answer_text()
|
||||
result.insert(0, answer_text)
|
||||
|
|
@ -613,8 +653,8 @@ class WorkflowManage:
|
|||
@param current_node_result: 当前可执行节点结果
|
||||
@return: 可执行节点列表
|
||||
"""
|
||||
|
||||
if current_node.type == 'form-node' and 'form_data' not in current_node_result.node_variable:
|
||||
# 判断是否中断执行
|
||||
if current_node_result.is_interrupt_exec(current_node):
|
||||
return []
|
||||
node_list = []
|
||||
if current_node_result is not None and current_node_result.is_assertion_result():
|
||||
|
|
@ -689,11 +729,12 @@ class WorkflowManage:
|
|||
base_node_list = [node for node in self.flow.nodes if node.type == 'base-node']
|
||||
return base_node_list[0]
|
||||
|
||||
def get_node_cls_by_id(self, node_id, up_node_id_list=None):
|
||||
def get_node_cls_by_id(self, node_id, up_node_id_list=None,
|
||||
get_node_params=lambda node: node.properties.get('node_data')):
|
||||
for node in self.flow.nodes:
|
||||
if node.id == node_id:
|
||||
node_instance = get_node(node.type)(node,
|
||||
self.params, self, up_node_id_list)
|
||||
self.params, self, up_node_id_list, get_node_params)
|
||||
return node_instance
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import django.contrib.postgres.fields
|
|||
from django.db import migrations, models
|
||||
|
||||
sql = """
|
||||
UPDATE "public".application_chat_record
|
||||
SET "answer_text_list" = ARRAY[answer_text];
|
||||
UPDATE application_chat_record
|
||||
SET answer_text_list=ARRAY[jsonb_build_object('content',answer_text)]
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -28,8 +28,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='chatrecord',
|
||||
name='answer_text_list',
|
||||
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=40960), default=list,
|
||||
size=None, verbose_name='改进标注列表'),
|
||||
field=django.contrib.postgres.fields.ArrayField(base_field=models.JSONField(), default=list, size=None, verbose_name='改进标注列表')
|
||||
),
|
||||
migrations.RunSQL(sql)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ class Application(AppModelMixin):
|
|||
file_upload_enable = models.BooleanField(verbose_name="文件上传是否启用", default=False)
|
||||
file_upload_setting = models.JSONField(verbose_name="文件上传相关设置", default=dict)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_default_model_prompt():
|
||||
return ('已知信息:'
|
||||
|
|
@ -148,7 +147,7 @@ class ChatRecord(AppModelMixin):
|
|||
problem_text = models.CharField(max_length=10240, verbose_name="问题")
|
||||
answer_text = models.CharField(max_length=40960, verbose_name="答案")
|
||||
answer_text_list = ArrayField(verbose_name="改进标注列表",
|
||||
base_field=models.CharField(max_length=40960)
|
||||
base_field=models.JSONField()
|
||||
, default=list)
|
||||
message_tokens = models.IntegerField(verbose_name="请求token数量", default=0)
|
||||
answer_tokens = models.IntegerField(verbose_name="响应token数量", default=0)
|
||||
|
|
|
|||
|
|
@ -238,13 +238,14 @@ class ChatMessageSerializer(serializers.Serializer):
|
|||
runtime_node_id = serializers.CharField(required=False, allow_null=True, allow_blank=True,
|
||||
error_messages=ErrMessage.char("运行时节点id"))
|
||||
|
||||
node_data = serializers.DictField(required=False, error_messages=ErrMessage.char("节点参数"))
|
||||
node_data = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.char("节点参数"))
|
||||
application_id = serializers.UUIDField(required=False, allow_null=True, error_messages=ErrMessage.uuid("应用id"))
|
||||
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("图片"))
|
||||
document_list = serializers.ListField(required=False, error_messages=ErrMessage.list("文档"))
|
||||
child_node = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.dict("子节点"))
|
||||
|
||||
def is_valid_application_workflow(self, *, raise_exception=False):
|
||||
self.is_valid_intraday_access_num()
|
||||
|
|
@ -353,7 +354,7 @@ class ChatMessageSerializer(serializers.Serializer):
|
|||
'user_id': user_id}, WorkFlowPostHandler(chat_info, client_id, client_type),
|
||||
base_to_response, form_data, image_list, document_list,
|
||||
self.data.get('runtime_node_id'),
|
||||
self.data.get('node_data'), chat_record)
|
||||
self.data.get('node_data'), chat_record, self.data.get('child_node'))
|
||||
r = work_flow_manage.run()
|
||||
return r
|
||||
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ function initMaxkbStyle(root){
|
|||
position: absolute;
|
||||
{{x_type}}: {{x_value}}px;
|
||||
{{y_type}}: {{y_value}}px;
|
||||
z-index: 1000;
|
||||
z-index: 10001;
|
||||
}
|
||||
#maxkb .maxkb-tips {
|
||||
position: fixed;
|
||||
|
|
@ -180,7 +180,7 @@ function initMaxkbStyle(root){
|
|||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
background: #3370FF;
|
||||
z-index: 1000;
|
||||
z-index: 10001;
|
||||
}
|
||||
#maxkb .maxkb-tips .maxkb-arrow {
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -138,7 +138,8 @@ class ChatView(APIView):
|
|||
'node_id': request.data.get('node_id', None),
|
||||
'runtime_node_id': request.data.get('runtime_node_id', None),
|
||||
'node_data': request.data.get('node_data', {}),
|
||||
'chat_record_id': request.data.get('chat_record_id')}
|
||||
'chat_record_id': request.data.get('chat_record_id'),
|
||||
'child_node': request.data.get('child_node')}
|
||||
).chat()
|
||||
|
||||
@action(methods=['GET'], detail=False)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,14 @@ import setting.models
|
|||
from setting.models import Model
|
||||
from .listener_manage import *
|
||||
|
||||
update_document_status_sql = """
|
||||
UPDATE "public"."document"
|
||||
SET status ="replace"("replace"("replace"(status, '1', '3'), '0', '3'), '4', '3')
|
||||
"""
|
||||
|
||||
|
||||
def run():
|
||||
# QuerySet(Document).filter(status__in=[Status.embedding, Status.queue_up]).update(**{'status': Status.error})
|
||||
QuerySet(Model).filter(status=setting.models.Status.DOWNLOAD).update(status=setting.models.Status.ERROR,
|
||||
meta={'message': "下载程序被中断,请重试"})
|
||||
update_execute(update_document_status_sql, [])
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class OpenaiToResponse(BaseToResponse):
|
|||
def to_stream_chunk_response(self, chat_id, chat_record_id, node_id, up_node_id_list, content, is_end, completion_tokens,
|
||||
prompt_tokens, other_params: dict = None):
|
||||
chunk = ChatCompletionChunk(id=chat_record_id, model='', object='chat.completion.chunk',
|
||||
created=datetime.datetime.now().second, choices=[
|
||||
created=datetime.datetime.now().second,choices=[
|
||||
Choice(delta=ChoiceDelta(content=content, chat_id=chat_id), finish_reason='stop' if is_end else None,
|
||||
index=0)],
|
||||
usage=CompletionUsage(completion_tokens=completion_tokens,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class SystemToResponse(BaseToResponse):
|
|||
prompt_tokens, other_params: dict = None):
|
||||
if other_params is None:
|
||||
other_params = {}
|
||||
chunk = json.dumps({'chat_id': str(chat_id), 'id': str(chat_record_id), 'operate': True,
|
||||
chunk = json.dumps({'chat_id': str(chat_id), 'chat_record_id': str(chat_record_id), 'operate': True,
|
||||
'content': content, 'node_id': node_id, 'up_node_id_list': up_node_id_list, 'is_end': is_end,
|
||||
'usage': {'completion_tokens': completion_tokens,
|
||||
'prompt_tokens': prompt_tokens,
|
||||
|
|
|
|||
|
|
@ -142,7 +142,10 @@ class Fork:
|
|||
if len(charset_list) > 0:
|
||||
charset = charset_list[0]
|
||||
if charset != encoding:
|
||||
html_content = response.content.decode(charset)
|
||||
try:
|
||||
html_content = response.content.decode(charset)
|
||||
except Exception as e:
|
||||
logging.getLogger("max_kb").error(f'{e}')
|
||||
return BeautifulSoup(html_content, "html.parser")
|
||||
return beautiful_soup
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import openpyxl
|
|||
from celery_once import AlreadyQueued
|
||||
from django.core import validators
|
||||
from django.db import transaction
|
||||
from django.db.models import QuerySet
|
||||
from django.db.models import QuerySet, Count
|
||||
from django.db.models.functions import Substr, Reverse
|
||||
from django.http import HttpResponse
|
||||
from drf_yasg import openapi
|
||||
|
|
@ -56,6 +56,7 @@ from embedding.task.embedding import embedding_by_document, delete_embedding_by_
|
|||
delete_embedding_by_document, update_embedding_dataset_id, delete_embedding_by_paragraph_ids, \
|
||||
embedding_by_document_list
|
||||
from smartdoc.conf import PROJECT_DIR
|
||||
from django.db import models
|
||||
|
||||
parse_qa_handle_list = [XlsParseQAHandle(), CsvParseQAHandle(), XlsxParseQAHandle()]
|
||||
parse_table_handle_list = [CsvSplitHandle(), XlsSplitHandle(), XlsxSplitHandle()]
|
||||
|
|
@ -442,6 +443,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||
QuerySet(model=Paragraph).filter(document_id=document_id).delete()
|
||||
# 删除问题
|
||||
QuerySet(model=ProblemParagraphMapping).filter(document_id=document_id).delete()
|
||||
delete_problems_and_mappings([document_id])
|
||||
# 删除向量库
|
||||
delete_embedding_by_document(document_id)
|
||||
paragraphs = get_split_model('web.md').parse(result.content)
|
||||
|
|
@ -660,7 +662,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||
# 删除段落
|
||||
QuerySet(model=Paragraph).filter(document_id=document_id).delete()
|
||||
# 删除问题
|
||||
QuerySet(model=ProblemParagraphMapping).filter(document_id=document_id).delete()
|
||||
delete_problems_and_mappings([document_id])
|
||||
# 删除向量库
|
||||
delete_embedding_by_document(document_id)
|
||||
return True
|
||||
|
|
@ -987,7 +989,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||
document_id_list = instance.get("id_list")
|
||||
QuerySet(Document).filter(id__in=document_id_list).delete()
|
||||
QuerySet(Paragraph).filter(document_id__in=document_id_list).delete()
|
||||
QuerySet(ProblemParagraphMapping).filter(document_id__in=document_id_list).delete()
|
||||
delete_problems_and_mappings(document_id_list)
|
||||
# 删除向量库
|
||||
delete_embedding_by_document_list(document_id_list)
|
||||
return True
|
||||
|
|
@ -1086,3 +1088,18 @@ def file_to_paragraph(file, pattern_list: List, with_filter: bool, limit: int):
|
|||
if split_handle.support(file, get_buffer):
|
||||
return split_handle.handle(file, pattern_list, with_filter, limit, get_buffer, save_image)
|
||||
return default_split_handle.handle(file, pattern_list, with_filter, limit, get_buffer, save_image)
|
||||
|
||||
|
||||
def delete_problems_and_mappings(document_ids):
|
||||
problem_paragraph_mappings = ProblemParagraphMapping.objects.filter(document_id__in=document_ids)
|
||||
problem_ids = set(problem_paragraph_mappings.values_list('problem_id', flat=True))
|
||||
|
||||
if problem_ids:
|
||||
problem_paragraph_mappings.delete()
|
||||
remaining_problem_counts = ProblemParagraphMapping.objects.filter(problem_id__in=problem_ids).values(
|
||||
'problem_id').annotate(count=Count('problem_id'))
|
||||
remaining_problem_ids = {pc['problem_id'] for pc in remaining_problem_counts}
|
||||
problem_ids_to_delete = problem_ids - remaining_problem_ids
|
||||
Problem.objects.filter(id__in=problem_ids_to_delete).delete()
|
||||
else:
|
||||
problem_paragraph_mappings.delete()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from typing import Dict
|
|||
|
||||
from celery_once import AlreadyQueued
|
||||
from django.db import transaction
|
||||
from django.db.models import QuerySet
|
||||
from django.db.models import QuerySet, Count
|
||||
from drf_yasg import openapi
|
||||
from rest_framework import serializers
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ class ParagraphSerializers(ApiMixin, serializers.Serializer):
|
|||
self.is_valid(raise_exception=True)
|
||||
paragraph_id_list = instance.get("id_list")
|
||||
QuerySet(Paragraph).filter(id__in=paragraph_id_list).delete()
|
||||
QuerySet(ProblemParagraphMapping).filter(paragraph_id__in=paragraph_id_list).delete()
|
||||
delete_problems_and_mappings(paragraph_id_list)
|
||||
update_document_char_length(self.data.get('document_id'))
|
||||
# 删除向量库
|
||||
delete_embedding_by_paragraph_ids(paragraph_id_list)
|
||||
|
|
@ -541,14 +541,7 @@ class ParagraphSerializers(ApiMixin, serializers.Serializer):
|
|||
self.is_valid(raise_exception=True)
|
||||
paragraph_id = self.data.get('paragraph_id')
|
||||
Paragraph.objects.filter(id=paragraph_id).delete()
|
||||
|
||||
problem_id = ProblemParagraphMapping.objects.filter(paragraph_id=paragraph_id).values_list('problem_id',
|
||||
flat=True).first()
|
||||
|
||||
if problem_id is not None:
|
||||
if ProblemParagraphMapping.objects.filter(problem_id=problem_id).count() == 1:
|
||||
Problem.objects.filter(id=problem_id).delete()
|
||||
ProblemParagraphMapping.objects.filter(paragraph_id=paragraph_id).delete()
|
||||
delete_problems_and_mappings([paragraph_id])
|
||||
|
||||
update_document_char_length(self.data.get('document_id'))
|
||||
delete_embedding_by_paragraph(paragraph_id)
|
||||
|
|
@ -755,3 +748,18 @@ class ParagraphSerializers(ApiMixin, serializers.Serializer):
|
|||
prompt)
|
||||
except AlreadyQueued as e:
|
||||
raise AppApiException(500, "任务正在执行中,请勿重复下发")
|
||||
|
||||
|
||||
def delete_problems_and_mappings(paragraph_ids):
|
||||
problem_paragraph_mappings = ProblemParagraphMapping.objects.filter(paragraph_id__in=paragraph_ids)
|
||||
problem_ids = set(problem_paragraph_mappings.values_list('problem_id', flat=True))
|
||||
|
||||
if problem_ids:
|
||||
problem_paragraph_mappings.delete()
|
||||
remaining_problem_counts = ProblemParagraphMapping.objects.filter(problem_id__in=problem_ids).values(
|
||||
'problem_id').annotate(count=Count('problem_id'))
|
||||
remaining_problem_ids = {pc['problem_id'] for pc in remaining_problem_counts}
|
||||
problem_ids_to_delete = problem_ids - remaining_problem_ids
|
||||
Problem.objects.filter(id__in=problem_ids_to_delete).delete()
|
||||
else:
|
||||
problem_paragraph_mappings.delete()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
@date:2024/8/19 14:13
|
||||
@desc:
|
||||
"""
|
||||
import datetime
|
||||
import logging
|
||||
import traceback
|
||||
from typing import List
|
||||
|
|
@ -17,7 +16,7 @@ from django.db.models import QuerySet
|
|||
from common.config.embedding_config import ModelManage
|
||||
from common.event import ListenerManagement, UpdateProblemArgs, UpdateEmbeddingDatasetIdArgs, \
|
||||
UpdateEmbeddingDocumentIdArgs
|
||||
from dataset.models import Document, Status, TaskType, State
|
||||
from dataset.models import Document, TaskType, State
|
||||
from ops import celery_app
|
||||
from setting.models import Model
|
||||
from setting.models_provider import get_model
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 4.2.15 on 2024-10-15 14:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
sql = """
|
||||
UPDATE "public"."model"
|
||||
SET "model_params_form" = '[{"attrs": {"max": 1, "min": 0.1, "step": 0.01, "precision": 2, "show-input": true, "show-input-controls": false}, "field": "temperature", "label": {"attrs": {"tooltip": "较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定"}, "label": "温度", "input_type": "TooltipLabel", "props_info": {}}, "required": true, "input_type": "Slider", "props_info": {}, "trigger_type": "OPTION_LIST", "default_value": 0.5, "relation_show_field_dict": {}, "relation_trigger_field_dict": {}}, {"attrs": {"max": 100000, "min": 1, "step": 1, "precision": 0, "show-input": true, "show-input-controls": false}, "field": "max_tokens", "label": {"attrs": {"tooltip": "指定模型可生成的最大token个数"}, "label": "输出最大Tokens", "input_type": "TooltipLabel", "props_info": {}}, "required": true, "input_type": "Slider", "props_info": {}, "trigger_type": "OPTION_LIST", "default_value": 4096, "relation_show_field_dict": {}, "relation_trigger_field_dict": {}}]'
|
||||
WHERE jsonb_array_length(model_params_form)=0
|
||||
"""
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('setting', '0008_modelparam'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunSQL(sql)
|
||||
]
|
||||
|
|
@ -79,7 +79,6 @@ class ModelSerializer(serializers.Serializer):
|
|||
|
||||
create_user = serializers.CharField(required=False, error_messages=ErrMessage.char("创建者"))
|
||||
|
||||
|
||||
def list(self, with_valid):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
|
|
@ -92,7 +91,8 @@ class ModelSerializer(serializers.Serializer):
|
|||
model_query_set = QuerySet(Model).filter(Q(user_id=create_user))
|
||||
# 当前用户能查看其他人的模型,只能查看公开的
|
||||
else:
|
||||
model_query_set = QuerySet(Model).filter((Q(user_id=self.data.get('create_user')) & Q(permission_type='PUBLIC')))
|
||||
model_query_set = QuerySet(Model).filter(
|
||||
(Q(user_id=self.data.get('create_user')) & Q(permission_type='PUBLIC')))
|
||||
else:
|
||||
model_query_set = QuerySet(Model).filter((Q(user_id=user_id) | Q(permission_type='PUBLIC')))
|
||||
query_params = {}
|
||||
|
|
@ -107,11 +107,11 @@ class ModelSerializer(serializers.Serializer):
|
|||
if self.data.get('permission_type') is not None:
|
||||
query_params['permission_type'] = self.data.get('permission_type')
|
||||
|
||||
|
||||
return [
|
||||
{'id': str(model.id), 'provider': model.provider, 'name': model.name, 'model_type': model.model_type,
|
||||
'model_name': model.model_name, 'status': model.status, 'meta': model.meta,
|
||||
'permission_type': model.permission_type, 'user_id': model.user_id, 'username': model.user.username} for model in
|
||||
'permission_type': model.permission_type, 'user_id': model.user_id, 'username': model.user.username}
|
||||
for model in
|
||||
model_query_set.filter(**query_params).order_by("-create_time")]
|
||||
|
||||
class Edit(serializers.Serializer):
|
||||
|
|
@ -243,14 +243,7 @@ class ModelSerializer(serializers.Serializer):
|
|||
self.is_valid(raise_exception=True)
|
||||
model_id = self.data.get('id')
|
||||
model = QuerySet(Model).filter(id=model_id).first()
|
||||
credential = get_model_credential(model.provider, model.model_type, model.model_name)
|
||||
# 已经保存过的模型参数表单
|
||||
if model.model_params_form is not None and len(model.model_params_form) > 0:
|
||||
return model.model_params_form
|
||||
# 没有保存过的LLM类型的
|
||||
if credential.get_model_params_setting_form(model.model_name) is not None:
|
||||
return credential.get_model_params_setting_form(model.model_name).to_form_list()
|
||||
# 其他的
|
||||
return model.model_params_form
|
||||
|
||||
class ModelParamsForm(serializers.Serializer):
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
@desc:
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from smartdoc.const import CONFIG, PROJECT_DIR
|
||||
|
||||
|
|
@ -34,6 +35,11 @@ CELERY_TASK_SOFT_TIME_LIMIT = 3600
|
|||
CELERY_WORKER_CANCEL_LONG_RUNNING_TASKS_ON_CONNECTION_LOSS = True
|
||||
CELERY_ACKS_LATE = True
|
||||
celery_once_path = os.path.join(celery_data_dir, "celery_once")
|
||||
try:
|
||||
if os.path.exists(celery_once_path) and os.path.isdir(celery_once_path):
|
||||
shutil.rmtree(celery_once_path)
|
||||
except Exception as e:
|
||||
pass
|
||||
CELERY_ONCE = {
|
||||
'backend': 'celery_once.backends.File',
|
||||
'settings': {'location': celery_once_path}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p --max_old_space_size=4096 type-check build-only",
|
||||
"build": "set NODE_OPTIONS=--max_old_space_size=4096 && run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "vitest",
|
||||
"build-only": "vite build",
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 52 KiB |
|
|
@ -23,8 +23,9 @@ interface ApplicationFormType {
|
|||
tts_type?: string
|
||||
}
|
||||
interface Chunk {
|
||||
real_node_id: string
|
||||
chat_id: string
|
||||
id: string
|
||||
chat_record_id: string
|
||||
content: string
|
||||
node_id: string
|
||||
up_node_id: string
|
||||
|
|
@ -32,13 +33,20 @@ interface Chunk {
|
|||
node_is_end: boolean
|
||||
node_type: string
|
||||
view_type: string
|
||||
runtime_node_id: string
|
||||
child_node: any
|
||||
}
|
||||
interface chatType {
|
||||
id: string
|
||||
problem_text: string
|
||||
answer_text: string
|
||||
buffer: Array<String>
|
||||
answer_text_list: Array<string>
|
||||
answer_text_list: Array<{
|
||||
content: string
|
||||
chat_record_id?: string
|
||||
runtime_node_id?: string
|
||||
child_node?: any
|
||||
}>
|
||||
/**
|
||||
* 是否写入结束
|
||||
*/
|
||||
|
|
@ -92,15 +100,24 @@ export class ChatRecordManage {
|
|||
this.write_ed = false
|
||||
this.node_list = []
|
||||
}
|
||||
append_answer(chunk_answer: string, index?: number) {
|
||||
this.chat.answer_text_list[index != undefined ? index : this.chat.answer_text_list.length - 1] =
|
||||
this.chat.answer_text_list[
|
||||
index !== undefined ? index : this.chat.answer_text_list.length - 1
|
||||
]
|
||||
? this.chat.answer_text_list[
|
||||
index !== undefined ? index : this.chat.answer_text_list.length - 1
|
||||
] + chunk_answer
|
||||
: chunk_answer
|
||||
append_answer(
|
||||
chunk_answer: string,
|
||||
index?: number,
|
||||
chat_record_id?: string,
|
||||
runtime_node_id?: string,
|
||||
child_node?: any
|
||||
) {
|
||||
const set_index = index != undefined ? index : this.chat.answer_text_list.length - 1
|
||||
const content = this.chat.answer_text_list[set_index]
|
||||
? this.chat.answer_text_list[set_index].content + chunk_answer
|
||||
: chunk_answer
|
||||
this.chat.answer_text_list[set_index] = {
|
||||
content: content,
|
||||
chat_record_id,
|
||||
runtime_node_id,
|
||||
child_node
|
||||
}
|
||||
|
||||
this.chat.answer_text = this.chat.answer_text + chunk_answer
|
||||
}
|
||||
|
||||
|
|
@ -127,14 +144,22 @@ export class ChatRecordManage {
|
|||
run_node.view_type == 'single_view' ||
|
||||
(run_node.view_type == 'many_view' && current_up_node.view_type == 'single_view')
|
||||
) {
|
||||
const none_index = this.chat.answer_text_list.indexOf('')
|
||||
const none_index = this.findIndex(
|
||||
this.chat.answer_text_list,
|
||||
(item) => item.content == '',
|
||||
'index'
|
||||
)
|
||||
if (none_index > -1) {
|
||||
answer_text_list_index = none_index
|
||||
} else {
|
||||
answer_text_list_index = this.chat.answer_text_list.length
|
||||
}
|
||||
} else {
|
||||
const none_index = this.chat.answer_text_list.indexOf('')
|
||||
const none_index = this.findIndex(
|
||||
this.chat.answer_text_list,
|
||||
(item) => item.content === '',
|
||||
'index'
|
||||
)
|
||||
if (none_index > -1) {
|
||||
answer_text_list_index = none_index
|
||||
} else {
|
||||
|
|
@ -152,6 +177,19 @@ export class ChatRecordManage {
|
|||
}
|
||||
return undefined
|
||||
}
|
||||
findIndex<T>(array: Array<T>, find: (item: T) => boolean, type: 'last' | 'index') {
|
||||
let set_index = -1
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
const element = array[index]
|
||||
if (find(element)) {
|
||||
set_index = index
|
||||
if (type == 'index') {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return set_index
|
||||
}
|
||||
closeInterval() {
|
||||
this.chat.write_ed = true
|
||||
this.write_ed = true
|
||||
|
|
@ -161,7 +199,11 @@ export class ChatRecordManage {
|
|||
if (this.id) {
|
||||
clearInterval(this.id)
|
||||
}
|
||||
const last_index = this.chat.answer_text_list.lastIndexOf('')
|
||||
const last_index = this.findIndex(
|
||||
this.chat.answer_text_list,
|
||||
(item) => item.content == '',
|
||||
'last'
|
||||
)
|
||||
if (last_index > 0) {
|
||||
this.chat.answer_text_list.splice(last_index, 1)
|
||||
}
|
||||
|
|
@ -193,19 +235,29 @@ export class ChatRecordManage {
|
|||
)
|
||||
this.append_answer(
|
||||
(divider_content ? divider_content.splice(0).join('') : '') + context.join(''),
|
||||
answer_text_list_index
|
||||
answer_text_list_index,
|
||||
current_node.chat_record_id,
|
||||
current_node.runtime_node_id,
|
||||
current_node.child_node
|
||||
)
|
||||
} else if (this.is_close) {
|
||||
while (true) {
|
||||
const node_info = this.get_run_node()
|
||||
|
||||
if (node_info == undefined) {
|
||||
break
|
||||
}
|
||||
this.append_answer(
|
||||
(node_info.divider_content ? node_info.divider_content.splice(0).join('') : '') +
|
||||
node_info.current_node.buffer.splice(0).join(''),
|
||||
node_info.answer_text_list_index
|
||||
node_info.answer_text_list_index,
|
||||
current_node.chat_record_id,
|
||||
current_node.runtime_node_id,
|
||||
current_node.child_node
|
||||
)
|
||||
if (node_info.current_node.buffer.length == 0) {
|
||||
node_info.current_node.is_end = true
|
||||
}
|
||||
}
|
||||
this.closeInterval()
|
||||
} else {
|
||||
|
|
@ -213,7 +265,10 @@ export class ChatRecordManage {
|
|||
if (s !== undefined) {
|
||||
this.append_answer(
|
||||
(divider_content ? divider_content.splice(0).join('') : '') + s,
|
||||
answer_text_list_index
|
||||
answer_text_list_index,
|
||||
current_node.chat_record_id,
|
||||
current_node.runtime_node_id,
|
||||
current_node.child_node
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -235,16 +290,18 @@ export class ChatRecordManage {
|
|||
this.is_stop = false
|
||||
}
|
||||
appendChunk(chunk: Chunk) {
|
||||
let n = this.node_list.find(
|
||||
(item) => item.node_id == chunk.node_id && item.up_node_id === chunk.up_node_id
|
||||
)
|
||||
let n = this.node_list.find((item) => item.real_node_id == chunk.real_node_id)
|
||||
if (n) {
|
||||
n.buffer.push(...chunk.content)
|
||||
} else {
|
||||
n = {
|
||||
buffer: [...chunk.content],
|
||||
real_node_id: chunk.real_node_id,
|
||||
node_id: chunk.node_id,
|
||||
chat_record_id: chunk.chat_record_id,
|
||||
up_node_id: chunk.up_node_id,
|
||||
runtime_node_id: chunk.runtime_node_id,
|
||||
child_node: chunk.child_node,
|
||||
node_type: chunk.node_type,
|
||||
index: this.node_list.length,
|
||||
view_type: chunk.view_type,
|
||||
|
|
@ -257,9 +314,12 @@ export class ChatRecordManage {
|
|||
}
|
||||
}
|
||||
append(answer_text_block: string) {
|
||||
const index =this.chat.answer_text_list.indexOf("")
|
||||
this.chat.answer_text_list[index]=answer_text_block
|
||||
|
||||
let set_index = this.findIndex(
|
||||
this.chat.answer_text_list,
|
||||
(item) => item.content == '',
|
||||
'index'
|
||||
)
|
||||
this.chat.answer_text_list[set_index] = { content: answer_text_block }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@
|
|||
<template v-if="item.type === WorkflowType.FormNode">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">
|
||||
参数输入<span style="color: #f54a45">{{
|
||||
参数输出<span style="color: #f54a45">{{
|
||||
item.is_submit ? '' : '(用户未提交)'
|
||||
}}</span>
|
||||
</h5>
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@
|
|||
<el-input v-model="detail.padding_problem_text" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="引用分段">
|
||||
<template v-for="(item, index) in detail.paragraph_list" :key="index">
|
||||
<ParagraphCard :data="item" :index="index" />
|
||||
</template>
|
||||
<div v-if="detail.paragraph_list.length > 0">
|
||||
<template v-for="(item, index) in detail.paragraph_list" :key="index">
|
||||
<ParagraphCard :data="item" :index="index" />
|
||||
</template>
|
||||
</div>
|
||||
<span v-else> - </span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
</template>
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<el-text class="flex align-center" style="width: 70%">
|
||||
<el-text class="flex align-center" style="width: 50%">
|
||||
<img :src="getImgUrl(data?.document_name?.trim())" alt="" width="20" class="mr-4" />
|
||||
|
||||
<template v-if="meta?.source_url">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="item-content mb-16 lighter">
|
||||
<template v-for="(answer_text, index) in chatRecord.answer_text_list" :key="index">
|
||||
<template v-for="(answer_text, index) in answer_text_list" :key="index">
|
||||
<div class="avatar">
|
||||
<img v-if="application.avatar" :src="application.avatar" height="32px" width="32px" />
|
||||
<LogoIcon v-else height="32px" width="32px" />
|
||||
|
|
@ -9,14 +9,18 @@
|
|||
<el-card shadow="always" class="dialog-card mb-8">
|
||||
<MdRenderer
|
||||
v-if="
|
||||
(chatRecord.write_ed === undefined || chatRecord.write_ed === true) && !answer_text
|
||||
(chatRecord.write_ed === undefined || chatRecord.write_ed === true) &&
|
||||
!answer_text.content
|
||||
"
|
||||
source=" 抱歉,没有查找到相关内容,请重新描述您的问题或提供更多信息。"
|
||||
></MdRenderer>
|
||||
<MdRenderer
|
||||
:chat_record_id="answer_text.chat_record_id"
|
||||
:child_node="answer_text.child_node"
|
||||
:runtime_node_id="answer_text.runtime_node_id"
|
||||
:loading="loading"
|
||||
v-else-if="answer_text"
|
||||
:source="answer_text"
|
||||
v-else-if="answer_text.content"
|
||||
:source="answer_text.content"
|
||||
:send-message="chatMessage"
|
||||
></MdRenderer>
|
||||
<span v-else-if="chatRecord.is_stop" shadow="always" class="dialog-card">
|
||||
|
|
@ -36,7 +40,8 @@
|
|||
<OperationButton
|
||||
:type="type"
|
||||
:application="application"
|
||||
:chat-record="chatRecord"
|
||||
:chatRecord="chatRecord"
|
||||
@update:chatRecord="(event: any) => emit('update:chatRecord', event)"
|
||||
:loading="loading"
|
||||
:start-chat="startChat"
|
||||
:stop-chat="stopChat"
|
||||
|
|
@ -50,6 +55,7 @@ import KnowledgeSource from '@/components/ai-chat/KnowledgeSource.vue'
|
|||
import MdRenderer from '@/components/markdown/MdRenderer.vue'
|
||||
import OperationButton from '@/components/ai-chat/component/operation-button/index.vue'
|
||||
import { type chatType } from '@/api/type/application'
|
||||
import { computed } from 'vue'
|
||||
const props = defineProps<{
|
||||
chatRecord: chatType
|
||||
application: any
|
||||
|
|
@ -59,6 +65,8 @@ const props = defineProps<{
|
|||
type: 'log' | 'ai-chat' | 'debug-ai-chat'
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:chatRecord'])
|
||||
|
||||
const chatMessage = (question: string, type: 'old' | 'new', other_params_data?: any) => {
|
||||
if (type === 'old') {
|
||||
add_answer_text_list(props.chatRecord.answer_text_list)
|
||||
|
|
@ -68,9 +76,17 @@ const chatMessage = (question: string, type: 'old' | 'new', other_params_data?:
|
|||
props.sendMessage(question, other_params_data)
|
||||
}
|
||||
}
|
||||
const add_answer_text_list = (answer_text_list: Array<string>) => {
|
||||
answer_text_list.push('')
|
||||
const add_answer_text_list = (answer_text_list: Array<any>) => {
|
||||
answer_text_list.push({ content: '' })
|
||||
}
|
||||
const answer_text_list = computed(() => {
|
||||
return props.chatRecord.answer_text_list.map((item) => {
|
||||
if (typeof item == 'string') {
|
||||
return { content: item }
|
||||
}
|
||||
return item
|
||||
})
|
||||
})
|
||||
|
||||
function showSource(row: any) {
|
||||
if (props.type === 'log') {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,8 @@
|
|||
>
|
||||
<el-tooltip effect="dark" placement="top" popper-class="upload-tooltip-width">
|
||||
<template #content>
|
||||
<div class="break-all pre-wrap">上传文件:最多{{
|
||||
<div class="break-all pre-wrap">
|
||||
上传文件:最多{{
|
||||
props.applicationDetails.file_upload_setting.maxFiles
|
||||
}}个,每个文件限制
|
||||
{{ props.applicationDetails.file_upload_setting.fileLimit }}MB<br />文件类型:{{
|
||||
|
|
@ -93,7 +94,7 @@
|
|||
}}
|
||||
</div>
|
||||
</template>
|
||||
<el-button text>
|
||||
<el-button text :disabled="checkMaxFilesLimit()" class="mt-4">
|
||||
<el-icon><Paperclip /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
|
@ -223,6 +224,13 @@ const getAcceptList = () => {
|
|||
return accepts.map((ext: any) => '.' + ext).join(',')
|
||||
}
|
||||
|
||||
const checkMaxFilesLimit = () => {
|
||||
return (
|
||||
props.applicationDetails.file_upload_setting.maxFiles <=
|
||||
uploadImageList.value.length + uploadDocumentList.value.length
|
||||
)
|
||||
}
|
||||
|
||||
const uploadFile = async (file: any, fileList: any) => {
|
||||
const { maxFiles, fileLimit } = props.applicationDetails.file_upload_setting
|
||||
// 单次上传文件数量限制
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
</el-tooltip>
|
||||
<el-divider direction="vertical" />
|
||||
<el-tooltip
|
||||
v-if="data.improve_paragraph_id_list.length === 0"
|
||||
v-if="buttonData.improve_paragraph_id_list.length === 0"
|
||||
effect="dark"
|
||||
content="修改内容"
|
||||
placement="top"
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { copyClick } from '@/utils/clipboard'
|
||||
import EditContentDialog from '@/views/log/component/EditContentDialog.vue'
|
||||
import EditMarkDialog from '@/views/log/component/EditMarkDialog.vue'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<LogOperationButton
|
||||
v-if="type === 'log'"
|
||||
v-bind:data="chatRecord"
|
||||
@update:data="(event: any) => emit('update:chatRecord', event)"
|
||||
:applicationId="application.id"
|
||||
:tts="application.tts_model_enable"
|
||||
:tts_type="application.tts_type"
|
||||
|
|
@ -54,5 +55,6 @@ defineProps<{
|
|||
stopChat: (chat_record: any) => void
|
||||
regenerationChart: (chat_record: any) => void
|
||||
}>()
|
||||
const emit = defineEmits(['update:chatRecord'])
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
<AnswerContent
|
||||
:application="applicationDetails"
|
||||
:loading="loading"
|
||||
:chat-record="item"
|
||||
v-model:chat-record="chatList[index]"
|
||||
:type="type"
|
||||
:send-message="sendMessage"
|
||||
:chat-management="ChatManagement"
|
||||
|
|
@ -222,9 +222,10 @@ const getWrite = (chat: any, reader: any, stream: boolean) => {
|
|||
for (const index in split) {
|
||||
const chunk = JSON?.parse(split[index].replace('data:', ''))
|
||||
chat.chat_id = chunk.chat_id
|
||||
chat.record_id = chunk.id
|
||||
ChatManagement.appendChunk(chat.id, chunk)
|
||||
|
||||
chat.record_id = chunk.chat_record_id
|
||||
if (!chunk.is_end) {
|
||||
ChatManagement.appendChunk(chat.id, chunk)
|
||||
}
|
||||
if (chunk.is_end) {
|
||||
// 流处理成功 返回成功回调
|
||||
return Promise.resolve()
|
||||
|
|
@ -278,7 +279,7 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean, other_para
|
|||
id: randomId(),
|
||||
problem_text: problem ? problem : inputValue.value.trim(),
|
||||
answer_text: '',
|
||||
answer_text_list: [''],
|
||||
answer_text_list: [{ content: '' }],
|
||||
buffer: [],
|
||||
write_ed: false,
|
||||
is_stop: false,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
v-for="(option, $index) in formValue.option_list"
|
||||
:key="$index"
|
||||
:gutter="10"
|
||||
class="mb-8"
|
||||
>
|
||||
<el-col :span="10"
|
||||
><div class="grid-content ep-bg-purple" />
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
v-for="(option, $index) in formValue.option_list"
|
||||
:key="$index"
|
||||
:gutter="10"
|
||||
class="mb-8"
|
||||
>
|
||||
<el-col :span="10"
|
||||
><div class="grid-content ep-bg-purple" />
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
v-for="(option, $index) in formValue.option_list"
|
||||
:key="$index"
|
||||
:gutter="10"
|
||||
class="mb-8"
|
||||
>
|
||||
<el-col :span="10"
|
||||
><div class="grid-content ep-bg-purple" />
|
||||
|
|
@ -93,7 +94,7 @@ const formField = computed<FormField>(() => {
|
|||
})
|
||||
const getData = () => {
|
||||
return {
|
||||
input_type: 'RadioCard',
|
||||
input_type: 'RadioRow',
|
||||
attrs: {},
|
||||
default_value: formValue.value.default_value,
|
||||
text_field: 'label',
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
v-for="(option, $index) in formValue.option_list"
|
||||
:key="$index"
|
||||
:gutter="10"
|
||||
class="mb-8"
|
||||
>
|
||||
<el-col :span="10"
|
||||
><div class="grid-content ep-bg-purple" />
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
:key="item.value"
|
||||
class="item"
|
||||
shadow="never"
|
||||
:class="[modelValue == item[valueField] ? 'active' : '']"
|
||||
@click="selected(item[valueField])"
|
||||
:class="[inputDisabled ? 'is-disabled' : '', modelValue == item[valueField] ? 'active' : '']"
|
||||
@click="inputDisabled ? () => {} : selected(item[valueField])"
|
||||
>
|
||||
{{ item[textField] }}
|
||||
</el-card>
|
||||
|
|
@ -15,6 +15,9 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import type { FormField } from '@/components/dynamics-form/type'
|
||||
import { useFormDisabled } from 'element-plus'
|
||||
const inputDisabled = useFormDisabled()
|
||||
|
||||
const props = defineProps<{
|
||||
formValue?: any
|
||||
formfieldList?: Array<FormField>
|
||||
|
|
@ -24,6 +27,7 @@ const props = defineProps<{
|
|||
view?: boolean
|
||||
// 选中的值
|
||||
modelValue?: any
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
const selected = (activeValue: string | number) => {
|
||||
|
|
@ -66,6 +70,16 @@ const option_list = computed(() => {
|
|||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.is-disabled {
|
||||
border: 1px solid var(--el-card-border-color);
|
||||
background-color: var(--el-fill-color-light);
|
||||
color: var(--el-text-color-placeholder);
|
||||
cursor: not-allowed;
|
||||
&:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
v-for="item in option_list"
|
||||
:key="item.value"
|
||||
class="item"
|
||||
:class="[modelValue == item[valueField] ? 'active' : '']"
|
||||
:class="[inputDisabled ? 'is-disabled' : '', modelValue == item[valueField] ? 'active' : '']"
|
||||
@click="selected(item[valueField])"
|
||||
>
|
||||
{{ item[textField] }}
|
||||
|
|
@ -14,6 +14,8 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import type { FormField } from '@/components/dynamics-form/type'
|
||||
import { useFormDisabled } from 'element-plus'
|
||||
const inputDisabled = useFormDisabled()
|
||||
const props = defineProps<{
|
||||
formValue?: any
|
||||
formfieldList?: Array<FormField>
|
||||
|
|
@ -54,7 +56,15 @@ const option_list = computed(() => {
|
|||
padding: 3px 4px;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
|
||||
.is-disabled {
|
||||
border: 1px solid var(--el-card-border-color);
|
||||
background-color: var(--el-fill-color-light);
|
||||
color: var(--el-text-color-placeholder);
|
||||
cursor: not-allowed;
|
||||
&:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
border-radius: 4px;
|
||||
background: var(--el-color-primary-light-9);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
v-model="form_data"
|
||||
:model="form_data"
|
||||
></DynamicsForm>
|
||||
<el-button :type="is_submit ? 'info' : 'primary'" :disabled="is_submit||loading" @click="submit"
|
||||
<el-button
|
||||
:type="is_submit ? 'info' : 'primary'"
|
||||
:disabled="is_submit || loading"
|
||||
@click="submit"
|
||||
>提交</el-button
|
||||
>
|
||||
</div>
|
||||
|
|
@ -18,13 +21,19 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
const props = withDefaults(defineProps<{
|
||||
form_setting: string
|
||||
loading?:boolean
|
||||
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||
}>(),{
|
||||
loading:false
|
||||
})
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
form_setting: string
|
||||
loading?: boolean
|
||||
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||
child_node?: any
|
||||
chat_record_id?: string
|
||||
runtime_node_id?: string
|
||||
}>(),
|
||||
{
|
||||
loading: false
|
||||
}
|
||||
)
|
||||
const form_setting_data = computed(() => {
|
||||
if (props.form_setting) {
|
||||
return JSON.parse(props.form_setting)
|
||||
|
|
@ -69,11 +78,11 @@ const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
|
|||
const submit = () => {
|
||||
dynamicsFormRef.value?.validate().then(() => {
|
||||
_submit.value = true
|
||||
const setting = JSON.parse(props.form_setting)
|
||||
if (props.sendMessage) {
|
||||
props.sendMessage('', 'old', {
|
||||
runtime_node_id: setting.runtime_node_id,
|
||||
chat_record_id: setting.chat_record_id,
|
||||
child_node: props.child_node,
|
||||
runtime_node_id: props.runtime_node_id,
|
||||
chat_record_id: props.chat_record_id,
|
||||
node_data: form_data.value
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
>
|
||||
<template #defFooters>
|
||||
<el-button text type="info" @click="openDialog">
|
||||
<AppIcon iconName="app-magnify" style="font-size: 16px"></AppIcon>
|
||||
<AppIcon class="color-secondary" iconName="app-magnify" style="font-size: 16px"></AppIcon>
|
||||
</el-button>
|
||||
</template>
|
||||
</MdEditor>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
:option="item.content"
|
||||
></EchartsRander>
|
||||
<FormRander
|
||||
:chat_record_id="chat_record_id"
|
||||
:runtime_node_id="runtime_node_id"
|
||||
:child_node="child_node"
|
||||
:loading="loading"
|
||||
:send-message="sendMessage"
|
||||
v-else-if="item.type === 'form_rander'"
|
||||
|
|
@ -64,6 +67,9 @@ const props = withDefaults(
|
|||
source?: string
|
||||
inner_suffix?: boolean
|
||||
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||
child_node?: any
|
||||
chat_record_id?: string
|
||||
runtime_node_id?: string
|
||||
loading?: boolean
|
||||
}>(),
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default {
|
|||
jump_tip: 'Jumping to the authentication source page for authentication',
|
||||
jump: 'Jump',
|
||||
oauth2: {
|
||||
title: 'OAUTH2 Settings',
|
||||
title: 'OAuth2 Settings',
|
||||
authEndpoint: 'Auth Endpoint',
|
||||
authEndpointPlaceholder: 'Please enter Auth Endpoint',
|
||||
tokenEndpoint: 'Token Endpoint',
|
||||
|
|
@ -82,7 +82,7 @@ export default {
|
|||
redirectUrlPlaceholder: 'Please enter Redirect URL',
|
||||
filedMapping: 'Field Mapping',
|
||||
filedMappingPlaceholder: 'Please enter Field Mapping',
|
||||
enableAuthentication: 'Enable OAUTH2 Authentication',
|
||||
enableAuthentication: 'Enable OAuth2 Authentication',
|
||||
save: 'Save',
|
||||
saveSuccess: 'Save Success'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default {
|
|||
jump_tip: '即将跳转至认证源页面进行认证',
|
||||
jump: '跳转',
|
||||
oauth2: {
|
||||
title: 'OAUTH2 设置',
|
||||
title: 'OAuth2 设置',
|
||||
authEndpoint: '授权端地址',
|
||||
authEndpointPlaceholder: '请输入授权端地址',
|
||||
tokenEndpoint: 'Token 端地址',
|
||||
|
|
@ -82,7 +82,7 @@ export default {
|
|||
redirectUrlPlaceholder: '请输入回调地址',
|
||||
filedMapping: '字段映射',
|
||||
filedMappingPlaceholder: '请输入字段映射',
|
||||
enableAuthentication: '启用 OAUTH2 认证',
|
||||
enableAuthentication: '启用 OAuth2 认证',
|
||||
save: '保存',
|
||||
saveSuccess: '保存成功'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,6 +256,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
.el-select__placeholder {
|
||||
font-weight: 400;
|
||||
}
|
||||
.el-select__placeholder.is-transparent {
|
||||
color: var(--app-input-color-placeholder);
|
||||
font-weight: 400;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
.md-editor {
|
||||
font-weight: 400;
|
||||
}
|
||||
.md-editor-preview {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div v-show="show" class="workflow-dropdown-menu border border-r-4">
|
||||
<el-tabs v-model="activeName" class="workflow-dropdown-tabs">
|
||||
<div style="display: flex; width: 100%; justify-content: center">
|
||||
<div style="display: flex; width: 100%; justify-content: center" class="mb-4">
|
||||
<el-input v-model="search_text" style="width: 240px" placeholder="按名称搜索">
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon"><search /></el-icon>
|
||||
|
|
@ -61,32 +61,37 @@
|
|||
</el-tab-pane>
|
||||
<el-tab-pane label="应用" name="application">
|
||||
<el-scrollbar height="400">
|
||||
<template v-for="(item, index) in filter_application_list" :key="index">
|
||||
<div
|
||||
class="workflow-dropdown-item cursor flex p-8-12"
|
||||
@click.stop="clickNodes(applicationNode, item, 'application')"
|
||||
@mousedown.stop="onmousedown(applicationNode, item, 'application')"
|
||||
>
|
||||
<component
|
||||
:is="iconComponent(`application-node-icon`)"
|
||||
class="mr-8 mt-4"
|
||||
:size="32"
|
||||
:item="item"
|
||||
/>
|
||||
<div class="pre-wrap" style="width: 60%">
|
||||
<auto-tooltip :content="item.name" style="width: 80%" class="lighter">
|
||||
{{ item.name }}
|
||||
</auto-tooltip>
|
||||
<el-text type="info" size="small" style="width: 80%">{{ item.desc }}</el-text>
|
||||
<div v-if="filter_application_list.length > 0">
|
||||
<template v-for="(item, index) in filter_application_list" :key="index">
|
||||
<div
|
||||
class="workflow-dropdown-item cursor flex p-8-12"
|
||||
@click.stop="clickNodes(applicationNode, item, 'application')"
|
||||
@mousedown.stop="onmousedown(applicationNode, item, 'application')"
|
||||
>
|
||||
<component
|
||||
:is="iconComponent(`application-node-icon`)"
|
||||
class="mr-8 mt-4"
|
||||
:size="32"
|
||||
:item="item"
|
||||
/>
|
||||
<div class="pre-wrap" style="width: 60%">
|
||||
<auto-tooltip :content="item.name" style="width: 80%" class="lighter">
|
||||
{{ item.name }}
|
||||
</auto-tooltip>
|
||||
<el-text type="info" size="small" style="width: 80%">{{ item.desc }}</el-text>
|
||||
</div>
|
||||
<div class="status-tag" style="margin-left: auto">
|
||||
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px"
|
||||
>高级编排</el-tag
|
||||
>
|
||||
<el-tag class="blue-tag" v-else style="height: 22px">简单配置</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-tag" style="margin-left: auto">
|
||||
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px"
|
||||
>高级编排</el-tag
|
||||
>
|
||||
<el-tag class="blue-tag" v-else style="height: 22px">简单配置</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else class="ml-16 mt-8">
|
||||
<el-text type="info">没有找到相关结果</el-text>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
|
|
|||
|
|
@ -409,6 +409,7 @@
|
|||
link
|
||||
@click="openTTSParamSettingDialog"
|
||||
:disabled="!applicationForm.tts_model_id"
|
||||
class="mr-8"
|
||||
>
|
||||
<el-icon class="mr-4"><Setting /></el-icon>
|
||||
设置
|
||||
|
|
@ -424,6 +425,7 @@
|
|||
<el-radio-group
|
||||
v-model="applicationForm.tts_type"
|
||||
v-show="applicationForm.tts_model_enable"
|
||||
class="mb-8"
|
||||
>
|
||||
<el-radio value="BROWSER">浏览器播放(免费)</el-radio>
|
||||
<el-radio value="TTS">TTS模型</el-radio>
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch, onMounted } from 'vue'
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import authApi from '@/api/auth-setting'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { t } from '@/locales'
|
||||
|
|
@ -86,7 +86,7 @@ import { MsgSuccess } from '@/utils/message'
|
|||
|
||||
const form = ref<any>({
|
||||
id: '',
|
||||
auth_type: 'OAUTH2',
|
||||
auth_type: 'OAuth2',
|
||||
config_data: {
|
||||
authEndpoint: '',
|
||||
tokenEndpoint: '',
|
||||
|
|
@ -11,15 +11,15 @@
|
|||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import LDAP from './component/LDAP.vue'
|
||||
import CAS from './component/CAS.vue'
|
||||
import OIDC from './component/OIDC.vue'
|
||||
import SCAN from './component/SCAN.vue'
|
||||
import OAuth2 from './component/OAuth2.vue'
|
||||
import { t } from '@/locales'
|
||||
import useStore from '@/stores'
|
||||
import OAUTH2 from '@/views/authentication/component/OAUTH2.vue'
|
||||
|
||||
const { user } = useStore()
|
||||
const router = useRouter()
|
||||
|
|
@ -43,8 +43,8 @@ const tabList = [
|
|||
},
|
||||
{
|
||||
label: t('login.oauth2.title'),
|
||||
name: 'OAUTH2',
|
||||
component: OAUTH2
|
||||
name: 'OAuth2',
|
||||
component: OAuth2
|
||||
},
|
||||
{
|
||||
label: '扫码登录',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
title="生成关联问题"
|
||||
title="生成问题"
|
||||
v-model="dialogVisible"
|
||||
width="600"
|
||||
class="select-dataset-dialog"
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
>
|
||||
<template #header="{ titleId, titleClass }">
|
||||
<div class="my-header flex">
|
||||
<h4 :id="titleId" :class="titleClass">生成关联问题</h4>
|
||||
<h4 :id="titleId" :class="titleClass">生成问题</h4>
|
||||
</div>
|
||||
</template>
|
||||
<div class="content-height">
|
||||
|
|
@ -174,7 +174,7 @@ const submitHandle = async (formEl: FormInstance) => {
|
|||
prompt.save(user.userInfo?.id as string, form.value)
|
||||
const data = { ...form.value, document_id_list: documentIdList.value }
|
||||
documentApi.batchGenerateRelated(id, data).then(() => {
|
||||
MsgSuccess('生成关联问题成功')
|
||||
MsgSuccess('生成问题成功')
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-row :gutter="3" v-for="status in statusTable" :key="status.type">
|
||||
<el-col :span="4">{{ taskTypeMap[status.type] }} </el-col>
|
||||
<el-col :span="4">
|
||||
<div v-for="status in statusTable" :key="status.type">
|
||||
<span> {{ taskTypeMap[status.type] }}:</span>
|
||||
<span>
|
||||
<el-text v-if="status.state === State.SUCCESS || status.state === State.REVOKED">
|
||||
<el-icon class="success"><SuccessFilled /></el-icon>
|
||||
{{ stateMap[status.state](status.type) }}
|
||||
|
|
@ -22,23 +22,22 @@
|
|||
<el-icon class="is-loading primary"><Loading /></el-icon>
|
||||
{{ stateMap[status.state](status.type) }}
|
||||
</el-text>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<span
|
||||
:style="{ color: [State.FAILURE, State.REVOKED].includes(status.state) ? '#F54A45' : '' }"
|
||||
>
|
||||
完成
|
||||
{{
|
||||
Object.keys(status.aggs ? status.aggs : {})
|
||||
.filter((k) => k == State.SUCCESS)
|
||||
.map((k) => status.aggs[k])
|
||||
.reduce((x: any, y: any) => x + y, 0)
|
||||
}}/{{
|
||||
Object.values(status.aggs ? status.aggs : {}).reduce((x: any, y: any) => x + y, 0)
|
||||
}}</span
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="9">
|
||||
</span>
|
||||
<span
|
||||
class="ml-8 lighter"
|
||||
:style="{ color: [State.FAILURE, State.REVOKED].includes(status.state) ? '#F54A45' : '' }"
|
||||
>
|
||||
完成
|
||||
{{
|
||||
Object.keys(status.aggs ? status.aggs : {})
|
||||
.filter((k) => k == State.SUCCESS)
|
||||
.map((k) => status.aggs[k])
|
||||
.reduce((x: any, y: any) => x + y, 0)
|
||||
}}/{{
|
||||
Object.values(status.aggs ? status.aggs : {}).reduce((x: any, y: any) => x + y, 0)
|
||||
}}</span
|
||||
>
|
||||
<el-text type="info" class="ml-4">
|
||||
{{
|
||||
status.time
|
||||
? status.time[status.state == State.REVOKED ? State.REVOKED : State.PENDING]?.substring(
|
||||
|
|
@ -47,8 +46,8 @@
|
|||
)
|
||||
: undefined
|
||||
}}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-text>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
向量化
|
||||
</el-button>
|
||||
<el-button @click="openGenerateDialog()" :disabled="multipleSelection.length === 0">
|
||||
关联问题
|
||||
生成问题
|
||||
</el-button>
|
||||
<el-button @click="openBatchEditDocument" :disabled="multipleSelection.length === 0">
|
||||
设置
|
||||
|
|
@ -636,7 +636,7 @@ function batchGenerateRelated() {
|
|||
}
|
||||
})
|
||||
documentApi.batchGenerateRelated(id, arr, loading).then(() => {
|
||||
MsgSuccess('批量关联问题成功')
|
||||
MsgSuccess('批量生成问题成功')
|
||||
multipleTableRef.value?.clearSelection()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,13 @@
|
|||
class="login-button-circle color-secondary"
|
||||
@click="changeMode(item)"
|
||||
>
|
||||
<span style="font-size: 10px">{{ item }}</span>
|
||||
<span
|
||||
:style="{
|
||||
'font-size': item === 'OAUTH2' ? '8px' : '10px',
|
||||
color: user.themeInfo?.theme
|
||||
}"
|
||||
>{{ item }}</span
|
||||
>
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="item === 'QR_CODE' && loginMode !== item"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
title="生成关联问题"
|
||||
title="生成问题"
|
||||
v-model="dialogVisible"
|
||||
width="600"
|
||||
class="select-dataset-dialog"
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
>
|
||||
<template #header="{ titleId, titleClass }">
|
||||
<div class="my-header flex">
|
||||
<h4 :id="titleId" :class="titleClass">生成关联问题</h4>
|
||||
<h4 :id="titleId" :class="titleClass">生成问题</h4>
|
||||
</div>
|
||||
</template>
|
||||
<div class="content-height">
|
||||
|
|
@ -174,7 +174,7 @@ const submitHandle = async (formEl: FormInstance) => {
|
|||
|
||||
const data = { ...form.value, paragraph_id_list: paragraphIdList.value }
|
||||
paragraphApi.batchGenerateRelated(id, documentId, data).then(() => {
|
||||
MsgSuccess('生成关联问题成功')
|
||||
MsgSuccess('生成问题成功')
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@
|
|||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openGenerateDialog(item)">
|
||||
<el-icon><Connection /></el-icon>
|
||||
生成关联问题</el-dropdown-item
|
||||
生成问题</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="openSelectDocumentDialog(item)">
|
||||
<AppIcon iconName="app-migrate"></AppIcon>
|
||||
|
|
@ -147,7 +147,7 @@
|
|||
|
||||
<div class="mul-operation border-t w-full" v-if="isBatch === true">
|
||||
<el-button :disabled="multipleSelection.length === 0" @click="openGenerateDialog()">
|
||||
生成关联问题
|
||||
生成问题
|
||||
</el-button>
|
||||
<el-button :disabled="multipleSelection.length === 0" @click="openSelectDocumentDialog()">
|
||||
迁移
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ const allChecked: any = ref({
|
|||
if (val) {
|
||||
filterData.value.map((item: any) => {
|
||||
item.operate[TeamEnum.MANAGE] = true
|
||||
item.operate[TeamEnum.USE] = true
|
||||
})
|
||||
} else {
|
||||
filterData.value.map((item: any) => {
|
||||
|
|
@ -113,6 +114,7 @@ const allChecked: any = ref({
|
|||
} else {
|
||||
filterData.value.map((item: any) => {
|
||||
item.operate[TeamEnum.USE] = false
|
||||
item.operate[TeamEnum.MANAGE] = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -144,6 +146,11 @@ function checkedOperateChange(Name: string | number, row: any, e: boolean) {
|
|||
props.data.map((item: any) => {
|
||||
if (item.id === row.id) {
|
||||
item.operate[Name] = e
|
||||
if (Name === TeamEnum.MANAGE && e) {
|
||||
item.operate[TeamEnum.USE] = true
|
||||
} else if (Name === TeamEnum.USE && !e) {
|
||||
item.operate[TeamEnum.MANAGE] = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@ const validate = () => {
|
|||
}
|
||||
return Promise.resolve('')
|
||||
}
|
||||
props.nodeModel.graphModel.eventCenter.on('refresh_incoming_node_field', () => {
|
||||
getIncomingNode(props.nodeModel.id)
|
||||
})
|
||||
defineExpose({ validate })
|
||||
onMounted(() => {
|
||||
options.value = getIncomingNode(props.nodeModel.id)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
<div @mousemove.stop @mousedown.stop @keydown.stop @click.stop>
|
||||
<el-button text @click="showNode = !showNode" class="mr-4">
|
||||
<el-icon class="arrow-icon" :class="showNode ? 'rotate-180' : ''"
|
||||
<el-icon class="arrow-icon color-secondary" :class="showNode ? 'rotate-180' : ''"
|
||||
><ArrowDownBold />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@
|
|||
<el-divider direction="vertical" />
|
||||
<el-button link @click="retract">
|
||||
<el-tooltip class="box-item" effect="dark" content="收起全部节点" placement="top">
|
||||
<AppIcon iconName="app-retract" title="收起全部节点"></AppIcon>
|
||||
<AppIcon style="font-size: 16px" iconName="app-retract" title="收起全部节点"></AppIcon>
|
||||
</el-tooltip>
|
||||
</el-button>
|
||||
<el-button link @click="extend">
|
||||
<el-tooltip class="box-item" effect="dark" content="展开全部节点" placement="top">
|
||||
<AppIcon iconName="app-extend" title="展开全部节点"></AppIcon>
|
||||
<AppIcon style="font-size: 16px" iconName="app-extend" title="展开全部节点"></AppIcon>
|
||||
</el-tooltip>
|
||||
</el-button>
|
||||
<el-button link @click="layout">
|
||||
<el-tooltip class="box-item" effect="dark" content="一键美化" placement="top">
|
||||
<AppIcon iconName="app-beautify" title="一键美化"></AppIcon>
|
||||
<AppIcon style="font-size: 16px" iconName="app-beautify" title="一键美化"></AppIcon>
|
||||
</el-tooltip>
|
||||
</el-button>
|
||||
</el-card>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
<el-table-column prop="default_value" label="默认值">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.default_value" class="ellipsis-1">{{ row.default_value }}</span>
|
||||
<span :title="row.default_value" class="ellipsis-1">{{ getDefaultValue(row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="必填">
|
||||
|
|
@ -126,6 +126,18 @@ function refreshFieldList(data: any, index: any) {
|
|||
props.nodeModel.graphModel.eventCenter.emit('refreshFieldList')
|
||||
}
|
||||
|
||||
const getDefaultValue = (row: any) => {
|
||||
if (row.default_value) {
|
||||
const default_value = row.option_list?.filter((v: any) => row.default_value.indexOf(v.value) > -1)
|
||||
.map((v: any) => v.label).join(',')
|
||||
if (default_value) {
|
||||
return default_value
|
||||
}
|
||||
return row.default_value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
if (!props.nodeModel.properties.user_input_field_list) {
|
||||
if (props.nodeModel.properties.input_field_list) {
|
||||
|
|
|
|||
|
|
@ -1,122 +1,121 @@
|
|||
<template>
|
||||
<NodeContainer :nodeModel="nodeModel">
|
||||
<el-form
|
||||
@submit.prevent
|
||||
:model="form_data"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
label-width="auto"
|
||||
ref="formNodeFormRef"
|
||||
hide-required-asterisk
|
||||
>
|
||||
<el-form-item
|
||||
label="表单输出内容"
|
||||
prop="form_content_format"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请表单输出内容',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
<h5 class="title-decoration-1 mb-8">节点设置</h5>
|
||||
<el-card shadow="never" class="card-never" style="--el-card-padding: 12px">
|
||||
<el-form
|
||||
@submit.prevent
|
||||
:model="form_data"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
label-width="auto"
|
||||
ref="formNodeFormRef"
|
||||
hide-required-asterisk
|
||||
>
|
||||
<template #label>
|
||||
<div class="flex align-center">
|
||||
<div class="mr-4">
|
||||
<span>表单输出内容<span class="danger">*</span></span>
|
||||
</div>
|
||||
<el-tooltip effect="dark" placement="right" popper-class="max-w-200">
|
||||
<template #content>
|
||||
设置执行该节点输出的内容,{{ '{ form }' }}为表单的占位符。
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<MdEditorMagnify
|
||||
title="表单输出内容"
|
||||
v-model="form_data.form_content_format"
|
||||
style="height: 150px"
|
||||
@submitDialog="submitDialog"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="表单配置" @click.prevent>
|
||||
<template #label>
|
||||
<div class="flex-between mb-16">
|
||||
<h5 class="lighter">{{ '表单配置' }}</h5>
|
||||
<el-button link type="primary" @click="openAddFormCollect()">
|
||||
<el-icon class="mr-4">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
添加
|
||||
</el-button>
|
||||
</div></template
|
||||
<el-form-item
|
||||
label="表单输出内容"
|
||||
prop="form_content_format"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请表单输出内容',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
|
||||
<el-table
|
||||
v-if="form_data.form_field_list.length > 0"
|
||||
:data="form_data.form_field_list"
|
||||
class="mb-16"
|
||||
>
|
||||
<el-table-column prop="field" label="参数">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.field" class="ellipsis-1">{{ row.field }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="label" label="显示名称">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.label && row.label.input_type === 'TooltipLabel'">
|
||||
<span :title="row.label.label" class="ellipsis-1">
|
||||
{{ row.label.label }}
|
||||
</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
<span :title="row.label" class="ellipsis-1">
|
||||
{{ row.label }}
|
||||
</span></span
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="组件类型" width="110px">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" class="info-tag">{{
|
||||
input_type_list.find((item) => item.value === row.input_type)?.label
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="default_value" label="默认值">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.default_value" class="ellipsis-1">{{ row.default_value }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="必填">
|
||||
<template #default="{ row }">
|
||||
<div @click.stop>
|
||||
<el-switch disabled size="small" v-model="row.required" />
|
||||
<template #label>
|
||||
<div class="flex align-center">
|
||||
<div class="mr-4">
|
||||
<span>表单输出内容<span class="danger">*</span></span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="left" width="80">
|
||||
<template #default="{ row, $index }">
|
||||
<span class="mr-4">
|
||||
<el-tooltip effect="dark" content="修改" placement="top">
|
||||
<el-button type="primary" text @click.stop="openEditFormCollect(row, $index)">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
<el-tooltip effect="dark" placement="right" popper-class="max-w-200">
|
||||
<template #content>
|
||||
设置执行该节点输出的内容,{{ '{ form }' }}为表单的占位符。
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<MdEditorMagnify
|
||||
title="表单输出内容"
|
||||
v-model="form_data.form_content_format"
|
||||
style="height: 150px"
|
||||
@submitDialog="submitDialog"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="表单配置" @click.prevent>
|
||||
<template #label>
|
||||
<div class="flex-between">
|
||||
<h5 class="lighter">{{ '表单配置' }}</h5>
|
||||
<el-button link type="primary" @click="openAddFormCollect()">
|
||||
<el-icon class="mr-4">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
添加
|
||||
</el-button>
|
||||
</div></template
|
||||
>
|
||||
|
||||
<el-table class="border" v-if="form_data.form_field_list.length > 0" :data="form_data.form_field_list">
|
||||
<el-table-column prop="field" label="参数">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.field" class="ellipsis-1">{{ row.field }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="label" label="显示名称">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.label && row.label.input_type === 'TooltipLabel'">
|
||||
<span :title="row.label.label" class="ellipsis-1">
|
||||
{{ row.label.label }}
|
||||
</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
<span :title="row.label" class="ellipsis-1">
|
||||
{{ row.label }}
|
||||
</span></span
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="组件类型" width="110px">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" class="info-tag">{{
|
||||
input_type_list.find((item) => item.value === row.input_type)?.label
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="default_value" label="默认值">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.default_value" class="ellipsis-1">{{ getDefaultValue(row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="必填">
|
||||
<template #default="{ row }">
|
||||
<div @click.stop>
|
||||
<el-switch disabled size="small" v-model="row.required" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="left" width="80">
|
||||
<template #default="{ row, $index }">
|
||||
<span class="mr-4">
|
||||
<el-tooltip effect="dark" content="修改" placement="top">
|
||||
<el-button type="primary" text @click.stop="openEditFormCollect(row, $index)">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip effect="dark" content="删除" placement="top">
|
||||
<el-button type="primary" text @click="deleteField(row)">
|
||||
<el-icon>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip effect="dark" content="删除" placement="top">
|
||||
<el-button type="primary" text @click="deleteField(row)">
|
||||
<el-icon>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<AddFormCollect ref="addFormCollectRef" :addFormField="addFormField"></AddFormCollect>
|
||||
<EditFormCollect ref="editFormCollectRef" :editFormField="editFormField"></EditFormCollect>
|
||||
</NodeContainer>
|
||||
|
|
@ -169,7 +168,7 @@ const openAddFormCollect = () => {
|
|||
addFormCollectRef.value?.open()
|
||||
}
|
||||
const openEditFormCollect = (form_field_data: any, index: number) => {
|
||||
editFormCollectRef.value?.open(form_field_data, index)
|
||||
editFormCollectRef.value?.open(cloneDeep(form_field_data), index)
|
||||
}
|
||||
const deleteField = (form_field_data: any) => {
|
||||
form_data.value.form_field_list = form_data.value.form_field_list.filter(
|
||||
|
|
@ -197,6 +196,20 @@ const form_data = computed({
|
|||
set(props.nodeModel.properties, 'node_data', value)
|
||||
}
|
||||
})
|
||||
|
||||
const getDefaultValue = (row: any) => {
|
||||
if (row.default_value) {
|
||||
const default_value = row.option_list
|
||||
?.filter((v: any) => row.default_value.indexOf(v.value) > -1)
|
||||
.map((v: any) => v.label)
|
||||
.join(',')
|
||||
if (default_value) {
|
||||
return default_value
|
||||
}
|
||||
return row.default_value
|
||||
}
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
return formNodeFormRef.value?.validate()
|
||||
}
|
||||
|
|
@ -206,6 +219,7 @@ function submitDialog(val: string) {
|
|||
onMounted(() => {
|
||||
set(props.nodeModel, 'validate', validate)
|
||||
sync_form_field_list()
|
||||
props.nodeModel.graphModel.eventCenter.emit('refresh_incoming_node_field')
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue