mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-27 12:12:57 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
906ec28f1f | ||
|
|
5f02b56006 | ||
|
|
ef2bcdcd17 | ||
|
|
3cd7b29611 | ||
|
|
20fa075c1b | ||
|
|
33fe7ab8e6 | ||
|
|
328b9a095a | ||
|
|
7adfdf73d9 | ||
|
|
22cf23e593 | ||
|
|
a4b4db70bd | ||
|
|
a529bc3510 | ||
|
|
766f9fa745 | ||
|
|
cf66e33c58 | ||
|
|
cb83ed8688 | ||
|
|
4229192fc1 | ||
|
|
4076988374 | ||
|
|
e3de5e7a26 | ||
|
|
d6c86d96ee | ||
|
|
84b0998055 | ||
|
|
110d8fd398 | ||
|
|
f5af097e6d |
|
|
@ -62,6 +62,8 @@ def valid_reference_value(_type, value, name):
|
||||||
def convert_value(name: str, value, _type, is_required, source, node):
|
def convert_value(name: str, value, _type, is_required, source, node):
|
||||||
if not is_required and value is None:
|
if not is_required and value is None:
|
||||||
return None
|
return None
|
||||||
|
if not is_required and source == 'reference' and (value is None or len(value) == 0):
|
||||||
|
return None
|
||||||
if source == 'reference':
|
if source == 'reference':
|
||||||
value = node.workflow_manage.get_reference_field(
|
value = node.workflow_manage.get_reference_field(
|
||||||
value[0],
|
value[0],
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,13 @@ def get_none_result(question):
|
||||||
'directly_return': ''}, {})
|
'directly_return': ''}, {})
|
||||||
|
|
||||||
|
|
||||||
|
def reset_title(title):
|
||||||
|
if title is None or len(title.strip()) == 0:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
return f"#### {title}\n"
|
||||||
|
|
||||||
|
|
||||||
class BaseSearchDatasetNode(ISearchDatasetStepNode):
|
class BaseSearchDatasetNode(ISearchDatasetStepNode):
|
||||||
def execute(self, dataset_id_list, dataset_setting, question,
|
def execute(self, dataset_id_list, dataset_setting, question,
|
||||||
exclude_paragraph_id_list=None,
|
exclude_paragraph_id_list=None,
|
||||||
|
|
@ -63,10 +70,11 @@ class BaseSearchDatasetNode(ISearchDatasetStepNode):
|
||||||
return NodeResult({'paragraph_list': result,
|
return NodeResult({'paragraph_list': result,
|
||||||
'is_hit_handling_method_list': [row for row in result if row.get('is_hit_handling_method')],
|
'is_hit_handling_method_list': [row for row in result if row.get('is_hit_handling_method')],
|
||||||
'data': '\n'.join(
|
'data': '\n'.join(
|
||||||
[f"{paragraph.get('title', '')}:{paragraph.get('content')}" for paragraph in
|
[f"{reset_title(paragraph.get('title', ''))}{paragraph.get('content')}" for paragraph in
|
||||||
paragraph_list])[0:dataset_setting.get('max_paragraph_char_number', 5000)],
|
paragraph_list])[0:dataset_setting.get('max_paragraph_char_number', 5000)],
|
||||||
'directly_return': '\n'.join(
|
'directly_return': '\n'.join(
|
||||||
[f"{paragraph.get('title', '')}:{paragraph.get('content')}" for paragraph in result if
|
[paragraph.get('content') for paragraph in
|
||||||
|
result if
|
||||||
paragraph.get('is_hit_handling_method')]),
|
paragraph.get('is_hit_handling_method')]),
|
||||||
'question': question},
|
'question': question},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,6 @@ class Flow:
|
||||||
if model_params_setting is None:
|
if model_params_setting is None:
|
||||||
model_params_setting = model_params_setting_form.get_default_form_data()
|
model_params_setting = model_params_setting_form.get_default_form_data()
|
||||||
node.properties.get('node_data', {})['model_params_setting'] = model_params_setting
|
node.properties.get('node_data', {})['model_params_setting'] = model_params_setting
|
||||||
model_params_setting_form.valid_form(model_params_setting)
|
|
||||||
if node.properties.get('status', 200) != 200:
|
if node.properties.get('status', 200) != 200:
|
||||||
raise ValidationError(ErrorDetail(f'节点{node.properties.get("stepName")} 不可用'))
|
raise ValidationError(ErrorDetail(f'节点{node.properties.get("stepName")} 不可用'))
|
||||||
node_list = [node for node in self.nodes if (node.type == 'function-lib-node')]
|
node_list = [node for node in self.nodes if (node.type == 'function-lib-node')]
|
||||||
|
|
@ -308,6 +307,7 @@ class WorkflowManage:
|
||||||
self.work_flow_post_handler.handler(self.params['chat_id'], self.params['chat_record_id'],
|
self.work_flow_post_handler.handler(self.params['chat_id'], self.params['chat_record_id'],
|
||||||
self.answer,
|
self.answer,
|
||||||
self)
|
self)
|
||||||
|
yield self.get_chunk_content('', True)
|
||||||
|
|
||||||
def run_chain_async(self, current_node):
|
def run_chain_async(self, current_node):
|
||||||
future = executor.submit(self.run_chain, current_node)
|
future = executor.submit(self.run_chain, current_node)
|
||||||
|
|
|
||||||
|
|
@ -831,8 +831,6 @@ class ApplicationSerializer(serializers.Serializer):
|
||||||
ApplicationSerializer.Edit(data=instance).is_valid(
|
ApplicationSerializer.Edit(data=instance).is_valid(
|
||||||
raise_exception=True)
|
raise_exception=True)
|
||||||
application_id = self.data.get("application_id")
|
application_id = self.data.get("application_id")
|
||||||
valid_model_params_setting(instance.get('model_id'),
|
|
||||||
instance.get('model_params_setting'))
|
|
||||||
|
|
||||||
application = QuerySet(Application).get(id=application_id)
|
application = QuerySet(Application).get(id=application_id)
|
||||||
if instance.get('model_id') is None or len(instance.get('model_id')) == 0:
|
if instance.get('model_id') is None or len(instance.get('model_id')) == 0:
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,6 @@ class ChatSerializers(serializers.Serializer):
|
||||||
return chat_id
|
return chat_id
|
||||||
|
|
||||||
def open_simple(self, application):
|
def open_simple(self, application):
|
||||||
valid_model_params_setting(application.model_id, application.model_params_setting)
|
|
||||||
application_id = self.data.get('application_id')
|
application_id = self.data.get('application_id')
|
||||||
dataset_id_list = [str(row.dataset_id) for row in
|
dataset_id_list = [str(row.dataset_id) for row in
|
||||||
QuerySet(ApplicationDatasetMapping).filter(
|
QuerySet(ApplicationDatasetMapping).filter(
|
||||||
|
|
@ -376,7 +375,6 @@ class ChatSerializers(serializers.Serializer):
|
||||||
model_id = self.data.get('model_id')
|
model_id = self.data.get('model_id')
|
||||||
dataset_id_list = self.data.get('dataset_id_list')
|
dataset_id_list = self.data.get('dataset_id_list')
|
||||||
dialogue_number = 3 if self.data.get('multiple_rounds_dialogue', False) else 0
|
dialogue_number = 3 if self.data.get('multiple_rounds_dialogue', False) else 0
|
||||||
valid_model_params_setting(model_id, self.data.get('model_params_setting'))
|
|
||||||
application = Application(id=None, dialogue_number=dialogue_number, model_id=model_id,
|
application = Application(id=None, dialogue_number=dialogue_number, model_id=model_id,
|
||||||
dataset_setting=self.data.get('dataset_setting'),
|
dataset_setting=self.data.get('dataset_setting'),
|
||||||
model_setting=self.data.get('model_setting'),
|
model_setting=self.data.get('model_setting'),
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class FileCache(BaseCache):
|
||||||
value.application.id) == application_id):
|
value.application.id) == application_id):
|
||||||
delete_keys.append(key)
|
delete_keys.append(key)
|
||||||
for key in delete_keys:
|
for key in delete_keys:
|
||||||
self.delete(key)
|
self.cache.delete(key)
|
||||||
|
|
||||||
def clear_timeout_data(self):
|
def clear_timeout_data(self):
|
||||||
for key in self.cache.iterkeys():
|
for key in self.cache.iterkeys():
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,10 @@ class CustomSwaggerAutoSchema(SwaggerAutoSchema):
|
||||||
if "api" in tags and operation_keys:
|
if "api" in tags and operation_keys:
|
||||||
return [tags_dict.get(operation_keys[1]) if operation_keys[1] in tags_dict else operation_keys[1]]
|
return [tags_dict.get(operation_keys[1]) if operation_keys[1] in tags_dict else operation_keys[1]]
|
||||||
return tags
|
return tags
|
||||||
|
def get_schema(self, request=None, public=False):
|
||||||
|
schema = super().get_schema(request, public)
|
||||||
|
if request.is_secure():
|
||||||
|
schema.schemes = ['https']
|
||||||
|
else:
|
||||||
|
schema.schemes = ['http']
|
||||||
|
return schema
|
||||||
|
|
@ -14,9 +14,9 @@ from functools import reduce
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from docx import Document, ImagePart
|
from docx import Document, ImagePart
|
||||||
|
from docx.oxml import ns
|
||||||
from docx.table import Table
|
from docx.table import Table
|
||||||
from docx.text.paragraph import Paragraph
|
from docx.text.paragraph import Paragraph
|
||||||
from docx.oxml import ns
|
|
||||||
|
|
||||||
from common.handle.base_split_handle import BaseSplitHandle
|
from common.handle.base_split_handle import BaseSplitHandle
|
||||||
from common.util.split_model import SplitModel
|
from common.util.split_model import SplitModel
|
||||||
|
|
@ -33,11 +33,8 @@ old_docx_nsmap = {'v': 'urn:schemas-microsoft-com:vml'}
|
||||||
combine_nsmap = {**ns.nsmap, **old_docx_nsmap}
|
combine_nsmap = {**ns.nsmap, **old_docx_nsmap}
|
||||||
|
|
||||||
|
|
||||||
def image_to_mode(image, doc: Document, images_list, get_image_id, is_new_docx=True):
|
def image_to_mode(image, doc: Document, images_list, get_image_id):
|
||||||
if is_new_docx:
|
image_ids = image['get_image_id_handle'](image.get('image'))
|
||||||
image_ids = image.xpath('.//a:blip/@r:embed')
|
|
||||||
else:
|
|
||||||
image_ids = image.xpath('.//v:imagedata/@r:id', namespaces=combine_nsmap)
|
|
||||||
for img_id in image_ids: # 获取图片id
|
for img_id in image_ids: # 获取图片id
|
||||||
part = doc.part.related_parts[img_id] # 根据图片id获取对应的图片
|
part = doc.part.related_parts[img_id] # 根据图片id获取对应的图片
|
||||||
if isinstance(part, ImagePart):
|
if isinstance(part, ImagePart):
|
||||||
|
|
@ -49,14 +46,15 @@ def image_to_mode(image, doc: Document, images_list, get_image_id, is_new_docx=T
|
||||||
|
|
||||||
|
|
||||||
def get_paragraph_element_images(paragraph_element, doc: Document, images_list, get_image_id):
|
def get_paragraph_element_images(paragraph_element, doc: Document, images_list, get_image_id):
|
||||||
images_xpath_list = [".//pic:pic", ".//w:pict"]
|
images_xpath_list = [(".//pic:pic", lambda img: img.xpath('.//a:blip/@r:embed')),
|
||||||
|
(".//w:pict", lambda img: img.xpath('.//v:imagedata/@r:id', namespaces=combine_nsmap))]
|
||||||
images = []
|
images = []
|
||||||
for images_xpath in images_xpath_list:
|
for images_xpath, get_image_id_handle in images_xpath_list:
|
||||||
try:
|
try:
|
||||||
_images = paragraph_element.xpath(images_xpath)
|
_images = paragraph_element.xpath(images_xpath)
|
||||||
if _images is not None and len(_images) > 0:
|
if _images is not None and len(_images) > 0:
|
||||||
for image in _images:
|
for image in _images:
|
||||||
images.append(image)
|
images.append({'image': image, 'get_image_id_handle': get_image_id_handle})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
return images
|
return images
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
from typing import List, Optional, Any, Iterator, Dict
|
from typing import List, Optional, Any, Iterator, Dict
|
||||||
|
|
||||||
from langchain_community.chat_models.sparkllm import \
|
from langchain_community.chat_models.sparkllm import \
|
||||||
ChatSparkLLM, _convert_message_to_dict, _convert_delta_to_message_chunk
|
ChatSparkLLM, convert_message_to_dict, _convert_delta_to_message_chunk
|
||||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||||
from langchain_core.messages import BaseMessage, AIMessageChunk
|
from langchain_core.messages import BaseMessage, AIMessageChunk
|
||||||
from langchain_core.outputs import ChatGenerationChunk
|
from langchain_core.outputs import ChatGenerationChunk
|
||||||
|
|
@ -56,7 +56,7 @@ class XFChatSparkLLM(MaxKBBaseModel, ChatSparkLLM):
|
||||||
default_chunk_class = AIMessageChunk
|
default_chunk_class = AIMessageChunk
|
||||||
|
|
||||||
self.client.arun(
|
self.client.arun(
|
||||||
[_convert_message_to_dict(m) for m in messages],
|
[convert_message_to_dict(m) for m in messages],
|
||||||
self.spark_user_id,
|
self.spark_user_id,
|
||||||
self.model_kwargs,
|
self.model_kwargs,
|
||||||
True,
|
True,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ class XinferenceEmbeddingModelCredential(BaseForm, BaseModelCredential):
|
||||||
if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))):
|
if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))):
|
||||||
raise AppApiException(ValidCode.valid_error.value, f'{model_type} 模型类型不支持')
|
raise AppApiException(ValidCode.valid_error.value, f'{model_type} 模型类型不支持')
|
||||||
try:
|
try:
|
||||||
model_list = provider.get_base_model_list(model_credential.get('api_base'), 'embedding')
|
model_list = provider.get_base_model_list(model_credential.get('api_base'), model_credential.get('api_key'),
|
||||||
|
'embedding')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise AppApiException(ValidCode.valid_error.value, "API 域名无效")
|
raise AppApiException(ValidCode.valid_error.value, "API 域名无效")
|
||||||
exist = provider.get_model_info_by_name(model_list, model_name)
|
exist = provider.get_model_info_by_name(model_list, model_name)
|
||||||
|
|
@ -36,3 +37,4 @@ class XinferenceEmbeddingModelCredential(BaseForm, BaseModelCredential):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
api_base = forms.TextInputField('API 域名', required=True)
|
api_base = forms.TextInputField('API 域名', required=True)
|
||||||
|
api_key = forms.PasswordInputField('API Key', required=True)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,26 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
import threading
|
import threading
|
||||||
from typing import Dict
|
from typing import Dict, Optional, List, Any
|
||||||
|
|
||||||
from langchain_community.embeddings import XinferenceEmbeddings
|
from langchain_community.embeddings import XinferenceEmbeddings
|
||||||
|
from langchain_core.embeddings import Embeddings
|
||||||
|
|
||||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||||
|
|
||||||
|
|
||||||
class XinferenceEmbedding(MaxKBBaseModel, XinferenceEmbeddings):
|
class XinferenceEmbedding(MaxKBBaseModel, Embeddings):
|
||||||
|
client: Any
|
||||||
|
server_url: Optional[str]
|
||||||
|
"""URL of the xinference server"""
|
||||||
|
model_uid: Optional[str]
|
||||||
|
"""UID of the launched model"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||||
return XinferenceEmbedding(
|
return XinferenceEmbedding(
|
||||||
model_uid=model_name,
|
model_uid=model_name,
|
||||||
server_url=model_credential.get('api_base'),
|
server_url=model_credential.get('api_base'),
|
||||||
|
api_key=model_credential.get('api_key'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def down_model(self):
|
def down_model(self):
|
||||||
|
|
@ -22,3 +30,63 @@ class XinferenceEmbedding(MaxKBBaseModel, XinferenceEmbeddings):
|
||||||
thread = threading.Thread(target=self.down_model)
|
thread = threading.Thread(target=self.down_model)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, server_url: Optional[str] = None, model_uid: Optional[str] = None,
|
||||||
|
api_key: Optional[str] = None
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
from xinference.client import RESTfulClient
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from xinference_client import RESTfulClient
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(
|
||||||
|
"Could not import RESTfulClient from xinference. Please install it"
|
||||||
|
" with `pip install xinference` or `pip install xinference_client`."
|
||||||
|
) from e
|
||||||
|
|
||||||
|
if server_url is None:
|
||||||
|
raise ValueError("Please provide server URL")
|
||||||
|
|
||||||
|
if model_uid is None:
|
||||||
|
raise ValueError("Please provide the model UID")
|
||||||
|
|
||||||
|
self.server_url = server_url
|
||||||
|
|
||||||
|
self.model_uid = model_uid
|
||||||
|
|
||||||
|
self.api_key = api_key
|
||||||
|
|
||||||
|
self.client = RESTfulClient(server_url, api_key)
|
||||||
|
|
||||||
|
def embed_documents(self, texts: List[str]) -> List[List[float]]:
|
||||||
|
"""Embed a list of documents using Xinference.
|
||||||
|
Args:
|
||||||
|
texts: The list of texts to embed.
|
||||||
|
Returns:
|
||||||
|
List of embeddings, one for each text.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = self.client.get_model(self.model_uid)
|
||||||
|
|
||||||
|
embeddings = [
|
||||||
|
model.create_embedding(text)["data"][0]["embedding"] for text in texts
|
||||||
|
]
|
||||||
|
return [list(map(float, e)) for e in embeddings]
|
||||||
|
|
||||||
|
def embed_query(self, text: str) -> List[float]:
|
||||||
|
"""Embed a query of documents using Xinference.
|
||||||
|
Args:
|
||||||
|
text: The text to embed.
|
||||||
|
Returns:
|
||||||
|
Embeddings for the text.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = self.client.get_model(self.model_uid)
|
||||||
|
|
||||||
|
embedding_res = model.create_embedding(text)
|
||||||
|
|
||||||
|
embedding = embedding_res["data"][0]["embedding"]
|
||||||
|
|
||||||
|
return list(map(float, embedding))
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ DATABASES = {
|
||||||
'default': CONFIG.get_db_setting()
|
'default': CONFIG.get_db_setting()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ djangorestframework = "^3.15.2"
|
||||||
drf-yasg = "1.21.7"
|
drf-yasg = "1.21.7"
|
||||||
django-filter = "23.2"
|
django-filter = "23.2"
|
||||||
langchain = "0.2.16"
|
langchain = "0.2.16"
|
||||||
langchain_community = "0.2.4"
|
langchain_community = "0.2.17"
|
||||||
langchain-huggingface = "^0.0.3"
|
langchain-huggingface = "^0.0.3"
|
||||||
psycopg2-binary = "2.9.7"
|
psycopg2-binary = "2.9.7"
|
||||||
jieba = "^0.42.1"
|
jieba = "^0.42.1"
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
|
|
@ -40,8 +40,14 @@
|
||||||
<div ref="dialogScrollbar" class="ai-chat__content p-24 chat-width">
|
<div ref="dialogScrollbar" class="ai-chat__content p-24 chat-width">
|
||||||
<div class="item-content mb-16" v-if="!props.available || (props.data?.prologue && !log)">
|
<div class="item-content mb-16" v-if="!props.available || (props.data?.prologue && !log)">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-if="data.avatar" :src="data.avatar" height="30px" />
|
<el-image
|
||||||
<LogoIcon v-else height="30px" />
|
v-if="data.avatar"
|
||||||
|
:src="data.avatar"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
|
<LogoIcon v-else height="32px" width="32px" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
@ -96,8 +102,14 @@
|
||||||
<!-- 回答 -->
|
<!-- 回答 -->
|
||||||
<div class="item-content mb-16 lighter">
|
<div class="item-content mb-16 lighter">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-if="data.avatar" :src="data.avatar" height="30px" />
|
<el-image
|
||||||
<LogoIcon v-else height="30px" />
|
v-if="data.avatar"
|
||||||
|
:src="data.avatar"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 30px; height: 30px; display: block"
|
||||||
|
/>
|
||||||
|
<LogoIcon v-else height="32px" width="32px" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
defineOptions({ name: 'InfiniteScroll' })
|
defineOptions({ name: 'InfiniteScroll' })
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -47,6 +47,14 @@ const props = defineProps({
|
||||||
const emit = defineEmits(['update:current_page', 'load'])
|
const emit = defineEmits(['update:current_page', 'load'])
|
||||||
const current = ref(props.current_page)
|
const current = ref(props.current_page)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.current_page,
|
||||||
|
(val) => {
|
||||||
|
if (val === 1) {
|
||||||
|
current.value = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
const noMore = computed(
|
const noMore = computed(
|
||||||
() =>
|
() =>
|
||||||
props.size > 0 && props.size === props.total && props.total > props.page_size && !props.loading
|
props.size > 0 && props.size === props.total && props.total > props.page_size && !props.loading
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,17 @@ import HtmlRander from './HtmlRander.vue'
|
||||||
import EchartsRander from './EchartsRander.vue'
|
import EchartsRander from './EchartsRander.vue'
|
||||||
config({
|
config({
|
||||||
markdownItConfig(md) {
|
markdownItConfig(md) {
|
||||||
|
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
||||||
|
tokens[idx].attrSet('style', 'display:inline-block;min-height:33px;padding:0;margin:0')
|
||||||
|
if (tokens[idx].content) {
|
||||||
|
tokens[idx].attrSet('title', tokens[idx].content)
|
||||||
|
}
|
||||||
|
tokens[idx].attrSet(
|
||||||
|
'onerror',
|
||||||
|
'this.src="/ui/assets/load_error.png";this.onerror=null;this.height="33px"'
|
||||||
|
)
|
||||||
|
return md.renderer.renderToken(tokens, idx, options)
|
||||||
|
}
|
||||||
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
|
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
|
||||||
tokens[idx].attrSet('target', '_blank')
|
tokens[idx].attrSet('target', '_blank')
|
||||||
return md.renderer.renderToken(tokens, idx, options)
|
return md.renderer.renderToken(tokens, idx, options)
|
||||||
|
|
@ -52,51 +63,9 @@ const props = withDefaults(
|
||||||
const editorRef = ref()
|
const editorRef = ref()
|
||||||
const md_view_list = computed(() => {
|
const md_view_list = computed(() => {
|
||||||
const temp_source = props.source
|
const temp_source = props.source
|
||||||
const temp_md_img_list = temp_source.match(/(!\[.*?\]\(img\/.*?\){.*?})|(!\[.*?\]\(img\/.*?\))/g)
|
return split_echarts_rander(split_html_rander(split_quick_question([temp_source])))
|
||||||
const md_img_list = temp_md_img_list ? temp_md_img_list.filter((i) => i) : []
|
|
||||||
const split_img_value = temp_source
|
|
||||||
.split(/(!\[.*?\]\(img\/.*?\){.*?})|(!\[.*?\]\(img\/.*?\))/g)
|
|
||||||
.filter((item) => item !== undefined)
|
|
||||||
.filter((item) => !md_img_list?.includes(item))
|
|
||||||
const result = Array.from(
|
|
||||||
{ length: md_img_list.length + split_img_value.length },
|
|
||||||
(v, i) => i
|
|
||||||
).map((index) => {
|
|
||||||
if (index % 2 == 0) {
|
|
||||||
return split_img_value[Math.floor(index / 2)]
|
|
||||||
} else {
|
|
||||||
return md_img_list[Math.floor(index / 2)]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return split_echarts_rander(split_html_rander(split_quick_question(split_md_img(result))))
|
|
||||||
})
|
})
|
||||||
const split_md_img = (result: Array<string>) => {
|
|
||||||
return result
|
|
||||||
.map((item) => split_md_img_(item))
|
|
||||||
.reduce((x: any, y: any) => {
|
|
||||||
return [...x, ...y]
|
|
||||||
}, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
const split_md_img_ = (source: string) => {
|
|
||||||
const temp_md_img_list = source.match(/(!\[.*?\]\(.*?\){.*?})|(!\[.*?\]\(.*?\))/g)
|
|
||||||
const md_img_list = temp_md_img_list ? temp_md_img_list.filter((i) => i) : []
|
|
||||||
const split_img_value = source
|
|
||||||
.split(/(!\[.*?\]\(.*?\){.*?})|(!\[.*?\]\(.*?\))/g)
|
|
||||||
.filter((item) => item !== undefined)
|
|
||||||
.filter((item) => !md_img_list?.includes(item))
|
|
||||||
const result = Array.from(
|
|
||||||
{ length: md_img_list.length + split_img_value.length },
|
|
||||||
(v, i) => i
|
|
||||||
).map((index) => {
|
|
||||||
if (index % 2 == 0) {
|
|
||||||
return split_img_value[Math.floor(index / 2)]
|
|
||||||
} else {
|
|
||||||
return md_img_list[Math.floor(index / 2)]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
const split_quick_question = (result: Array<string>) => {
|
const split_quick_question = (result: Array<string>) => {
|
||||||
return result
|
return result
|
||||||
.map((item) => split_quick_question_(item))
|
.map((item) => split_quick_question_(item))
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,6 @@ const applicationRouter = {
|
||||||
name: 'application',
|
name: 'application',
|
||||||
component: () => import('@/views/application/index.vue')
|
component: () => import('@/views/application/index.vue')
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/application/create',
|
|
||||||
name: 'CreateApplication',
|
|
||||||
meta: { activeMenu: '/application' },
|
|
||||||
component: () => import('@/views/application/CreateAndSetting.vue'),
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/application/:id/:type',
|
path: '/application/:id/:type',
|
||||||
name: 'ApplicationDetail',
|
name: 'ApplicationDetail',
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ const datasetRouter = {
|
||||||
component: () => import('@/views/dataset/index.vue')
|
component: () => import('@/views/dataset/index.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dataset/:type', // create 或者 upload
|
path: '/dataset/:type', // upload
|
||||||
name: 'UploadDocumentDataset',
|
name: 'UploadDocumentDataset',
|
||||||
meta: { activeMenu: '/dataset' },
|
meta: { activeMenu: '/dataset' },
|
||||||
component: () => import('@/views/dataset/UploadDocumentDataset.vue'),
|
component: () => import('@/views/dataset/UploadDocumentDataset.vue'),
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,12 @@
|
||||||
:size="32"
|
:size="32"
|
||||||
style="background: none"
|
style="background: none"
|
||||||
>
|
>
|
||||||
<img :src="detail?.icon" alt="" />
|
<el-image
|
||||||
|
:src="detail?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="detail?.name"
|
v-else-if="detail?.name"
|
||||||
|
|
@ -178,7 +183,7 @@
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
<el-text type="info" size="small"
|
<el-text type="info" size="small"
|
||||||
>建议尺寸 64*64,支持 JPG、PNG、GIF,大小不超过 10 MB</el-text
|
>建议尺寸 32*32,支持 JPG、PNG、GIF,大小不超过 10 MB</el-text
|
||||||
>
|
>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card shadow="never" class="mb-8">
|
<el-card shadow="never" class="mb-8">
|
||||||
|
|
@ -197,7 +202,7 @@
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
<el-text type="info" size="small">
|
<el-text type="info" size="small">
|
||||||
建议尺寸 64*64,支持 JPG、PNG、GIF,大小不超过 10 MB</el-text
|
建议尺寸 32*32,支持 JPG、PNG、GIF,大小不超过 10 MB</el-text
|
||||||
>
|
>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card shadow="never" class="mb-8">
|
<el-card shadow="never" class="mb-8">
|
||||||
|
|
@ -215,7 +220,7 @@
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
<el-text type="info" size="small">
|
<el-text type="info" size="small">
|
||||||
建议尺寸 64*64,支持 JPG、PNG、GIF,大小不超过 10 MB
|
建议尺寸 32*32,支持 JPG、PNG、GIF,大小不超过 10 MB
|
||||||
</el-text>
|
</el-text>
|
||||||
<div class="border-t mt-8">
|
<div class="border-t mt-8">
|
||||||
<div class="flex-between mb-8">
|
<div class="flex-between mb-8">
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,12 @@
|
||||||
:size="32"
|
:size="32"
|
||||||
style="background: none"
|
style="background: none"
|
||||||
>
|
>
|
||||||
<img :src="detail?.icon" alt="" />
|
<el-image
|
||||||
|
:src="detail?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="detail?.name"
|
v-else-if="detail?.name"
|
||||||
|
|
@ -353,23 +358,22 @@ function getDetail() {
|
||||||
?.filter((v: any) => v.id === 'base-node')
|
?.filter((v: any) => v.id === 'base-node')
|
||||||
.map((v: any) => {
|
.map((v: any) => {
|
||||||
apiInputParams.value = v.properties.api_input_field_list
|
apiInputParams.value = v.properties.api_input_field_list
|
||||||
? v.properties.api_input_field_list
|
? v.properties.api_input_field_list.map((v: any) => {
|
||||||
.map((v: any) => {
|
return {
|
||||||
return {
|
name: v.variable,
|
||||||
name: v.variable,
|
value: v.default_value
|
||||||
value: v.default_value
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
: v.properties.input_field_list
|
: v.properties.input_field_list
|
||||||
? v.properties.input_field_list
|
? v.properties.input_field_list
|
||||||
.filter((v: any) => v.assignment_method === 'api_input')
|
.filter((v: any) => v.assignment_method === 'api_input')
|
||||||
.map((v: any) => {
|
.map((v: any) => {
|
||||||
return {
|
return {
|
||||||
name: v.variable,
|
name: v.variable,
|
||||||
value: v.default_value
|
value: v.default_value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
: []
|
: []
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,12 @@
|
||||||
:size="32"
|
:size="32"
|
||||||
style="background: none"
|
style="background: none"
|
||||||
>
|
>
|
||||||
<img :src="detail?.icon" alt="" />
|
<el-image
|
||||||
|
:src="detail?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="detail?.name"
|
v-else-if="detail?.name"
|
||||||
|
|
|
||||||
|
|
@ -252,12 +252,12 @@
|
||||||
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="32">
|
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="32">
|
||||||
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<auto-tooltip
|
<span
|
||||||
:content="relatedObject(datasetList, item, 'id')?.name"
|
class="ellipsis cursor"
|
||||||
style="width: 80%"
|
:title="relatedObject(datasetList, item, 'id')?.name"
|
||||||
|
>
|
||||||
|
{{ relatedObject(datasetList, item, 'id')?.name }}</span
|
||||||
>
|
>
|
||||||
{{ relatedObject(datasetList, item, 'id')?.name }}
|
|
||||||
</auto-tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
<el-button text @click="removeDataset(item)">
|
<el-button text @click="removeDataset(item)">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
|
|
@ -326,7 +326,11 @@
|
||||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||||
</el-tooltip> -->
|
</el-tooltip> -->
|
||||||
</div>
|
</div>
|
||||||
<el-switch size="small" v-model="applicationForm.stt_model_enable" @change="sttModelEnableChange"/>
|
<el-switch
|
||||||
|
size="small"
|
||||||
|
v-model="applicationForm.stt_model_enable"
|
||||||
|
@change="sttModelEnableChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-select
|
<el-select
|
||||||
|
|
@ -406,7 +410,11 @@
|
||||||
<el-icon class="mr-4"><Setting /></el-icon>
|
<el-icon class="mr-4"><Setting /></el-icon>
|
||||||
设置
|
设置
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-switch size="small" v-model="applicationForm.tts_model_enable" @change="ttsModelEnableChange"/>
|
<el-switch
|
||||||
|
size="small"
|
||||||
|
v-model="applicationForm.tts_model_enable"
|
||||||
|
@change="ttsModelEnableChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -501,7 +509,11 @@
|
||||||
:size="32"
|
:size="32"
|
||||||
style="background: none"
|
style="background: none"
|
||||||
>
|
>
|
||||||
<img :src="applicationForm?.icon" alt="" />
|
<img
|
||||||
|
:src="applicationForm?.icon"
|
||||||
|
alt=""
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="applicationForm?.name"
|
v-else-if="applicationForm?.name"
|
||||||
|
|
|
||||||
|
|
@ -1,546 +0,0 @@
|
||||||
<template>
|
|
||||||
<LayoutContainer
|
|
||||||
:header="
|
|
||||||
id
|
|
||||||
? $t('views.application.applicationForm.title.edit')
|
|
||||||
: $t('views.application.applicationForm.title.create')
|
|
||||||
"
|
|
||||||
:back-to="id ? '' : '-1'"
|
|
||||||
class="create-application"
|
|
||||||
>
|
|
||||||
<el-row v-loading="loading">
|
|
||||||
<el-col :span="10">
|
|
||||||
<div class="p-24 mb-16" style="padding-bottom: 0">
|
|
||||||
<h4 class="title-decoration-1">
|
|
||||||
{{ $t('views.application.applicationForm.title.info') }}
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div class="scrollbar-height-left">
|
|
||||||
<el-scrollbar>
|
|
||||||
<el-form
|
|
||||||
hide-required-asterisk
|
|
||||||
ref="applicationFormRef"
|
|
||||||
:model="applicationForm"
|
|
||||||
:rules="rules"
|
|
||||||
label-position="top"
|
|
||||||
require-asterisk-position="right"
|
|
||||||
class="p-24"
|
|
||||||
style="padding-top: 0"
|
|
||||||
>
|
|
||||||
<el-form-item prop="name">
|
|
||||||
<template #label>
|
|
||||||
<div class="flex-between">
|
|
||||||
<span
|
|
||||||
>{{ $t('views.application.applicationForm.form.appName.label') }}
|
|
||||||
<span class="danger">*</span></span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-input
|
|
||||||
v-model="applicationForm.name"
|
|
||||||
maxlength="64"
|
|
||||||
:placeholder="$t('views.application.applicationForm.form.appName.placeholder')"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('views.application.applicationForm.form.appDescription.label')"
|
|
||||||
>
|
|
||||||
<el-input
|
|
||||||
v-model="applicationForm.desc"
|
|
||||||
type="textarea"
|
|
||||||
:placeholder="
|
|
||||||
$t('views.application.applicationForm.form.appDescription.placeholder')
|
|
||||||
"
|
|
||||||
:rows="3"
|
|
||||||
maxlength="256"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('views.application.applicationForm.form.aiModel.label')"
|
|
||||||
prop="model_id"
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<div class="flex-between">
|
|
||||||
<span>{{ $t('views.application.applicationForm.form.aiModel.label') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-select
|
|
||||||
v-model="applicationForm.model_id"
|
|
||||||
:placeholder="$t('views.application.applicationForm.form.aiModel.placeholder')"
|
|
||||||
class="w-full"
|
|
||||||
popper-class="select-model"
|
|
||||||
:clearable="true"
|
|
||||||
>
|
|
||||||
<el-option-group
|
|
||||||
v-for="(value, label) in modelOptions"
|
|
||||||
:key="value"
|
|
||||||
:label="relatedObject(providerOptions, label, 'provider')?.name"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in value.filter((v: any) => v.status === 'SUCCESS')"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
class="flex-between"
|
|
||||||
>
|
|
||||||
<div class="flex">
|
|
||||||
<span
|
|
||||||
v-html="relatedObject(providerOptions, label, 'provider')?.icon"
|
|
||||||
class="model-icon mr-8"
|
|
||||||
></span>
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
</div>
|
|
||||||
<el-icon class="check-icon" v-if="item.id === applicationForm.model_id"
|
|
||||||
><Check
|
|
||||||
/></el-icon>
|
|
||||||
</el-option>
|
|
||||||
<!-- 不可用 -->
|
|
||||||
<el-option
|
|
||||||
v-for="item in value.filter((v: any) => v.status !== 'SUCCESS')"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
class="flex-between"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<div class="flex">
|
|
||||||
<span
|
|
||||||
v-html="relatedObject(providerOptions, label, 'provider')?.icon"
|
|
||||||
class="model-icon mr-8"
|
|
||||||
></span>
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
<span class="danger">{{
|
|
||||||
$t('views.application.applicationForm.form.aiModel.unavailable')
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
<el-icon class="check-icon" v-if="item.id === applicationForm.model_id"
|
|
||||||
><Check
|
|
||||||
/></el-icon>
|
|
||||||
</el-option>
|
|
||||||
</el-option-group>
|
|
||||||
<template #footer>
|
|
||||||
<div class="w-full text-left cursor" @click="openCreateModel()">
|
|
||||||
<el-button type="primary" link>
|
|
||||||
<el-icon class="mr-4"><Plus /></el-icon>
|
|
||||||
{{ $t('views.application.applicationForm.form.addModel') }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('views.application.applicationForm.form.prompt.label')"
|
|
||||||
prop="model_setting.prompt"
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<div class="flex align-center">
|
|
||||||
<div class="flex-between mr-4">
|
|
||||||
<span
|
|
||||||
>{{ $t('views.application.applicationForm.form.prompt.label') }}
|
|
||||||
<span class="danger">*</span></span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<el-tooltip effect="dark" placement="right">
|
|
||||||
<template #content>{{
|
|
||||||
$t('views.application.applicationForm.form.prompt.tooltip', {
|
|
||||||
data: '{data}',
|
|
||||||
question: '{question}'
|
|
||||||
})
|
|
||||||
}}</template>
|
|
||||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-input
|
|
||||||
v-model="applicationForm.model_setting.prompt"
|
|
||||||
:rows="6"
|
|
||||||
type="textarea"
|
|
||||||
maxlength="2048"
|
|
||||||
:placeholder="defaultPrompt"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="历史聊天记录" @click.prevent>
|
|
||||||
<el-input-number
|
|
||||||
v-model="applicationForm.dialogue_number"
|
|
||||||
:min="0"
|
|
||||||
:value-on-clear="0"
|
|
||||||
controls-position="right"
|
|
||||||
class="w-full"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
label="$t('views.application.applicationForm.form.relatedKnowledgeBase')"
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<div class="flex-between">
|
|
||||||
<span>{{
|
|
||||||
$t('views.application.applicationForm.form.relatedKnowledgeBase')
|
|
||||||
}}</span>
|
|
||||||
<div>
|
|
||||||
<el-button type="primary" link @click="openParamSettingDialog">
|
|
||||||
<AppIcon iconName="app-operation" class="mr-4"></AppIcon
|
|
||||||
>{{ $t('views.application.applicationForm.form.paramSetting') }}
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" link @click="openDatasetDialog">
|
|
||||||
<el-icon class="mr-4"><Plus /></el-icon
|
|
||||||
>{{ $t('views.application.applicationForm.form.add') }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="w-full">
|
|
||||||
<el-text type="info" v-if="applicationForm.dataset_id_list?.length === 0">{{
|
|
||||||
$t('views.application.applicationForm.form.relatedKnowledgeBaseWhere')
|
|
||||||
}}</el-text>
|
|
||||||
<el-row :gutter="12" v-else>
|
|
||||||
<el-col
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
:md="24"
|
|
||||||
:lg="12"
|
|
||||||
:xl="12"
|
|
||||||
class="mb-8"
|
|
||||||
v-for="(item, index) in applicationForm.dataset_id_list"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<el-card class="relate-dataset-card border-r-4" shadow="never">
|
|
||||||
<div class="flex-between">
|
|
||||||
<div class="flex align-center">
|
|
||||||
<AppAvatar
|
|
||||||
v-if="relatedObject(datasetList, item, 'id')?.type === '1'"
|
|
||||||
class="mr-8 avatar-purple"
|
|
||||||
shape="square"
|
|
||||||
:size="32"
|
|
||||||
>
|
|
||||||
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
|
|
||||||
</AppAvatar>
|
|
||||||
|
|
||||||
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="32">
|
|
||||||
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
|
||||||
</AppAvatar>
|
|
||||||
<div class="ellipsis">
|
|
||||||
{{ relatedObject(datasetList, item, 'id')?.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-button text @click="removeDataset(item)">
|
|
||||||
<el-icon><Close /></el-icon>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('views.application.applicationForm.form.prologue')">
|
|
||||||
<MdEditor
|
|
||||||
class="prologue-md-editor"
|
|
||||||
v-model="applicationForm.prologue"
|
|
||||||
:preview="false"
|
|
||||||
:toolbars="[]"
|
|
||||||
:footers="[]"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item @click.prevent>
|
|
||||||
<template #label>
|
|
||||||
<div class="flex align-center">
|
|
||||||
<span class="mr-4">{{
|
|
||||||
$t('views.application.applicationForm.form.problemOptimization.label')
|
|
||||||
}}</span>
|
|
||||||
<el-tooltip
|
|
||||||
effect="dark"
|
|
||||||
:content="
|
|
||||||
$t('views.application.applicationForm.form.problemOptimization.tooltip')
|
|
||||||
"
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-switch size="small" v-model="applicationForm.problem_optimization"></el-switch>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
<div class="text-right border-t p-16">
|
|
||||||
<el-button v-if="!id" @click="router.push({ path: `/application` })"
|
|
||||||
>{{ $t('views.application.applicationForm.buttons.cancel') }}
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" @click="submit(applicationFormRef)" :disabled="loading">
|
|
||||||
{{
|
|
||||||
id
|
|
||||||
? $t('views.application.applicationForm.buttons.save')
|
|
||||||
: $t('views.application.applicationForm.buttons.create')
|
|
||||||
}}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="14" class="p-24 border-l">
|
|
||||||
<h4 class="title-decoration-1 mb-16">
|
|
||||||
{{ $t('views.application.applicationForm.form.apptest') }}
|
|
||||||
</h4>
|
|
||||||
<div class="dialog-bg">
|
|
||||||
<h4 class="p-24">
|
|
||||||
{{
|
|
||||||
applicationForm?.name || $t('views.application.applicationForm.form.appName.label')
|
|
||||||
}}
|
|
||||||
</h4>
|
|
||||||
<div class="scrollbar-height">
|
|
||||||
<AiChat :data="applicationForm"></AiChat>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<ParamSettingDialog ref="ParamSettingDialogRef" @refresh="refreshParam" />
|
|
||||||
<AddDatasetDialog
|
|
||||||
ref="AddDatasetDialogRef"
|
|
||||||
@addData="addDataset"
|
|
||||||
:data="datasetList"
|
|
||||||
@refresh="refresh"
|
|
||||||
:loading="datasetLoading"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 添加模版 -->
|
|
||||||
<CreateModelDialog
|
|
||||||
ref="createModelRef"
|
|
||||||
@submit="getModel"
|
|
||||||
@change="openCreateModel($event)"
|
|
||||||
></CreateModelDialog>
|
|
||||||
<SelectProviderDialog ref="selectProviderRef" @change="openCreateModel($event)" />
|
|
||||||
</LayoutContainer>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { reactive, ref, watch, onMounted } from 'vue'
|
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
|
||||||
import { groupBy } from 'lodash'
|
|
||||||
import ParamSettingDialog from './component/ParamSettingDialog.vue'
|
|
||||||
import AddDatasetDialog from './component/AddDatasetDialog.vue'
|
|
||||||
import CreateModelDialog from '@/views/template/component/CreateModelDialog.vue'
|
|
||||||
import SelectProviderDialog from '@/views/template/component/SelectProviderDialog.vue'
|
|
||||||
import applicationApi from '@/api/application'
|
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
|
||||||
import type { ApplicationFormType } from '@/api/type/application'
|
|
||||||
import type { Provider } from '@/api/type/model'
|
|
||||||
import { relatedObject } from '@/utils/utils'
|
|
||||||
import { MsgSuccess } from '@/utils/message'
|
|
||||||
import useStore from '@/stores'
|
|
||||||
import { t } from '@/locales'
|
|
||||||
const { model, dataset, application, user } = useStore()
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
|
||||||
const {
|
|
||||||
params: { id }
|
|
||||||
} = route as any
|
|
||||||
// @ts-ignore
|
|
||||||
const defaultPrompt = t('views.application.prompt.defaultPrompt', {
|
|
||||||
data: '{data}',
|
|
||||||
question: '{question}'
|
|
||||||
})
|
|
||||||
|
|
||||||
const ParamSettingDialogRef = ref<InstanceType<typeof ParamSettingDialog>>()
|
|
||||||
const createModelRef = ref<InstanceType<typeof CreateModelDialog>>()
|
|
||||||
const selectProviderRef = ref<InstanceType<typeof SelectProviderDialog>>()
|
|
||||||
|
|
||||||
const applicationFormRef = ref<FormInstance>()
|
|
||||||
const AddDatasetDialogRef = ref()
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
const datasetLoading = ref(false)
|
|
||||||
const applicationForm = ref<ApplicationFormType>({
|
|
||||||
name: '',
|
|
||||||
desc: '',
|
|
||||||
model_id: '',
|
|
||||||
dialogue_number: 0,
|
|
||||||
prologue: t('views.application.prompt.defaultPrologue'),
|
|
||||||
dataset_id_list: [],
|
|
||||||
dataset_setting: {
|
|
||||||
top_n: 3,
|
|
||||||
similarity: 0.6,
|
|
||||||
max_paragraph_char_number: 5000,
|
|
||||||
search_mode: 'embedding',
|
|
||||||
no_references_setting: {
|
|
||||||
status: 'ai_questioning',
|
|
||||||
value: '{question}'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
model_setting: {
|
|
||||||
prompt: defaultPrompt
|
|
||||||
},
|
|
||||||
problem_optimization: false,
|
|
||||||
type: 'SIMPLE'
|
|
||||||
})
|
|
||||||
|
|
||||||
const rules = reactive<FormRules<ApplicationFormType>>({
|
|
||||||
name: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: t('views.application.applicationForm.form.appName.placeholder'),
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
model_id: [
|
|
||||||
{
|
|
||||||
required: false,
|
|
||||||
message: t('views.application.applicationForm.form.aiModel.placeholder'),
|
|
||||||
trigger: 'change'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'model_setting.prompt': [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: t('views.application.applicationForm.form.prompt.placeholder'),
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
const modelOptions = ref<any>(null)
|
|
||||||
const providerOptions = ref<Array<Provider>>([])
|
|
||||||
const datasetList = ref([])
|
|
||||||
|
|
||||||
const submit = async (formEl: FormInstance | undefined) => {
|
|
||||||
if (!formEl) return
|
|
||||||
await formEl.validate((valid, fields) => {
|
|
||||||
if (valid) {
|
|
||||||
if (id) {
|
|
||||||
application.asyncPutApplication(id, applicationForm.value, loading).then((res) => {
|
|
||||||
MsgSuccess(t('views.application.applicationForm.buttons.saveSuccess'))
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
applicationApi.postApplication(applicationForm.value, loading).then((res) => {
|
|
||||||
MsgSuccess(t('views.application.applicationForm.buttons.createSuccess'))
|
|
||||||
router.push({ path: `/application` })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const openParamSettingDialog = () => {
|
|
||||||
ParamSettingDialogRef.value?.open(applicationForm.value.dataset_setting)
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshParam(data: any) {
|
|
||||||
applicationForm.value.dataset_setting = data
|
|
||||||
}
|
|
||||||
|
|
||||||
const openCreateModel = (provider?: Provider) => {
|
|
||||||
if (provider && provider.provider) {
|
|
||||||
createModelRef.value?.open(provider)
|
|
||||||
} else {
|
|
||||||
selectProviderRef.value?.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDataset(id: any) {
|
|
||||||
if (applicationForm.value.dataset_id_list) {
|
|
||||||
applicationForm.value.dataset_id_list.splice(
|
|
||||||
applicationForm.value.dataset_id_list.indexOf(id),
|
|
||||||
1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function addDataset(val: Array<string>) {
|
|
||||||
applicationForm.value.dataset_id_list = val
|
|
||||||
}
|
|
||||||
function openDatasetDialog() {
|
|
||||||
AddDatasetDialogRef.value.open(applicationForm.value.dataset_id_list)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDetail() {
|
|
||||||
application.asyncGetApplicationDetail(id, loading).then((res: any) => {
|
|
||||||
applicationForm.value = res.data
|
|
||||||
applicationForm.value.model_id = res.data.model
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDataset() {
|
|
||||||
if (id) {
|
|
||||||
application.asyncGetApplicationDataset(id, datasetLoading).then((res: any) => {
|
|
||||||
datasetList.value = res.data
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
dataset.asyncGetAllDataset(datasetLoading).then((res: any) => {
|
|
||||||
datasetList.value = res.data?.filter((v: any) => v.user_id === user.userInfo?.id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getModel() {
|
|
||||||
loading.value = true
|
|
||||||
if (id) {
|
|
||||||
applicationApi
|
|
||||||
.getApplicationModel(id)
|
|
||||||
.then((res: any) => {
|
|
||||||
modelOptions.value = groupBy(res?.data, 'provider')
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
model
|
|
||||||
.asyncGetModel()
|
|
||||||
.then((res: any) => {
|
|
||||||
modelOptions.value = groupBy(res?.data, 'provider')
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProvider() {
|
|
||||||
loading.value = true
|
|
||||||
model
|
|
||||||
.asyncGetProvider()
|
|
||||||
.then((res: any) => {
|
|
||||||
providerOptions.value = res?.data
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function refresh() {
|
|
||||||
getDataset()
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getProvider()
|
|
||||||
getModel()
|
|
||||||
getDataset()
|
|
||||||
if (id) {
|
|
||||||
getDetail()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.create-application {
|
|
||||||
.relate-dataset-card {
|
|
||||||
color: var(--app-text-color);
|
|
||||||
}
|
|
||||||
.dialog-bg {
|
|
||||||
border-radius: 8px;
|
|
||||||
background: var(--dialog-bg-gradient-color);
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.scrollbar-height-left {
|
|
||||||
height: calc(var(--app-main-height) - 127px);
|
|
||||||
}
|
|
||||||
.scrollbar-height {
|
|
||||||
height: calc(var(--app-main-height) - 150px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.prologue-md-editor {
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -28,8 +28,8 @@
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="configType === 'wechat'" class="flex align-center" style="margin-bottom: 8px">
|
<div v-if="configType === 'wechat'" class="flex align-center mb-16">
|
||||||
<span class="el-form-item__label">认证通过</span>
|
<span class="lighter mr-8">认证通过</span>
|
||||||
<el-switch v-if="configType === 'wechat'" v-model="form[configType].is_certification" />
|
<el-switch v-if="configType === 'wechat'" v-model="form[configType].is_certification" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,7 @@
|
||||||
<el-row :gutter="12" v-loading="loading">
|
<el-row :gutter="12" v-loading="loading">
|
||||||
<el-col :span="12" v-for="(item, index) in filterData" :key="index" class="mb-16">
|
<el-col :span="12" v-for="(item, index) in filterData" :key="index" class="mb-16">
|
||||||
<CardCheckbox value-field="id" :data="item" v-model="checkList" @change="changeHandle">
|
<CardCheckbox value-field="id" :data="item" v-model="checkList" @change="changeHandle">
|
||||||
<auto-tooltip :content="item.name" style="max-width: 170px">
|
<span class="ellipsis cursor" :title="item.name"> {{ item.name }}</span>
|
||||||
{{ item.name }}
|
|
||||||
</auto-tooltip>
|
|
||||||
</CardCheckbox>
|
</CardCheckbox>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,12 @@
|
||||||
style="background: none"
|
style="background: none"
|
||||||
class="mr-8"
|
class="mr-8"
|
||||||
>
|
>
|
||||||
<img :src="item?.icon" alt="" />
|
<el-image
|
||||||
|
:src="item?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="item?.name"
|
v-else-if="item?.name"
|
||||||
|
|
@ -175,7 +180,7 @@ function openCreateDialog() {
|
||||||
} else {
|
} else {
|
||||||
MsgConfirm(`提示`, '社区版最多支持 5 个应用,如需拥有更多应用,请升级为专业版。', {
|
MsgConfirm(`提示`, '社区版最多支持 5 个应用,如需拥有更多应用,请升级为专业版。', {
|
||||||
cancelButtonText: '确定',
|
cancelButtonText: '确定',
|
||||||
confirmButtonText: '购买专业版',
|
confirmButtonText: '购买专业版'
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.open('https://maxkb.cn/pricing.html', '_blank')
|
window.open('https://maxkb.cn/pricing.html', '_blank')
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ const currentPlatform = reactive<Platform>({
|
||||||
const formatFieldName = (key?: any): string => {
|
const formatFieldName = (key?: any): string => {
|
||||||
const fieldNames: { [key: string]: string } = {
|
const fieldNames: { [key: string]: string } = {
|
||||||
corp_id: 'Corp ID',
|
corp_id: 'Corp ID',
|
||||||
app_key: 'APP Key',
|
app_key: currentPlatform?.key != 'lark' ? 'APP Key' : 'App ID',
|
||||||
app_secret: 'APP Secret',
|
app_secret: 'APP Secret',
|
||||||
agent_id: 'Agent ID',
|
agent_id: 'Agent ID',
|
||||||
callback_url: '回调地址'
|
callback_url: '回调地址'
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<div v-if="item.isValid" class="border-t mt-16">
|
<div v-if="item.isValid" class="border-t mt-16">
|
||||||
<el-row :gutter="12" class="mt-16">
|
<el-row :gutter="12" class="mt-16">
|
||||||
<el-col v-for="(value, key) in item.config" :key="key" :span="12">
|
<el-col v-for="(value, key) in item.config" :key="key" :span="12">
|
||||||
<el-text type="info">{{ formatFieldName(key) }}</el-text>
|
<el-text type="info">{{ formatFieldName(key, item) }}</el-text>
|
||||||
<div class="mt-4 mb-16 flex align-center">
|
<div class="mt-4 mb-16 flex align-center">
|
||||||
<span
|
<span
|
||||||
v-if="key !== 'app_secret'"
|
v-if="key !== 'app_secret'"
|
||||||
|
|
@ -146,10 +146,10 @@ function createPlatform(key: string, name: string): Platform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatFieldName(key?: any): string {
|
function formatFieldName(key?: any, item?: Platform): string {
|
||||||
const fieldNames: { [key: string]: string } = {
|
const fieldNames: { [key: string]: string } = {
|
||||||
corp_id: 'Corp ID',
|
corp_id: 'Corp ID',
|
||||||
app_key: 'APP Key',
|
app_key: item?.key != 'lark' ? 'APP Key' : 'App ID',
|
||||||
app_secret: 'APP Secret',
|
app_secret: 'APP Secret',
|
||||||
agent_id: 'Agent ID',
|
agent_id: 'Agent ID',
|
||||||
callback_url: '回调地址'
|
callback_url: '回调地址'
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,12 @@
|
||||||
:size="32"
|
:size="32"
|
||||||
style="background: none"
|
style="background: none"
|
||||||
>
|
>
|
||||||
<img :src="application_profile?.icon" alt="" />
|
<el-image
|
||||||
|
:src="application_profile?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="application_profile?.name"
|
v-else-if="application_profile?.name"
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,12 @@
|
||||||
:size="32"
|
:size="32"
|
||||||
style="background: none"
|
style="background: none"
|
||||||
>
|
>
|
||||||
<img :src="applicationDetail?.icon" alt="" />
|
<el-image
|
||||||
|
:src="applicationDetail?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="applicationDetail?.name"
|
v-else-if="applicationDetail?.name"
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,12 @@
|
||||||
:size="32"
|
:size="32"
|
||||||
style="background: none"
|
style="background: none"
|
||||||
>
|
>
|
||||||
<img :src="applicationDetail?.icon" alt="" />
|
<el-image
|
||||||
|
:src="applicationDetail?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="applicationDetail?.name"
|
v-else-if="applicationDetail?.name"
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,12 @@
|
||||||
:size="32"
|
:size="32"
|
||||||
style="background: none"
|
style="background: none"
|
||||||
>
|
>
|
||||||
<img :src="applicationDetail?.icon" alt="" />
|
<el-image
|
||||||
|
:src="applicationDetail?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="applicationDetail?.name"
|
v-else-if="applicationDetail?.name"
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,12 @@
|
||||||
style="background: none"
|
style="background: none"
|
||||||
class="mr-12"
|
class="mr-12"
|
||||||
>
|
>
|
||||||
<img :src="item?.icon" alt="" />
|
<el-image
|
||||||
|
:src="item?.icon"
|
||||||
|
alt=""
|
||||||
|
fit="cover"
|
||||||
|
style="width: 32px; height: 32px; display: block"
|
||||||
|
/>
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<AppAvatar
|
<AppAvatar
|
||||||
v-else-if="item?.name"
|
v-else-if="item?.name"
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,15 @@
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
:disabled="props.manage"
|
:disabled="props.manage"
|
||||||
v-model="allChecked[TeamEnum.MANAGE]"
|
v-model="allChecked[TeamEnum.MANAGE]"
|
||||||
|
:indeterminate="allIndeterminate[TeamEnum.MANAGE]"
|
||||||
label="管理"
|
label="管理"
|
||||||
@change="handleCheckAllChange($event, TeamEnum.MANAGE)"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
:disabled="props.manage"
|
:disabled="props.manage"
|
||||||
v-model="row.operate[TeamEnum.MANAGE]"
|
v-model="row.operate[TeamEnum.MANAGE]"
|
||||||
@change="checkedOperateChange(TeamEnum.MANAGE, row)"
|
@change="(e: boolean) => checkedOperateChange(TeamEnum.MANAGE, row, e)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
@ -50,15 +50,15 @@
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
:disabled="props.manage"
|
:disabled="props.manage"
|
||||||
v-model="allChecked[TeamEnum.USE]"
|
v-model="allChecked[TeamEnum.USE]"
|
||||||
|
:indeterminate="allIndeterminate[TeamEnum.USE]"
|
||||||
label="查看"
|
label="查看"
|
||||||
@change="handleCheckAllChange($event, TeamEnum.USE)"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
:disabled="props.manage"
|
:disabled="props.manage"
|
||||||
v-model="row.operate[TeamEnum.USE]"
|
v-model="row.operate[TeamEnum.USE]"
|
||||||
@change="checkedOperateChange(TeamEnum.USE, row)"
|
@change="(e: boolean) => checkedOperateChange(TeamEnum.USE, row, e)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
@ -85,58 +85,67 @@ const isApplication = computed(() => props.type === TeamEnum.APPLICATION)
|
||||||
|
|
||||||
const emit = defineEmits(['update:data'])
|
const emit = defineEmits(['update:data'])
|
||||||
const allChecked: any = ref({
|
const allChecked: any = ref({
|
||||||
[TeamEnum.MANAGE]: false,
|
[TeamEnum.MANAGE]: computed({
|
||||||
[TeamEnum.USE]: false
|
get: () => {
|
||||||
|
return filterData.value.some((item: any) => item.operate[TeamEnum.MANAGE])
|
||||||
|
},
|
||||||
|
set: (val: boolean) => {
|
||||||
|
if (val) {
|
||||||
|
filterData.value.map((item: any) => {
|
||||||
|
item.operate[TeamEnum.MANAGE] = true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
filterData.value.map((item: any) => {
|
||||||
|
item.operate[TeamEnum.MANAGE] = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[TeamEnum.USE]: computed({
|
||||||
|
get: () => {
|
||||||
|
return filterData.value.some((item: any) => item.operate[TeamEnum.USE])
|
||||||
|
},
|
||||||
|
set: (val: boolean) => {
|
||||||
|
if (val) {
|
||||||
|
filterData.value.map((item: any) => {
|
||||||
|
item.operate[TeamEnum.USE] = true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
filterData.value.map((item: any) => {
|
||||||
|
item.operate[TeamEnum.USE] = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterText = ref('')
|
const filterText = ref('')
|
||||||
|
|
||||||
const filterData = computed(() => props.data.filter((v: any) => v.name.includes(filterText.value)))
|
const filterData = computed(() => props.data.filter((v: any) => v.name.includes(filterText.value)))
|
||||||
|
|
||||||
watch(
|
const allIndeterminate: any = ref({
|
||||||
() => props.data,
|
[TeamEnum.MANAGE]: computed(() => {
|
||||||
(val) => {
|
const all_not_checked = filterData.value.every((item: any) => !item.operate[TeamEnum.MANAGE])
|
||||||
Object.keys(allChecked.value).map((item) => {
|
if (all_not_checked) {
|
||||||
allChecked.value[item] = compare(item)
|
return false
|
||||||
})
|
}
|
||||||
emit('update:data', val)
|
return !filterData.value.every((item: any) => item.operate[TeamEnum.MANAGE])
|
||||||
},
|
}),
|
||||||
{
|
[TeamEnum.USE]: computed(() => {
|
||||||
deep: true
|
const all_not_checked = filterData.value.every((item: any) => !item.operate[TeamEnum.USE])
|
||||||
}
|
if (all_not_checked) {
|
||||||
)
|
return false
|
||||||
|
}
|
||||||
function handleCheckAllChange(val: string | number | boolean, Name: string | number) {
|
return !filterData.value.every((item: any) => item.operate[TeamEnum.USE])
|
||||||
if (val) {
|
|
||||||
props.data.map((item: any) => {
|
|
||||||
item.operate[Name] = true
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
props.data.map((item: any) => {
|
|
||||||
item.operate[Name] = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function checkedOperateChange(Name: string | number, row: any) {
|
|
||||||
if (Name === TeamEnum.MANAGE && row.operate[TeamEnum.MANAGE]) {
|
|
||||||
props.data.map((item: any) => {
|
|
||||||
if (item.id === row.id) {
|
|
||||||
item.operate[TeamEnum.USE] = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
allChecked.value[Name] = compare(Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
function compare(attrs: string | number) {
|
|
||||||
const filterData = props.data.filter((item: any) => item?.operate[attrs])
|
|
||||||
return props.data.length > 0 && filterData.length === props.data.length
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
Object.keys(allChecked.value).map((item) => {
|
|
||||||
allChecked.value[item] = compare(item)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function checkedOperateChange(Name: string | number, row: any, e: boolean) {
|
||||||
|
props.data.map((item: any) => {
|
||||||
|
if (item.id === row.id) {
|
||||||
|
item.operate[Name] = e
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scope></style>
|
<style lang="scss" scope></style>
|
||||||
|
|
|
||||||
|
|
@ -26,19 +26,13 @@
|
||||||
<h4 v-else>{{ nodeModel.properties.stepName }}</h4>
|
<h4 v-else>{{ nodeModel.properties.stepName }}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div @mousemove.stop @mousedown.stop @keydown.stop @click.stop>
|
||||||
@mousemove.stop
|
|
||||||
@mousedown.stop
|
|
||||||
@keydown.stop
|
|
||||||
@click.stop
|
|
||||||
v-if="showOperate(nodeModel.type)"
|
|
||||||
>
|
|
||||||
<el-button text @click="showNode = !showNode" class="mr-4">
|
<el-button text @click="showNode = !showNode" class="mr-4">
|
||||||
<el-icon class="arrow-icon" :class="showNode ? 'rotate-180' : ''"
|
<el-icon class="arrow-icon" :class="showNode ? 'rotate-180' : ''"
|
||||||
><ArrowDownBold />
|
><ArrowDownBold />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-dropdown :teleported="false" trigger="click">
|
<el-dropdown v-if="showOperate(nodeModel.type)" :teleported="false" trigger="click">
|
||||||
<el-button text>
|
<el-button text>
|
||||||
<el-icon class="color-secondary"><MoreFilled /></el-icon>
|
<el-icon class="color-secondary"><MoreFilled /></el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
@ -52,7 +46,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-collapse-transition>
|
<el-collapse-transition>
|
||||||
<div @mousedown.stop @keydown.stop @click.stop v-if="showNode" class="mt-16">
|
<div @mousedown.stop @keydown.stop @click.stop v-show="showNode" class="mt-16">
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="node_status != 200"
|
v-if="node_status != 200"
|
||||||
class="mb-16"
|
class="mb-16"
|
||||||
|
|
@ -130,7 +124,19 @@ const height = ref<{
|
||||||
})
|
})
|
||||||
const showAnchor = ref<boolean>(false)
|
const showAnchor = ref<boolean>(false)
|
||||||
const anchorData = ref<any>()
|
const anchorData = ref<any>()
|
||||||
const showNode = ref<boolean>(true)
|
// const showNode = ref<boolean>(true)
|
||||||
|
const showNode = computed({
|
||||||
|
set: (v) => {
|
||||||
|
set(props.nodeModel.properties, 'showNode', v)
|
||||||
|
},
|
||||||
|
get: () => {
|
||||||
|
if (props.nodeModel.properties.showNode !== undefined) {
|
||||||
|
return props.nodeModel.properties.showNode
|
||||||
|
}
|
||||||
|
set(props.nodeModel.properties, 'showNode', true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
const node_status = computed(() => {
|
const node_status = computed(() => {
|
||||||
if (props.nodeModel.properties.status) {
|
if (props.nodeModel.properties.status) {
|
||||||
return props.nodeModel.properties.status
|
return props.nodeModel.properties.status
|
||||||
|
|
|
||||||
|
|
@ -234,13 +234,14 @@ class AppNodeModel extends HtmlResize.model {
|
||||||
}
|
}
|
||||||
getDefaultAnchor() {
|
getDefaultAnchor() {
|
||||||
const { id, x, y, width } = this
|
const { id, x, y, width } = this
|
||||||
|
const showNode = this.properties.showNode === undefined ? true : this.properties.showNode
|
||||||
const anchors: any = []
|
const anchors: any = []
|
||||||
|
|
||||||
if (this.type !== WorkflowType.Base) {
|
if (this.type !== WorkflowType.Base) {
|
||||||
if (this.type !== WorkflowType.Start) {
|
if (this.type !== WorkflowType.Start) {
|
||||||
anchors.push({
|
anchors.push({
|
||||||
x: x - width / 2 + 10,
|
x: x - width / 2 + 10,
|
||||||
y: y,
|
y: showNode ? y : y - 15,
|
||||||
id: `${id}_left`,
|
id: `${id}_left`,
|
||||||
edgeAddable: false,
|
edgeAddable: false,
|
||||||
type: 'left'
|
type: 'left'
|
||||||
|
|
@ -248,7 +249,7 @@ class AppNodeModel extends HtmlResize.model {
|
||||||
}
|
}
|
||||||
anchors.push({
|
anchors.push({
|
||||||
x: x + width / 2 - 10,
|
x: x + width / 2 - 10,
|
||||||
y: y,
|
y: showNode ? y : y - 15,
|
||||||
id: `${id}_right`,
|
id: `${id}_right`,
|
||||||
type: 'right'
|
type: 'right'
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,11 @@ class ConditionModel extends AppNodeModel {
|
||||||
if (this.height === undefined) {
|
if (this.height === undefined) {
|
||||||
this.height = 200
|
this.height = 200
|
||||||
}
|
}
|
||||||
|
const showNode = this.properties.showNode === undefined ? true : this.properties.showNode
|
||||||
const anchors: any = []
|
const anchors: any = []
|
||||||
anchors.push({
|
anchors.push({
|
||||||
x: x - width / 2 + 10,
|
x: x - width / 2 + 10,
|
||||||
y: y,
|
y: showNode ? y : y - 15,
|
||||||
id: `${id}_left`,
|
id: `${id}_left`,
|
||||||
edgeAddable: false,
|
edgeAddable: false,
|
||||||
type: 'left'
|
type: 'left'
|
||||||
|
|
@ -50,7 +51,7 @@ class ConditionModel extends AppNodeModel {
|
||||||
const h = get_up_index_height(branch_condition_list, index)
|
const h = get_up_index_height(branch_condition_list, index)
|
||||||
anchors.push({
|
anchors.push({
|
||||||
x: x + width / 2 - 10,
|
x: x + width / 2 - 10,
|
||||||
y: y - height / 2 + 75 + h + element.height / 2,
|
y: showNode ? y - height / 2 + 75 + h + element.height / 2 : y - 15,
|
||||||
id: `${id}_${element.id}_right`,
|
id: `${id}_${element.id}_right`,
|
||||||
type: 'right'
|
type: 'right'
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue