feat:修复知识库问题
parent
c142297f41
commit
4c9ac22a7f
|
|
@ -40,6 +40,8 @@ def get_embedding_id(knowledge_id_list):
|
||||||
_("The vector model of the associated knowledge base is inconsistent and the segmentation cannot be recalled."))
|
_("The vector model of the associated knowledge base is inconsistent and the segmentation cannot be recalled."))
|
||||||
if len(knowledge_list) == 0:
|
if len(knowledge_list) == 0:
|
||||||
raise Exception(_("The knowledge base setting is wrong, please reset the knowledge base"))
|
raise Exception(_("The knowledge base setting is wrong, please reset the knowledge base"))
|
||||||
|
if knowledge_list[0].embedding_model_id is None:
|
||||||
|
raise Exception(_("The knowledge base setting is wrong, please reset the knowledge base"))
|
||||||
return knowledge_list[0].embedding_model_id
|
return knowledge_list[0].embedding_model_id
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ def get_embedding_id(dataset_id_list):
|
||||||
raise Exception("关联知识库的向量模型不一致,无法召回分段。")
|
raise Exception("关联知识库的向量模型不一致,无法召回分段。")
|
||||||
if len(dataset_list) == 0:
|
if len(dataset_list) == 0:
|
||||||
raise Exception("知识库设置错误,请重新设置知识库")
|
raise Exception("知识库设置错误,请重新设置知识库")
|
||||||
|
if dataset_list[0].embedding_model_id is None:
|
||||||
|
raise Exception("Knowledge base setting error, please reset the knowledge base")
|
||||||
return dataset_list[0].embedding_model_id
|
return dataset_list[0].embedding_model_id
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ from django.db.models import QuerySet
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from application.models import ApplicationAccessToken, ChatUserType, Application, ApplicationVersion
|
from application.models import ApplicationAccessToken, ChatUserType, Application, ApplicationVersion, \
|
||||||
|
ApplicationKnowledgeMapping
|
||||||
from application.models.application_api_key import ApplicationApiKey
|
from application.models.application_api_key import ApplicationApiKey
|
||||||
from application.serializers.application import ApplicationSerializerModel
|
from application.serializers.application import ApplicationSerializerModel
|
||||||
from common.auth.common import ChatUserToken, ChatAuthentication
|
from common.auth.common import ChatUserToken, ChatAuthentication
|
||||||
|
|
@ -23,6 +24,7 @@ from common.constants.cache_version import Cache_Version
|
||||||
from common.database_model_manage.database_model_manage import DatabaseModelManage
|
from common.database_model_manage.database_model_manage import DatabaseModelManage
|
||||||
from common.exception.app_exception import NotFound404, AppUnauthorizedFailed
|
from common.exception.app_exception import NotFound404, AppUnauthorizedFailed
|
||||||
from common.utils.rsa_util import get_key_pair_by_sql
|
from common.utils.rsa_util import get_key_pair_by_sql
|
||||||
|
from knowledge.models import Knowledge, Problem
|
||||||
|
|
||||||
|
|
||||||
class AnonymousAuthenticationSerializer(serializers.Serializer):
|
class AnonymousAuthenticationSerializer(serializers.Serializer):
|
||||||
|
|
@ -173,6 +175,61 @@ class ApplicationProfileSerializer(serializers.Serializer):
|
||||||
_v = getattr(application_version, version_field)
|
_v = getattr(application_version, version_field)
|
||||||
setattr(application, app_field, _v)
|
setattr(application, app_field, _v)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_work_flow_knowledge_id_list(work_flow):
|
||||||
|
knowledge_id_list = []
|
||||||
|
knowledge_keys = {
|
||||||
|
'knowledge_id',
|
||||||
|
'knowledge_id_list',
|
||||||
|
'dataset_id',
|
||||||
|
'dataset_id_list',
|
||||||
|
'all_knowledge_id_list',
|
||||||
|
}
|
||||||
|
|
||||||
|
def collect(data):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
for key, value in data.items():
|
||||||
|
if key in knowledge_keys:
|
||||||
|
if isinstance(value, list):
|
||||||
|
knowledge_id_list.extend([str(item) for item in value if item])
|
||||||
|
elif value:
|
||||||
|
knowledge_id_list.append(str(value))
|
||||||
|
else:
|
||||||
|
collect(value)
|
||||||
|
elif isinstance(data, list):
|
||||||
|
for item in data:
|
||||||
|
collect(item)
|
||||||
|
|
||||||
|
collect(work_flow or {})
|
||||||
|
return list(dict.fromkeys(knowledge_id_list))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_suggested_questions(application):
|
||||||
|
knowledge_id_list = [str(knowledge_id) for knowledge_id in QuerySet(ApplicationKnowledgeMapping).filter(
|
||||||
|
application_id=application.id
|
||||||
|
).values_list('knowledge_id', flat=True)]
|
||||||
|
knowledge_id_list.extend(ApplicationProfileSerializer.get_work_flow_knowledge_id_list(application.work_flow))
|
||||||
|
knowledge_id_list = list(dict.fromkeys(knowledge_id_list))
|
||||||
|
knowledge_id_list = [str(knowledge_id) for knowledge_id in QuerySet(Knowledge).filter(
|
||||||
|
id__in=knowledge_id_list
|
||||||
|
).values_list('id', flat=True)]
|
||||||
|
problem_query_set = QuerySet(Problem)
|
||||||
|
if len(knowledge_id_list) > 0:
|
||||||
|
problem_query_set = problem_query_set.filter(knowledge_id__in=knowledge_id_list)
|
||||||
|
else:
|
||||||
|
problem_query_set = problem_query_set.filter(knowledge__workspace_id=application.workspace_id)
|
||||||
|
problem_list = problem_query_set.exclude(content='').order_by(
|
||||||
|
'-hit_num', '-create_time'
|
||||||
|
).values_list('content', flat=True)
|
||||||
|
result = []
|
||||||
|
for content in problem_list:
|
||||||
|
content = (content or '').strip()
|
||||||
|
if content and content not in result:
|
||||||
|
result.append(content)
|
||||||
|
if len(result) >= 3:
|
||||||
|
break
|
||||||
|
return result
|
||||||
|
|
||||||
def profile(self, with_valid=True):
|
def profile(self, with_valid=True):
|
||||||
if with_valid:
|
if with_valid:
|
||||||
self.is_valid()
|
self.is_valid()
|
||||||
|
|
@ -219,6 +276,7 @@ class ApplicationProfileSerializer(serializers.Serializer):
|
||||||
'show_user_avatar': application_setting.show_user_avatar,
|
'show_user_avatar': application_setting.show_user_avatar,
|
||||||
'float_location': application_setting.float_location,
|
'float_location': application_setting.float_location,
|
||||||
'chat_background': application_setting.chat_background}
|
'chat_background': application_setting.chat_background}
|
||||||
|
suggested_questions = self.get_suggested_questions(application)
|
||||||
base_node = [node for node in ((application.work_flow or {}).get('nodes', []) or []) if
|
base_node = [node for node in ((application.work_flow or {}).get('nodes', []) or []) if
|
||||||
node.get('id') == 'base-node']
|
node.get('id') == 'base-node']
|
||||||
return {**ApplicationSerializerModel(application).data,
|
return {**ApplicationSerializerModel(application).data,
|
||||||
|
|
@ -232,6 +290,7 @@ class ApplicationProfileSerializer(serializers.Serializer):
|
||||||
'file_upload_enable': application.file_upload_enable,
|
'file_upload_enable': application.file_upload_enable,
|
||||||
'file_upload_setting': application.file_upload_setting,
|
'file_upload_setting': application.file_upload_setting,
|
||||||
'work_flow': {'nodes': base_node} if base_node else None,
|
'work_flow': {'nodes': base_node} if base_node else None,
|
||||||
|
'suggested_questions': suggested_questions,
|
||||||
'show_source': application_access_token.show_source,
|
'show_source': application_access_token.show_source,
|
||||||
'show_exec': application_access_token.show_exec,
|
'show_exec': application_access_token.show_exec,
|
||||||
'language': application_access_token.language,
|
'language': application_access_token.language,
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,8 @@ def get_embedding_model_by_knowledge_id_list(knowledge_id_list: List):
|
||||||
raise Exception(_('The knowledge base is inconsistent with the vector model'))
|
raise Exception(_('The knowledge base is inconsistent with the vector model'))
|
||||||
if len(knowledge_list) == 0:
|
if len(knowledge_list) == 0:
|
||||||
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
||||||
|
if knowledge_list[0].embedding_model_id is None or knowledge_list[0].embedding_model is None:
|
||||||
|
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
||||||
|
|
||||||
default_params = get_model_default_params(knowledge_list[0].embedding_model)
|
default_params = get_model_default_params(knowledge_list[0].embedding_model)
|
||||||
|
|
||||||
|
|
@ -130,6 +132,8 @@ def get_embedding_model_by_knowledge_id_list(knowledge_id_list: List):
|
||||||
|
|
||||||
def get_embedding_model_by_knowledge_id(knowledge_id: str):
|
def get_embedding_model_by_knowledge_id(knowledge_id: str):
|
||||||
knowledge = QuerySet(Knowledge).select_related('embedding_model').filter(id=knowledge_id).first()
|
knowledge = QuerySet(Knowledge).select_related('embedding_model').filter(id=knowledge_id).first()
|
||||||
|
if knowledge is None or knowledge.embedding_model_id is None or knowledge.embedding_model is None:
|
||||||
|
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
||||||
|
|
||||||
default_params = get_model_default_params(knowledge.embedding_model)
|
default_params = get_model_default_params(knowledge.embedding_model)
|
||||||
|
|
||||||
|
|
@ -138,6 +142,8 @@ def get_embedding_model_by_knowledge_id(knowledge_id: str):
|
||||||
|
|
||||||
|
|
||||||
def get_embedding_model_by_knowledge(knowledge):
|
def get_embedding_model_by_knowledge(knowledge):
|
||||||
|
if knowledge is None or knowledge.embedding_model_id is None or knowledge.embedding_model is None:
|
||||||
|
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
||||||
default_params = get_model_default_params(knowledge.embedding_model)
|
default_params = get_model_default_params(knowledge.embedding_model)
|
||||||
|
|
||||||
return ModelManage.get_model(str(knowledge.embedding_model_id),
|
return ModelManage.get_model(str(knowledge.embedding_model_id),
|
||||||
|
|
@ -146,6 +152,8 @@ def get_embedding_model_by_knowledge(knowledge):
|
||||||
|
|
||||||
def get_embedding_model_id_by_knowledge_id(knowledge_id):
|
def get_embedding_model_id_by_knowledge_id(knowledge_id):
|
||||||
knowledge = QuerySet(Knowledge).select_related('embedding_model').filter(id=knowledge_id).first()
|
knowledge = QuerySet(Knowledge).select_related('embedding_model').filter(id=knowledge_id).first()
|
||||||
|
if knowledge is None or knowledge.embedding_model_id is None:
|
||||||
|
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
||||||
return str(knowledge.embedding_model_id)
|
return str(knowledge.embedding_model_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -155,6 +163,8 @@ def get_embedding_model_id_by_knowledge_id_list(knowledge_id_list: List):
|
||||||
raise Exception(_('The knowledge base is inconsistent with the vector model'))
|
raise Exception(_('The knowledge base is inconsistent with the vector model'))
|
||||||
if len(knowledge_list) == 0:
|
if len(knowledge_list) == 0:
|
||||||
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
||||||
|
if knowledge_list[0].embedding_model_id is None:
|
||||||
|
raise Exception(_('Knowledge base setting error, please reset the knowledge base'))
|
||||||
return str(knowledge_list[0].embedding_model_id)
|
return str(knowledge_list[0].embedding_model_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1702,9 +1702,11 @@ class DocumentSerializers(serializers.Serializer):
|
||||||
source_file.save(file_content)
|
source_file.save(file_content)
|
||||||
|
|
||||||
file.seek(0)
|
file.seek(0)
|
||||||
parsed_documents = DocumentSerializers.Split(
|
split_serializer = DocumentSerializers.Split(
|
||||||
data={'workspace_id': self.data.get('workspace_id'), 'knowledge_id': self.data.get('knowledge_id')}
|
data={'workspace_id': self.data.get('workspace_id'), 'knowledge_id': self.data.get('knowledge_id')}
|
||||||
).file_to_paragraph(file, None, None, 4096)
|
)
|
||||||
|
split_serializer.is_valid(instance={'file': [file]}, raise_exception=True)
|
||||||
|
parsed_documents = split_serializer.file_to_paragraph(file, None, None, 4096)
|
||||||
paragraphs = []
|
paragraphs = []
|
||||||
for parsed_document in parsed_documents:
|
for parsed_document in parsed_documents:
|
||||||
paragraphs.extend(parsed_document.get('content', []))
|
paragraphs.extend(parsed_document.get('content', []))
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ def get_model(model, **kwargs):
|
||||||
@param model: model 数据库Model实例对象
|
@param model: model 数据库Model实例对象
|
||||||
@return: 模型实例
|
@return: 模型实例
|
||||||
"""
|
"""
|
||||||
|
if model is None:
|
||||||
|
raise Exception(_("Model does not exist"))
|
||||||
return get_model_(model.provider, model.model_type, model.model_name, model.credential, str(model.id), **kwargs)
|
return get_model_(model.provider, model.model_type, model.model_name, model.credential, str(model.id), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -105,6 +107,8 @@ def is_valid_credential(provider, model_type, model_name, model_credential: Dict
|
||||||
|
|
||||||
|
|
||||||
def get_model_by_id(_id, workspace_id):
|
def get_model_by_id(_id, workspace_id):
|
||||||
|
if _id is None:
|
||||||
|
raise Exception(_("Model does not exist"))
|
||||||
model = QuerySet(Model).filter(id=_id).first()
|
model = QuerySet(Model).filter(id=_id).first()
|
||||||
# 归还链接到连接池
|
# 归还链接到连接池
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
@ -116,6 +120,9 @@ def get_model_by_id(_id, workspace_id):
|
||||||
return model
|
return model
|
||||||
|
|
||||||
def get_model_default_params(model):
|
def get_model_default_params(model):
|
||||||
|
if model is None:
|
||||||
|
raise Exception(_("Model does not exist"))
|
||||||
|
|
||||||
def convert_to_int(value):
|
def convert_to_int(value):
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,48 @@ const showUserAvatar = computed(() => {
|
||||||
return props.application.show_user_avatar == undefined ? true : props.application.show_user_avatar
|
return props.application.show_user_avatar == undefined ? true : props.application.show_user_avatar
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const applicationName = computed(() => {
|
||||||
|
return props.application?.name || props.application?.application_name || 'XXX'
|
||||||
|
})
|
||||||
|
|
||||||
|
const replaceApplicationName = (text: string) => {
|
||||||
|
return (text || '').replaceAll('XXX', applicationName.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultQuestions = computed(() => {
|
||||||
|
const source = replaceApplicationName(props.application?.prologue || '')
|
||||||
|
const questionList = Array.from(source.matchAll(/-\s+(.+)/g))
|
||||||
|
.map((match) => match[1]?.trim())
|
||||||
|
.filter((item) => item)
|
||||||
|
if (questionList.length) {
|
||||||
|
return questionList.slice(0, 3)
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
`${applicationName.value}主要功能有什么?`,
|
||||||
|
`${applicationName.value}如何收费?`,
|
||||||
|
'需要转人工服务',
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const suggestedQuestions = computed(() => {
|
||||||
|
const questions = props.application?.suggested_questions
|
||||||
|
return Array.isArray(questions)
|
||||||
|
? questions.map((item) => `${item || ''}`.trim()).filter((item) => item).slice(0, 3)
|
||||||
|
: []
|
||||||
|
})
|
||||||
|
|
||||||
const toQuickQuestion = (match: string, offset: number, input: string) => {
|
const toQuickQuestion = (match: string, offset: number, input: string) => {
|
||||||
return `<quick_question>${match.replace('- ', '')}</quick_question>`
|
return `<quick_question>${match.replace('- ', '')}</quick_question>`
|
||||||
}
|
}
|
||||||
const prologue = computed(() => {
|
const prologue = computed(() => {
|
||||||
const temp = props.available ? props.application?.prologue : t('chat.tip.prologueMessage')
|
const temp = props.available
|
||||||
|
? [
|
||||||
|
`您好,我是${applicationName.value},您可以向我提出以下或其它问题。`,
|
||||||
|
...(suggestedQuestions.value.length ? suggestedQuestions.value : defaultQuestions.value).map(
|
||||||
|
(item) => `<quick_question>${item}</quick_question>`,
|
||||||
|
),
|
||||||
|
].join('\n')
|
||||||
|
: t('chat.tip.prologueMessage')
|
||||||
if (temp) {
|
if (temp) {
|
||||||
const tag_list = [
|
const tag_list = [
|
||||||
/<html_rander>[\d\D]*?<\/html_rander>/g,
|
/<html_rander>[\d\D]*?<\/html_rander>/g,
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,20 @@
|
||||||
<template v-for="(item, index) in md_view_list" :key="index">
|
<template v-for="(item, index) in md_view_list" :key="index">
|
||||||
<div
|
<div
|
||||||
v-if="item.type === 'question'"
|
v-if="item.type === 'question'"
|
||||||
@click="
|
@click="sendQuestion(item.content)"
|
||||||
sendMessage && type !== 'log' ? sendMessage(item.content, 'new') : (content: string) => {}
|
|
||||||
"
|
|
||||||
class="problem-button mt-4 mb-4"
|
class="problem-button mt-4 mb-4"
|
||||||
:class="sendMessage && type !== 'log' ? 'cursor' : 'disabled'"
|
:class="sendMessage && type !== 'log' ? 'cursor' : 'disabled'"
|
||||||
>
|
>
|
||||||
<el-space :size="8" alignment="flex-start">
|
<el-space :size="8" alignment="flex-start">
|
||||||
<AppIcon iconName="app-edit" class="color-primary" style="margin-top: 3px"></AppIcon>
|
<AppIcon
|
||||||
{{ item.content }}
|
iconName="app-edit"
|
||||||
|
class="color-primary quick-question-edit"
|
||||||
|
style="margin-top: 3px"
|
||||||
|
@click.stop="setInputQuestion(item.content)"
|
||||||
|
></AppIcon>
|
||||||
|
<span>
|
||||||
|
{{ item.content }}
|
||||||
|
</span>
|
||||||
</el-space>
|
</el-space>
|
||||||
</div>
|
</div>
|
||||||
<HtmlRander v-else-if="item.type === 'html_rander'" :source="item.content"></HtmlRander>
|
<HtmlRander v-else-if="item.type === 'html_rander'" :source="item.content"></HtmlRander>
|
||||||
|
|
@ -49,6 +54,7 @@ import EchartsRander from './EchartsRander.vue'
|
||||||
import FormRander from './FormRander.vue'
|
import FormRander from './FormRander.vue'
|
||||||
import ReasoningRander from './ReasoningRander.vue'
|
import ReasoningRander from './ReasoningRander.vue'
|
||||||
import { nanoid } from 'nanoid'
|
import { nanoid } from 'nanoid'
|
||||||
|
import bus from '@/bus'
|
||||||
config({
|
config({
|
||||||
markdownItConfig(md) {
|
markdownItConfig(md) {
|
||||||
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
||||||
|
|
@ -88,6 +94,16 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
const editorRef = ref()
|
const editorRef = ref()
|
||||||
|
const sendQuestion = (question: string) => {
|
||||||
|
if (props.sendMessage && props.type !== 'log') {
|
||||||
|
props.sendMessage(question, 'new')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const setInputQuestion = (question: string) => {
|
||||||
|
if (props.type !== 'log') {
|
||||||
|
bus.emit('chat-input', question)
|
||||||
|
}
|
||||||
|
}
|
||||||
const md_view_list = computed(() => {
|
const md_view_list = computed(() => {
|
||||||
const temp_source = props.source
|
const temp_source = props.source
|
||||||
return split_form_rander(
|
return split_form_rander(
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,24 @@
|
||||||
@close="closeSingleSelectDialog"
|
@close="closeSingleSelectDialog"
|
||||||
width="500px"
|
width="500px"
|
||||||
>
|
>
|
||||||
|
<div v-if="pendingPermissionChange" class="permission-confirm-info mb-16">
|
||||||
|
<div>{{ resourceTypeLabel }}:{{ targetResourceName }}</div>
|
||||||
|
<div>
|
||||||
|
{{ $t('views.userManage.userForm.nick_name.label') }}:{{
|
||||||
|
pendingPermissionChange.row?.nick_name || '-'
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ $t('views.login.loginForm.username.label') }}:{{
|
||||||
|
pendingPermissionChange.row?.username || '-'
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ $t('views.model.modelForm.permissionType.label') }}:{{
|
||||||
|
getPermissionLabel(pendingPermissionChange.val)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<el-radio-group v-if="isFolder" v-model="authAllChildren" class="radio-block">
|
<el-radio-group v-if="isFolder" v-model="authAllChildren" class="radio-block">
|
||||||
<el-radio :value="false">
|
<el-radio :value="false">
|
||||||
<p class="color-text-primary lighter">
|
<p class="color-text-primary lighter">
|
||||||
|
|
@ -326,6 +344,8 @@ watch(drawerVisible, (bool) => {
|
||||||
searchType.value = 'nick_name'
|
searchType.value = 'nick_name'
|
||||||
searchForm.value = { nick_name: '', username: '', permission: undefined }
|
searchForm.value = { nick_name: '', username: '', permission: undefined }
|
||||||
permissionData.value = []
|
permissionData.value = []
|
||||||
|
targetResource.value = null
|
||||||
|
folderData.value = null
|
||||||
paginationConfig.current_page = 1
|
paginationConfig.current_page = 1
|
||||||
paginationConfig.total = 0
|
paginationConfig.total = 0
|
||||||
multipleSelection.value = []
|
multipleSelection.value = []
|
||||||
|
|
@ -336,6 +356,7 @@ watch(drawerVisible, (bool) => {
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const targetId = ref('')
|
const targetId = ref('')
|
||||||
const folderData = ref<any>(null)
|
const folderData = ref<any>(null)
|
||||||
|
const targetResource = ref<any>(null)
|
||||||
const permissionData = ref<any[]>([])
|
const permissionData = ref<any[]>([])
|
||||||
const searchType = ref('nick_name')
|
const searchType = ref('nick_name')
|
||||||
const searchForm = ref<any>({
|
const searchForm = ref<any>({
|
||||||
|
|
@ -374,6 +395,32 @@ const singleSelectDialogVisible = ref(false)
|
||||||
const pendingPermissionChange = ref<{ val: any; row: any } | null>(null)
|
const pendingPermissionChange = ref<{ val: any; row: any } | null>(null)
|
||||||
const radioPermission = ref('')
|
const radioPermission = ref('')
|
||||||
const authAllChildren = ref(false)
|
const authAllChildren = ref(false)
|
||||||
|
|
||||||
|
const resourceTypeLabelMap: Record<string, string> = {
|
||||||
|
APPLICATION: t('views.application.title'),
|
||||||
|
KNOWLEDGE: t('views.knowledge.title'),
|
||||||
|
TOOL: t('views.tool.title'),
|
||||||
|
MODEL: t('views.model.title'),
|
||||||
|
}
|
||||||
|
|
||||||
|
const resourceTypeLabel = computed(() => {
|
||||||
|
return props.isFolder
|
||||||
|
? '文件夹'
|
||||||
|
: resourceTypeLabelMap[props.type] || t('views.system.resourceAuthorization.title')
|
||||||
|
})
|
||||||
|
|
||||||
|
const targetResourceName = computed(() => {
|
||||||
|
return targetResource.value?.name || folderData.value?.name || targetId.value || '-'
|
||||||
|
})
|
||||||
|
|
||||||
|
const getPermissionLabel = (permission: string) => {
|
||||||
|
return (
|
||||||
|
getFolderPermissionOptions().find((item) => item.value === permission)?.label ||
|
||||||
|
permission ||
|
||||||
|
'-'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function openMulConfigureDialog() {
|
function openMulConfigureDialog() {
|
||||||
if (multipleSelection.value.length === 0) {
|
if (multipleSelection.value.length === 0) {
|
||||||
return
|
return
|
||||||
|
|
@ -458,9 +505,10 @@ const getPermissionList = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const open = (id: string, folder_data?: any) => {
|
const open = (id: string, resource_data?: any) => {
|
||||||
targetId.value = id
|
targetId.value = id
|
||||||
folderData.value = folder_data
|
folderData.value = props.isFolder ? resource_data : null
|
||||||
|
targetResource.value = resource_data || null
|
||||||
drawerVisible.value = true
|
drawerVisible.value = true
|
||||||
getPermissionList()
|
getPermissionList()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
<h1 class="header-title">AI-RAG</h1>
|
<h1 class="header-title">AI-RAG</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<router-link v-if="isSystemManagement" to="/application" class="back-link">
|
<button v-if="isSystemManagement" class="back-link" type="button" @click="goBackWorkspace">
|
||||||
<span class="back-icon">←</span>
|
<span class="back-icon">←</span>
|
||||||
{{menuText.back}}
|
{{menuText.back}}
|
||||||
</router-link>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -18,10 +18,11 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ChinaMobileIcon from '@/components/china-mobile-icon/index.vue'
|
import ChinaMobileIcon from '@/components/china-mobile-icon/index.vue'
|
||||||
const { locale } = useI18n({ useScope: 'global' })
|
const { locale } = useI18n({ useScope: 'global' })
|
||||||
|
const SYSTEM_ENTRY_FROM_KEY = 'system_entry_from'
|
||||||
const menuText = computed(() => {
|
const menuText = computed(() => {
|
||||||
if (locale.value === 'en-US') {
|
if (locale.value === 'en-US') {
|
||||||
return {
|
return {
|
||||||
|
|
@ -40,11 +41,21 @@ const menuText = computed(() => {
|
||||||
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const isSystemManagement = computed(() => {
|
const isSystemManagement = computed(() => {
|
||||||
const path = route.path
|
const path = route.path
|
||||||
return path.startsWith('/system') || path.startsWith('/admin/system')
|
return path.startsWith('/system') || path === '/operate' || path.startsWith('/admin/system')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const isValidReturnPath = (path?: string | null) => {
|
||||||
|
return Boolean(path && !path.startsWith('/system') && path !== '/operate')
|
||||||
|
}
|
||||||
|
|
||||||
|
const goBackWorkspace = () => {
|
||||||
|
const fromPath = sessionStorage.getItem(SYSTEM_ENTRY_FROM_KEY)
|
||||||
|
router.push(isValidReturnPath(fromPath) ? fromPath! : '/knowledge')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
@ -91,9 +102,10 @@ const isSystemManagement = computed(() => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
text-decoration: none;
|
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
border: 1px solid #1890ff;
|
border: 1px solid #1890ff;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-icon {
|
.back-icon {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,25 @@ import useStore from '@/stores'
|
||||||
import { routes } from '@/router/routes'
|
import { routes } from '@/router/routes'
|
||||||
import { getFirstAvailableMenuPath, resolveMenuIdByPath } from '@/utils/menu-setting'
|
import { getFirstAvailableMenuPath, resolveMenuIdByPath } from '@/utils/menu-setting'
|
||||||
NProgress.configure({ showSpinner: false, speed: 500, minimum: 0.3 })
|
NProgress.configure({ showSpinner: false, speed: 500, minimum: 0.3 })
|
||||||
|
const SYSTEM_ENTRY_FROM_KEY = 'system_entry_from'
|
||||||
|
|
||||||
|
const isSystemRoutePath = (path: string) => {
|
||||||
|
return path.startsWith('/system') || path === '/operate'
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldRecordSystemEntryFrom = (
|
||||||
|
to: RouteLocationNormalized,
|
||||||
|
from: RouteLocationNormalized,
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
isSystemRoutePath(to.path) &&
|
||||||
|
!isSystemRoutePath(from.path) &&
|
||||||
|
from.name &&
|
||||||
|
from.path !== '/' &&
|
||||||
|
from.path !== '/login'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(window.MaxKB?.prefix ? window.MaxKB?.prefix : import.meta.env.BASE_URL),
|
history: createWebHistory(window.MaxKB?.prefix ? window.MaxKB?.prefix : import.meta.env.BASE_URL),
|
||||||
routes: routes,
|
routes: routes,
|
||||||
|
|
@ -22,6 +41,9 @@ const router = createRouter({
|
||||||
router.beforeEach(
|
router.beforeEach(
|
||||||
async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
|
async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
|
||||||
NProgress.start()
|
NProgress.start()
|
||||||
|
if (shouldRecordSystemEntryFrom(to, from)) {
|
||||||
|
sessionStorage.setItem(SYSTEM_ENTRY_FROM_KEY, from.fullPath)
|
||||||
|
}
|
||||||
if (to.name === '404') {
|
if (to.name === '404') {
|
||||||
next()
|
next()
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -387,7 +387,7 @@ const CopyApplicationDialogRef = ref()
|
||||||
|
|
||||||
const ResourceAuthorizationDrawerRef = ref()
|
const ResourceAuthorizationDrawerRef = ref()
|
||||||
function openAuthorization(item: any) {
|
function openAuthorization(item: any) {
|
||||||
ResourceAuthorizationDrawerRef.value.open(item.id)
|
ResourceAuthorizationDrawerRef.value.open(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MoveToDialogRef = ref()
|
const MoveToDialogRef = ref()
|
||||||
|
|
|
||||||
|
|
@ -448,7 +448,7 @@ const paginationConfig = reactive({
|
||||||
|
|
||||||
const ResourceAuthorizationDrawerRef = ref()
|
const ResourceAuthorizationDrawerRef = ref()
|
||||||
function openAuthorization(item: any) {
|
function openAuthorization(item: any) {
|
||||||
ResourceAuthorizationDrawerRef.value.open(item.id)
|
ResourceAuthorizationDrawerRef.value.open(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MoveToDialogRef = ref()
|
const MoveToDialogRef = ref()
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ const MoreFilledPermission = (id: any) => {
|
||||||
|
|
||||||
const ResourceAuthorizationDrawerRef = ref()
|
const ResourceAuthorizationDrawerRef = ref()
|
||||||
function openAuthorization(item: any) {
|
function openAuthorization(item: any) {
|
||||||
ResourceAuthorizationDrawerRef.value.open(item.id)
|
ResourceAuthorizationDrawerRef.value.open(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const downModel = ref<Model>()
|
const downModel = ref<Model>()
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@ const MoreFilledPermission = () => {
|
||||||
const ResourceAuthorizationDrawerRef = ref()
|
const ResourceAuthorizationDrawerRef = ref()
|
||||||
|
|
||||||
function openAuthorization(item: any) {
|
function openAuthorization(item: any) {
|
||||||
ResourceAuthorizationDrawerRef.value.open(item.id)
|
ResourceAuthorizationDrawerRef.value.open(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiInputParams = ref([])
|
const apiInputParams = ref([])
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ const paginationConfig = reactive({
|
||||||
|
|
||||||
const ResourceAuthorizationDrawerRef = ref()
|
const ResourceAuthorizationDrawerRef = ref()
|
||||||
function openAuthorization(item: any) {
|
function openAuthorization(item: any) {
|
||||||
ResourceAuthorizationDrawerRef.value.open(item.id)
|
ResourceAuthorizationDrawerRef.value.open(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportKnowledge = (item: any) => {
|
const exportKnowledge = (item: any) => {
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ const MoreFilledPermission = () => {
|
||||||
|
|
||||||
const ResourceAuthorizationDrawerRef = ref()
|
const ResourceAuthorizationDrawerRef = ref()
|
||||||
function openAuthorization(item: any) {
|
function openAuthorization(item: any) {
|
||||||
ResourceAuthorizationDrawerRef.value.open(item.id)
|
ResourceAuthorizationDrawerRef.value.open(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteModel = (row: any) => {
|
const deleteModel = (row: any) => {
|
||||||
|
|
|
||||||
|
|
@ -404,7 +404,7 @@ const MoreFilledPermission = (row: any) => {
|
||||||
|
|
||||||
const ResourceAuthorizationDrawerRef = ref()
|
const ResourceAuthorizationDrawerRef = ref()
|
||||||
function openAuthorization(item: any) {
|
function openAuthorization(item: any) {
|
||||||
ResourceAuthorizationDrawerRef.value.open(item.id)
|
ResourceAuthorizationDrawerRef.value.open(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportTool(row: any) {
|
function exportTool(row: any) {
|
||||||
|
|
|
||||||
|
|
@ -150,12 +150,15 @@ import { ComplexPermission } from '@/utils/permission/type'
|
||||||
import { getPermissionOptions } from '@/views/system/resource-authorization/constant'
|
import { getPermissionOptions } from '@/views/system/resource-authorization/constant'
|
||||||
import useStore from '@/stores'
|
import useStore from '@/stores'
|
||||||
import { TreeToFlatten } from '@/utils/array'
|
import { TreeToFlatten } from '@/utils/array'
|
||||||
|
import { MsgConfirm } from '@/utils/message'
|
||||||
|
|
||||||
const { model, user } = useStore()
|
const { model, user } = useStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: any[]
|
data: any[]
|
||||||
type: string
|
type: string
|
||||||
|
resourceLabel?: string
|
||||||
|
currentMember?: any
|
||||||
getData?: () => void
|
getData?: () => void
|
||||||
}>()
|
}>()
|
||||||
const emit = defineEmits(['submitPermissions'])
|
const emit = defineEmits(['submitPermissions'])
|
||||||
|
|
@ -170,6 +173,7 @@ watch(
|
||||||
defaultExpandKeys.value = props.data?.length > 0 ? [props.data[0]?.id] : []
|
defaultExpandKeys.value = props.data?.length > 0 ? [props.data[0]?.id] : []
|
||||||
isComputedFirst.value = false
|
isComputedFirst.value = false
|
||||||
}
|
}
|
||||||
|
refreshPermissionSnapshot(newData || [])
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
|
|
@ -329,6 +333,41 @@ const filteredData = computed(() => {
|
||||||
|
|
||||||
const multipleSelection = ref<any[]>([])
|
const multipleSelection = ref<any[]>([])
|
||||||
const selectObj: any = {}
|
const selectObj: any = {}
|
||||||
|
const permissionSnapshot = ref<Record<string, string>>({})
|
||||||
|
|
||||||
|
const refreshPermissionSnapshot = (data: any[]) => {
|
||||||
|
const snapshot: Record<string, string> = {}
|
||||||
|
TreeToFlatten(data || []).forEach((item: any) => {
|
||||||
|
snapshot[item.id] = item.permission
|
||||||
|
})
|
||||||
|
permissionSnapshot.value = snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
const escapeHtml = (value: any) => {
|
||||||
|
return String(value ?? '-')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''')
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPermissionLabel = (value: string, row: any) => {
|
||||||
|
return getRowPermissionOptions(row).find((item) => item.value === value)?.label || value
|
||||||
|
}
|
||||||
|
|
||||||
|
const getConfirmDescription = (value: string, row: any) => {
|
||||||
|
const resourceLabel = props.resourceLabel || '资源'
|
||||||
|
const memberName = props.currentMember?.nick_name || props.currentMember?.username || '-'
|
||||||
|
return `
|
||||||
|
<div style="line-height: 1.8;">
|
||||||
|
<div>${escapeHtml(resourceLabel)}:${escapeHtml(row?.name)}</div>
|
||||||
|
<div>姓名:${escapeHtml(memberName)}</div>
|
||||||
|
<div>操作权限:${escapeHtml(getPermissionLabel(value, row))}</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
const selectAll = (selection: any[]) => {
|
const selectAll = (selection: any[]) => {
|
||||||
multipleSelection.value = selection
|
multipleSelection.value = selection
|
||||||
}
|
}
|
||||||
|
|
@ -384,7 +423,17 @@ function closeDialog() {
|
||||||
multipleTableRef.value?.clearSelection()
|
multipleTableRef.value?.clearSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitPermissions(value: string, row: any) {
|
async function submitPermissions(value: string, row: any) {
|
||||||
|
const previousPermission = permissionSnapshot.value[row.id] || 'NOT_AUTH'
|
||||||
|
try {
|
||||||
|
await MsgConfirm('确认修改资源权限', getConfirmDescription(value, row), {
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
row.permission = previousPermission
|
||||||
|
return
|
||||||
|
}
|
||||||
const obj = [
|
const obj = [
|
||||||
{
|
{
|
||||||
target_id: row.id,
|
target_id: row.id,
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@
|
||||||
<PermissionTable
|
<PermissionTable
|
||||||
:data="treeData"
|
:data="treeData"
|
||||||
:type="activeData.type"
|
:type="activeData.type"
|
||||||
|
:resource-label="activeData.label"
|
||||||
|
:current-member="currentMember"
|
||||||
ref="PermissionTableRef"
|
ref="PermissionTableRef"
|
||||||
:getData="getPermissionList"
|
:getData="getPermissionList"
|
||||||
@submitPermissions="submitPermissions"
|
@submitPermissions="submitPermissions"
|
||||||
|
|
@ -213,6 +215,10 @@ const treeData = computed(() => {
|
||||||
return toTree(permissionData.value, 'folder_id')
|
return toTree(permissionData.value, 'folder_id')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const currentMember = computed(() => {
|
||||||
|
return memberList.value.find((item: any) => item.id === currentUser.value)
|
||||||
|
})
|
||||||
|
|
||||||
function clickMemberHandle(item: any) {
|
function clickMemberHandle(item: any) {
|
||||||
currentUser.value = item.id
|
currentUser.value = item.id
|
||||||
currentType.value = item.type
|
currentType.value = item.type
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ const MoreFieldPermission = (id: any) => {
|
||||||
|
|
||||||
const ResourceAuthorizationDrawerRef = ref()
|
const ResourceAuthorizationDrawerRef = ref()
|
||||||
function openAuthorization(item: any) {
|
function openAuthorization(item: any) {
|
||||||
ResourceAuthorizationDrawerRef.value.open(item.id)
|
ResourceAuthorizationDrawerRef.value.open(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const InitParamDrawerRef = ref()
|
const InitParamDrawerRef = ref()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue