feat:修复知识库问题

v3.2
panyy 2026-07-01 11:27:06 +08:00
parent c142297f41
commit 4c9ac22a7f
21 changed files with 297 additions and 25 deletions

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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', []))

View File

@ -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:

View File

@ -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,

View File

@ -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(

View File

@ -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()
} }

View File

@ -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">&larr;</span> <span class="back-icon">&larr;</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 {

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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>()

View File

@ -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([])

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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) {

View File

@ -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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}
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,

View File

@ -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

View File

@ -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()