Merge branch 'main' of https://github.com/maxkb-dev/maxkb
This commit is contained in:
commit
01729157b7
84
apps/common/middleware/gzip.py
Normal file
84
apps/common/middleware/gzip.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
"""
|
||||||
|
@project: MaxKB
|
||||||
|
@Author:虎
|
||||||
|
@file: gzip.py
|
||||||
|
@date:2025/2/27 10:03
|
||||||
|
@desc:
|
||||||
|
"""
|
||||||
|
from django.utils.cache import patch_vary_headers
|
||||||
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
|
from django.utils.regex_helper import _lazy_re_compile
|
||||||
|
from django.utils.text import compress_sequence, compress_string
|
||||||
|
|
||||||
|
re_accepts_gzip = _lazy_re_compile(r"\bgzip\b")
|
||||||
|
|
||||||
|
|
||||||
|
class GZipMiddleware(MiddlewareMixin):
|
||||||
|
"""
|
||||||
|
Compress content if the browser allows gzip compression.
|
||||||
|
Set the Vary header accordingly, so that caches will base their storage
|
||||||
|
on the Accept-Encoding header.
|
||||||
|
"""
|
||||||
|
|
||||||
|
max_random_bytes = 100
|
||||||
|
|
||||||
|
def process_response(self, request, response):
|
||||||
|
if request.method != 'GET' or request.path.startswith('/api'):
|
||||||
|
return response
|
||||||
|
# It's not worth attempting to compress really short responses.
|
||||||
|
if not response.streaming and len(response.content) < 200:
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Avoid gzipping if we've already got a content-encoding.
|
||||||
|
if response.has_header("Content-Encoding"):
|
||||||
|
return response
|
||||||
|
|
||||||
|
patch_vary_headers(response, ("Accept-Encoding",))
|
||||||
|
|
||||||
|
ae = request.META.get("HTTP_ACCEPT_ENCODING", "")
|
||||||
|
if not re_accepts_gzip.search(ae):
|
||||||
|
return response
|
||||||
|
|
||||||
|
if response.streaming:
|
||||||
|
if response.is_async:
|
||||||
|
# pull to lexical scope to capture fixed reference in case
|
||||||
|
# streaming_content is set again later.
|
||||||
|
orignal_iterator = response.streaming_content
|
||||||
|
|
||||||
|
async def gzip_wrapper():
|
||||||
|
async for chunk in orignal_iterator:
|
||||||
|
yield compress_string(
|
||||||
|
chunk,
|
||||||
|
max_random_bytes=self.max_random_bytes,
|
||||||
|
)
|
||||||
|
|
||||||
|
response.streaming_content = gzip_wrapper()
|
||||||
|
else:
|
||||||
|
response.streaming_content = compress_sequence(
|
||||||
|
response.streaming_content,
|
||||||
|
max_random_bytes=self.max_random_bytes,
|
||||||
|
)
|
||||||
|
# Delete the `Content-Length` header for streaming content, because
|
||||||
|
# we won't know the compressed size until we stream it.
|
||||||
|
del response.headers["Content-Length"]
|
||||||
|
else:
|
||||||
|
# Return the compressed content only if it's actually shorter.
|
||||||
|
compressed_content = compress_string(
|
||||||
|
response.content,
|
||||||
|
max_random_bytes=self.max_random_bytes,
|
||||||
|
)
|
||||||
|
if len(compressed_content) >= len(response.content):
|
||||||
|
return response
|
||||||
|
response.content = compressed_content
|
||||||
|
response.headers["Content-Length"] = str(len(response.content))
|
||||||
|
|
||||||
|
# If there is a strong ETag, make it weak to fulfill the requirements
|
||||||
|
# of RFC 9110 Section 8.8.1 while also allowing conditional request
|
||||||
|
# matches on ETags.
|
||||||
|
etag = response.get("ETag")
|
||||||
|
if etag and etag.startswith('"'):
|
||||||
|
response.headers["ETag"] = "W/" + etag
|
||||||
|
response.headers["Content-Encoding"] = "gzip"
|
||||||
|
|
||||||
|
return response
|
||||||
@ -1106,7 +1106,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||||||
'order_by_query': QuerySet(Document).order_by('-create_time', 'id')
|
'order_by_query': QuerySet(Document).order_by('-create_time', 'id')
|
||||||
}, select_string=get_file_content(
|
}, select_string=get_file_content(
|
||||||
os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'list_document.sql')),
|
os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'list_document.sql')),
|
||||||
with_search_one=False), dataset_id
|
with_search_one=False), dataset_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _batch_sync(document_id_list: List[str]):
|
def _batch_sync(document_id_list: List[str]):
|
||||||
@ -1263,6 +1263,7 @@ def save_image(image_list):
|
|||||||
exist_image_list = [str(i.get('id')) for i in
|
exist_image_list = [str(i.get('id')) for i in
|
||||||
QuerySet(Image).filter(id__in=[i.id for i in image_list]).values('id')]
|
QuerySet(Image).filter(id__in=[i.id for i in image_list]).values('id')]
|
||||||
save_image_list = [image for image in image_list if not exist_image_list.__contains__(str(image.id))]
|
save_image_list = [image for image in image_list if not exist_image_list.__contains__(str(image.id))]
|
||||||
|
save_image_list = list({img.id: img for img in save_image_list}.values())
|
||||||
if len(save_image_list) > 0:
|
if len(save_image_list) > 0:
|
||||||
QuerySet(Image).bulk_create(save_image_list)
|
QuerySet(Image).bulk_create(save_image_list)
|
||||||
|
|
||||||
|
|||||||
@ -55,7 +55,7 @@ MIDDLEWARE = [
|
|||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.gzip.GZipMiddleware',
|
'common.middleware.gzip.GZipMiddleware',
|
||||||
'common.middleware.static_headers_middleware.StaticHeadersMiddleware',
|
'common.middleware.static_headers_middleware.StaticHeadersMiddleware',
|
||||||
'common.middleware.cross_domain_middleware.CrossDomainMiddleware'
|
'common.middleware.cross_domain_middleware.CrossDomainMiddleware'
|
||||||
|
|
||||||
|
|||||||
@ -454,6 +454,7 @@
|
|||||||
:data="paragraph.metadata"
|
:data="paragraph.metadata"
|
||||||
:content="paragraph.page_content"
|
:content="paragraph.page_content"
|
||||||
:index="paragraphIndex"
|
:index="paragraphIndex"
|
||||||
|
:score="paragraph.metadata?.relevance_score"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -9,10 +9,10 @@
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<AppAvatar class="mr-12 avatar-light" :size="22"> {{ index + 1 + '' }}</AppAvatar>
|
<AppAvatar class="mr-12 avatar-light" :size="22"> {{ index + 1 + '' }}</AppAvatar>
|
||||||
</template>
|
</template>
|
||||||
<div class="active-button primary">{{ data.similarity?.toFixed(3) }}</div>
|
<div class="active-button primary">{{ score?.toFixed(3) || data.similarity?.toFixed(3) }}</div>
|
||||||
<template #description>
|
<template #description>
|
||||||
<el-scrollbar height="150">
|
<el-scrollbar height="150">
|
||||||
<MdPreview ref="editorRef" editorId="preview-only" :modelValue="content" noImgZoomIn/>
|
<MdPreview ref="editorRef" editorId="preview-only" :modelValue="content" noImgZoomIn />
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -69,6 +69,10 @@ const props = defineProps({
|
|||||||
index: {
|
index: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
|
},
|
||||||
|
score: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const isMetaObject = computed(() => typeof props.data.meta === 'object')
|
const isMetaObject = computed(() => typeof props.data.meta === 'object')
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<LogoIcon v-else height="32px" width="32px" />
|
<LogoIcon v-else height="32px" width="32px" />
|
||||||
</div>
|
</div>
|
||||||
<div class="content" @mouseup="openControl">
|
<div class="content" @mouseup="openControl">
|
||||||
<el-card shadow="always" class="dialog-card mb-8">
|
<el-card shadow="always" class="mb-8 border-r-8">
|
||||||
<MdRenderer
|
<MdRenderer
|
||||||
v-if="
|
v-if="
|
||||||
(chatRecord.write_ed === undefined || chatRecord.write_ed === true) &&
|
(chatRecord.write_ed === undefined || chatRecord.write_ed === true) &&
|
||||||
@ -27,10 +27,10 @@
|
|||||||
:send-message="chatMessage"
|
:send-message="chatMessage"
|
||||||
></MdRenderer>
|
></MdRenderer>
|
||||||
</template>
|
</template>
|
||||||
<span v-else-if="chatRecord.is_stop" shadow="always" class="dialog-card">
|
<span v-else-if="chatRecord.is_stop" shadow="always">
|
||||||
{{ $t('chat.tip.stopAnswer') }}
|
{{ $t('chat.tip.stopAnswer') }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else shadow="always" class="dialog-card">
|
<span v-else shadow="always">
|
||||||
{{ $t('chat.tip.answerLoading') }} <span class="dotting"></span>
|
{{ $t('chat.tip.answerLoading') }} <span class="dotting"></span>
|
||||||
</span>
|
</span>
|
||||||
<!-- 知识来源 -->
|
<!-- 知识来源 -->
|
||||||
|
|||||||
@ -17,27 +17,41 @@ import { ref, nextTick, onMounted } from 'vue'
|
|||||||
import { t } from '@/locales'
|
import { t } from '@/locales'
|
||||||
const isOpen = ref<boolean>(false)
|
const isOpen = ref<boolean>(false)
|
||||||
const eventVal = ref<any>({})
|
const eventVal = ref<any>({})
|
||||||
|
|
||||||
function getSelection() {
|
function getSelection() {
|
||||||
const selection = window.getSelection()
|
const selection = window.getSelection()
|
||||||
if (selection && selection.anchorNode == null) {
|
if (selection) {
|
||||||
return null
|
if (selection.rangeCount === 0) return undefined
|
||||||
|
const range = selection.getRangeAt(0)
|
||||||
|
const fragment = range.cloneContents() // 克隆选区内容
|
||||||
|
const div = document.createElement('div')
|
||||||
|
div.appendChild(fragment)
|
||||||
|
if (div.textContent) {
|
||||||
|
return div.textContent.trim()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const text = selection?.anchorNode?.textContent
|
return undefined
|
||||||
return text && text.substring(selection.anchorOffset, selection.focusOffset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开控制台
|
* 打开控制台
|
||||||
* @param event
|
* @param event
|
||||||
*/
|
*/
|
||||||
const openControl = (event: any) => {
|
const openControl = (event: any) => {
|
||||||
const c = getSelection()
|
const c = getSelection()
|
||||||
isOpen.value = false
|
|
||||||
if (c) {
|
if (c) {
|
||||||
nextTick(() => {
|
if (!isOpen.value) {
|
||||||
eventVal.value = event
|
nextTick(() => {
|
||||||
isOpen.value = true
|
eventVal.value = event
|
||||||
})
|
isOpen.value = true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
clearSelectedText()
|
||||||
|
isOpen.value = false
|
||||||
|
}
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
} else {
|
||||||
|
isOpen.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<LogoIcon v-else height="32px" width="32px" />
|
<LogoIcon v-else height="32px" width="32px" />
|
||||||
</div>
|
</div>
|
||||||
<div class="content" v-if="prologue">
|
<div class="content" v-if="prologue">
|
||||||
<el-card shadow="always" class="dialog-card" style="--el-card-padding: 10px 16px 12px">
|
<el-card shadow="always" class="border-r-8" style="--el-card-padding: 10px 16px 12px">
|
||||||
<MdRenderer
|
<MdRenderer
|
||||||
:source="prologue"
|
:source="prologue"
|
||||||
:send-message="sendMessage"
|
:send-message="sendMessage"
|
||||||
|
|||||||
@ -1,20 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 问题内容 -->
|
<!-- 问题内容 -->
|
||||||
<div class="question-content item-content mb-16 lighter">
|
<div class="question-content item-content mb-16 lighter">
|
||||||
<div class="content mr-16">
|
<div
|
||||||
|
class="content mr-12 p-12-16 border-r-8"
|
||||||
|
:class="document_list.length >= 2 ? 'media_2' : `media_${document_list.length}`"
|
||||||
|
>
|
||||||
<div class="text break-all pre-wrap">
|
<div class="text break-all pre-wrap">
|
||||||
<div class="mb-8" v-if="document_list.length">
|
<div class="mb-8" v-if="document_list.length">
|
||||||
<el-row :gutter="10">
|
<el-space wrap class="w-full media-file-width">
|
||||||
<el-col
|
<template v-for="(item, index) in document_list" :key="index">
|
||||||
v-for="(item, index) in document_list"
|
|
||||||
:key="index"
|
|
||||||
:xs="24"
|
|
||||||
:sm="props.type === 'debug-ai-chat' ? 24 : 12"
|
|
||||||
:md="props.type === 'debug-ai-chat' ? 24 : 12"
|
|
||||||
:lg="props.type === 'debug-ai-chat' ? 24 : 12"
|
|
||||||
:xl="props.type === 'debug-ai-chat' ? 24 : 12"
|
|
||||||
class="mb-8 w-full"
|
|
||||||
>
|
|
||||||
<el-card shadow="never" style="--el-card-padding: 8px" class="download-file cursor">
|
<el-card shadow="never" style="--el-card-padding: 8px" class="download-file cursor">
|
||||||
<div class="download-button flex align-center" @click="downloadFile(item)">
|
<div class="download-button flex align-center" @click="downloadFile(item)">
|
||||||
<el-icon class="mr-4">
|
<el-icon class="mr-4">
|
||||||
@ -29,8 +23,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</template>
|
||||||
</el-row>
|
</el-space>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-8" v-if="image_list.length">
|
<div class="mb-8" v-if="image_list.length">
|
||||||
<el-space wrap>
|
<el-space wrap>
|
||||||
@ -53,17 +47,8 @@
|
|||||||
</el-space>
|
</el-space>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-8" v-if="audio_list.length">
|
<div class="mb-8" v-if="audio_list.length">
|
||||||
<el-row :gutter="10">
|
<el-space wrap>
|
||||||
<el-col
|
<template v-for="(item, index) in audio_list" :key="index">
|
||||||
v-for="(item, index) in audio_list"
|
|
||||||
:key="index"
|
|
||||||
:xs="24"
|
|
||||||
:sm="props.type === 'debug-ai-chat' ? 24 : 12"
|
|
||||||
:md="props.type === 'debug-ai-chat' ? 24 : 12"
|
|
||||||
:lg="props.type === 'debug-ai-chat' ? 24 : 12"
|
|
||||||
:xl="props.type === 'debug-ai-chat' ? 24 : 12"
|
|
||||||
class="mb-8"
|
|
||||||
>
|
|
||||||
<div class="file cursor border-r-4" v-if="item.url">
|
<div class="file cursor border-r-4" v-if="item.url">
|
||||||
<audio
|
<audio
|
||||||
:src="item.url"
|
:src="item.url"
|
||||||
@ -72,10 +57,10 @@
|
|||||||
class="border-r-4"
|
class="border-r-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</template>
|
||||||
</el-row>
|
</el-space>
|
||||||
</div>
|
</div>
|
||||||
{{ chatRecord.problem_text }}
|
<span> {{ chatRecord.problem_text }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
@ -140,6 +125,16 @@ onMounted(() => {})
|
|||||||
.question-content {
|
.question-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
padding-left: var(--padding-left);
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background: #d6e2ff;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.download-file {
|
.download-file {
|
||||||
height: 43px;
|
height: 43px;
|
||||||
@ -163,5 +158,44 @@ onMounted(() => {})
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.media-file-width {
|
||||||
|
:deep(.el-space__item) {
|
||||||
|
min-width: 40% !important;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.media_2 {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.media_0 {
|
||||||
|
flex: inherit;
|
||||||
|
}
|
||||||
|
.media_1 {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
.question-content {
|
||||||
|
.media-file-width {
|
||||||
|
:deep(.el-space__item) {
|
||||||
|
min-width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.media_1 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.debug-ai-chat {
|
||||||
|
.question-content {
|
||||||
|
.media-file-width {
|
||||||
|
:deep(.el-space__item) {
|
||||||
|
min-width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.media_1 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
class="mb-16"
|
class="mb-16"
|
||||||
style="padding: 0 24px"
|
style="padding: 0 24px"
|
||||||
>
|
>
|
||||||
<el-card shadow="always" class="dialog-card" style="--el-card-padding: 16px 8px">
|
<el-card shadow="always" class="border-r-8" style="--el-card-padding: 16px 8px">
|
||||||
<div
|
<div
|
||||||
class="flex align-center cursor w-full"
|
class="flex align-center cursor w-full"
|
||||||
style="padding: 0 8px"
|
style="padding: 0 8px"
|
||||||
|
|||||||
@ -17,15 +17,12 @@
|
|||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding-left: var(--padding-left);
|
padding-left: var(--padding-left);
|
||||||
|
padding-right: var(--padding-left);
|
||||||
|
|
||||||
:deep(ol) {
|
:deep(ol) {
|
||||||
margin-left: 16px !important;
|
margin-left: 16px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
|
||||||
padding: 6px 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&__operate {
|
&__operate {
|
||||||
background: #f3f7f9;
|
background: #f3f7f9;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="aiChatRef" class="ai-chat" :class="type == 'log' ? 'chart-log' : ''">
|
<div ref="aiChatRef" class="ai-chat" :class="type">
|
||||||
<UserForm
|
<UserForm
|
||||||
v-model:api_form_data="api_form_data"
|
v-model:api_form_data="api_form_data"
|
||||||
v-model:form_data="form_data"
|
v-model:form_data="form_data"
|
||||||
|
|||||||
@ -311,6 +311,9 @@ h5 {
|
|||||||
.border-r-4 {
|
.border-r-4 {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
.border-r-8 {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.border-t-dashed {
|
.border-t-dashed {
|
||||||
border-top: 1px dashed var(--el-border-color);
|
border-top: 1px dashed var(--el-border-color);
|
||||||
@ -761,4 +764,4 @@ h5 {
|
|||||||
.responsive-dialog {
|
.responsive-dialog {
|
||||||
width: 90% !important;
|
width: 90% !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,7 +101,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="chat-pc__right">
|
<div class="chat-pc__right">
|
||||||
<div class="right-header border-b mb-24 p-16-24 flex-between">
|
<div class="right-header border-b mb-24 p-16-24 flex-between">
|
||||||
<h4 class="ellipsis-1" style="width: 70%">
|
<h4 class="ellipsis-1" style="width: 66%">
|
||||||
{{ currentChatName }}
|
{{ currentChatName }}
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
label-position="top"
|
label-position="top"
|
||||||
require-asterisk-position="right"
|
require-asterisk-position="right"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
|
@submit.prevent
|
||||||
>
|
>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
:label="$t('views.functionLib.functionForm.form.functionName.label')"
|
:label="$t('views.functionLib.functionForm.form.functionName.label')"
|
||||||
|
|||||||
@ -209,11 +209,14 @@ const nodeCascaderRef = ref()
|
|||||||
const nodeCascaderRef2 = ref()
|
const nodeCascaderRef2 = ref()
|
||||||
const validate = async () => {
|
const validate = async () => {
|
||||||
// console.log(replyNodeFormRef.value.validate())
|
// console.log(replyNodeFormRef.value.validate())
|
||||||
return Promise.all([
|
let ps = [
|
||||||
replyNodeFormRef.value?.validate(),
|
replyNodeFormRef.value?.validate(),
|
||||||
...nodeCascaderRef.value.map((item: any) => item.validate()),
|
...nodeCascaderRef.value.map((item: any) => item.validate())
|
||||||
...nodeCascaderRef2.value.map((item: any) => item.validate())
|
]
|
||||||
]).catch((err: any) => {
|
if (nodeCascaderRef2.value) {
|
||||||
|
ps = [...ps, ...nodeCascaderRef.value.map((item: any) => item.validate())]
|
||||||
|
}
|
||||||
|
return Promise.all(ps).catch((err: any) => {
|
||||||
return Promise.reject({ node: props.nodeModel, errMessage: err })
|
return Promise.reject({ node: props.nodeModel, errMessage: err })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user