feat:修复知识库问题1

v3.2
panyy 2026-07-01 20:24:14 +08:00
parent 4c9ac22a7f
commit 9ed4f6edfc
15 changed files with 208 additions and 170 deletions

View File

@ -23,12 +23,14 @@ from django.utils.translation import gettext_lazy as _, gettext
from openpyxl.cell.cell import ILLEGAL_CHARACTERS_RE
from rest_framework import serializers
from application.models import Chat, Application, ChatRecord
from application.models import Chat, Application, ChatRecord, ChatUserType
from common.database_model_manage.database_model_manage import DatabaseModelManage
from common.db.search import get_dynamics_model, native_search, native_page_search, native_page_handler
from common.exception.app_exception import AppApiException
from common.utils.common import get_file_content
from maxkb.conf import PROJECT_DIR
from maxkb.settings import TIME_ZONE, edition
from users.models import User
class ApplicationChatResponseSerializers(serializers.Serializer):
@ -147,6 +149,75 @@ class ApplicationChatQuerySerializers(serializers.Serializer):
with_table_name=False)
return self.append_feedback_content(result)
@staticmethod
def format_user_display_name(nick_name, username):
nick_name = (nick_name or '').strip()
username = (username or '').strip()
if nick_name and username and nick_name != username:
return f'{nick_name}{username}'
return nick_name or username or '游客'
@classmethod
def build_asker(cls, user):
return {
'id': str(user.id),
'email': getattr(user, 'email', ''),
'phone': getattr(user, 'phone', ''),
'nick_name': getattr(user, 'nick_name', ''),
'username': getattr(user, 'username', ''),
'display_name': cls.format_user_display_name(
getattr(user, 'nick_name', ''),
getattr(user, 'username', '')
),
'source': getattr(user, 'source', '')
}
@classmethod
def normalize_asker(cls, asker):
if isinstance(asker, dict):
nick_name = asker.get('nick_name') or asker.get('name') or ''
username = asker.get('username') or asker.get('user_name') or ''
display_name = asker.get('display_name') or cls.format_user_display_name(nick_name, username)
return {**asker, 'display_name': display_name, 'username': username or display_name}
if asker:
return {'username': str(asker), 'display_name': str(asker)}
return {'username': '游客', 'display_name': '游客'}
@classmethod
def fill_asker_display_name(cls, records):
if not records:
return records
platform_user_ids = [
row.get('chat_user_id') for row in records
if row.get('chat_user_type') == ChatUserType.PLATFORM_USER.value and row.get('chat_user_id')
]
chat_user_ids = [
row.get('chat_user_id') for row in records
if row.get('chat_user_type') == ChatUserType.CHAT_USER.value and row.get('chat_user_id')
]
platform_user_map = {
str(user.id): user for user in QuerySet(User).filter(id__in=platform_user_ids)
} if platform_user_ids else {}
chat_user_map = {}
chat_user_model = DatabaseModelManage.get_model("chat_user")
if chat_user_model and chat_user_ids:
chat_user_map = {
str(user.id): user for user in QuerySet(chat_user_model).filter(id__in=chat_user_ids)
}
for row in records:
chat_user_id = str(row.get('chat_user_id') or '')
chat_user_type = row.get('chat_user_type')
if chat_user_type == ChatUserType.PLATFORM_USER.value and chat_user_id in platform_user_map:
row['asker'] = cls.build_asker(platform_user_map[chat_user_id])
elif chat_user_type == ChatUserType.CHAT_USER.value and chat_user_id in chat_user_map:
row['asker'] = cls.build_asker(chat_user_map[chat_user_id])
else:
row['asker'] = cls.normalize_asker(row.get('asker'))
return records
@staticmethod
def get_feedback_items(details):
if not isinstance(details, dict):
@ -163,6 +234,7 @@ class ApplicationChatQuerySerializers(serializers.Serializer):
def append_feedback_content(cls, records):
if not records:
return records
cls.fill_asker_display_name(records)
chat_id_list = [row.get('id') for row in records if row.get('id') is not None]
feedback_map = {}
chat_records = QuerySet(ChatRecord).filter(chat_id__in=chat_id_list).order_by('create_time')
@ -213,7 +285,7 @@ class ApplicationChatQuerySerializers(serializers.Serializer):
"\n".join([
f"{improve_paragraph_list[index].get('title')}\n{improve_paragraph_list[index].get('content')}"
for index in range(len(improve_paragraph_list))]),
row.get('asker').get('username'),
ApplicationChatQuerySerializers.normalize_asker(row.get('asker')).get('display_name'),
(row.get('message_tokens') or 0) + (row.get('answer_tokens') or 0), row.get('run_time'),
str(row.get('create_time').astimezone(pytz.timezone(TIME_ZONE)).strftime('%Y-%m-%d %H:%M:%S')
if row.get('create_time') is not None else None)]
@ -256,8 +328,9 @@ class ApplicationChatQuerySerializers(serializers.Serializer):
('export_application_chat_ee.sql' if ['PE',
'EE'].__contains__(
edition) else 'export_application_chat.sql'))),
with_table_name=False):
with_table_name=False):
self.fill_asker_display_name(data_list)
for item in data_list:
row = [self.reset_value(v) for v in self.to_row(item)]
worksheet.append(row)

View File

@ -25,6 +25,7 @@ from common.exception.app_exception import ChatException
from knowledge.models import Document
from models_provider.models import Model
from models_provider.tools import get_model_credential
from users.models import User
class ChatInfo:
@ -99,14 +100,30 @@ class ChatInfo:
chat_user_model = DatabaseModelManage.get_model("chat_user")
if self.chat_user_type == ChatUserType.CHAT_USER.value and chat_user_model:
chat_user = QuerySet(chat_user_model).filter(id=self.chat_user_id).first()
if chat_user is None:
return {'username': '游客'}
return {
'id': str(chat_user.id),
'email': chat_user.email,
'phone': chat_user.phone,
'nick_name': chat_user.nick_name,
'username': chat_user.username,
'display_name': self.format_user_display_name(chat_user.nick_name, chat_user.username),
'source': chat_user.source
}
elif self.chat_user_type == ChatUserType.PLATFORM_USER.value:
user = QuerySet(User).filter(id=self.chat_user_id).first()
if user is None:
return {'username': '游客'}
return {
'id': str(user.id),
'email': user.email,
'phone': user.phone,
'nick_name': user.nick_name,
'username': user.username,
'display_name': self.format_user_display_name(user.nick_name, user.username),
'source': user.source
}
else:
if asker:
if isinstance(asker, dict):
@ -117,6 +134,14 @@ class ChatInfo:
self.chat_user = {'username': '游客'}
return self.chat_user
@staticmethod
def format_user_display_name(nick_name, username):
nick_name = (nick_name or '').strip()
username = (username or '').strip()
if nick_name and username and nick_name != username:
return f'{nick_name}{username}'
return nick_name or username or '游客'
def to_base_pipeline_manage_params(self):
self.get_application()
self.get_chat_user()

View File

@ -1,12 +1,12 @@
<template>
<h2 v-if="breadcrumbData?.length === 1" class="ellipsis" :title="breadcrumbData[0]?.name">
{{ breadcrumbData[0]?.name }}
<h2 v-if="breadcrumbData?.length === 1" class="ellipsis" :title="getFolderName(breadcrumbData[0])">
{{ getFolderName(breadcrumbData[0]) }}
</h2>
<el-breadcrumb separator-icon="ArrowRight" style="line-height: normal" class="mt-4" v-else>
<template v-if="breadcrumbData?.length > 3">
<el-breadcrumb-item>
<el-button link @click="handleClick(breadcrumbData[0])" :title="breadcrumbData[0].name">
<span class="ellipsis"> {{ breadcrumbData[0].name }}</span>
<el-button link @click="handleClick(breadcrumbData[0])" :title="getFolderName(breadcrumbData[0])">
<span class="ellipsis"> {{ getFolderName(breadcrumbData[0]) }}</span>
</el-button>
</el-breadcrumb-item>
<el-breadcrumb-item>
@ -15,19 +15,26 @@
</el-button>
</el-breadcrumb-item>
<el-breadcrumb-item>
<h5 class="ml-4 ellipsis" :title="breadcrumbData[breadcrumbData.length - 1].name">
{{ breadcrumbData[breadcrumbData.length - 1].name }}
<h5
class="ml-4 ellipsis"
:title="getFolderName(breadcrumbData[breadcrumbData.length - 1])"
>
{{ getFolderName(breadcrumbData[breadcrumbData.length - 1]) }}
</h5>
</el-breadcrumb-item>
</template>
<template v-else>
<el-breadcrumb-item v-for="(item, index) in breadcrumbData" :key="index">
<h5 class="ml-4 ellipsis" v-if="index === breadcrumbData.length - 1" :title="item.name">
{{ item.name }}
<h5
class="ml-4 ellipsis"
v-if="index === breadcrumbData.length - 1"
:title="getFolderName(item)"
>
{{ getFolderName(item) }}
</h5>
<el-button v-else link @click="handleClick(item)" :title="item.name">
<span class="ellipsis"> {{ item.name }}</span>
<el-button v-else link @click="handleClick(item)" :title="getFolderName(item)">
<span class="ellipsis"> {{ getFolderName(item) }}</span>
</el-button>
</el-breadcrumb-item>
</template>
@ -46,6 +53,10 @@ const props = defineProps({
type: Array,
default: () => [],
},
source: {
type: String,
default: '',
},
})
const breadcrumbData = computed(() => {
@ -72,6 +83,19 @@ function getBreadcrumbData() {
function handleClick(item: any) {
emit('click', item)
}
const rootFolderNameMap: Record<string, string> = {
APPLICATION: 'AI应用',
KNOWLEDGE: '知识库',
TOOL: '工具',
}
function getFolderName(item: any) {
if (!item?.parent_id && item?.id === 'default' && props.source) {
return rootFolderNameMap[props.source] || item?.name
}
return item?.name
}
</script>
<style lang="scss" scoped></style>

View File

@ -55,7 +55,9 @@
class="flex align-center w-full custom-tree-node"
>
<AppIcon iconName="app-folder" style="font-size: 20px"></AppIcon>
<span class="tree-label ml-8" :title="node.label">{{ i18n_name(node.label) }}</span>
<span class="tree-label ml-8" :title="getFolderLabel(data, node)">
{{ getFolderLabel(data, node) }}
</span>
<div
v-if="canOperation && MoreFilledPermission(node, data)"
@ -197,6 +199,19 @@ const resourceType = computed(() => {
}
})
const rootFolderNameMap: Record<string, string> = {
APPLICATION: 'AI应用',
KNOWLEDGE: '知识库',
TOOL: '工具',
}
function getFolderLabel(data: any, node?: any) {
if (!data?.parent_id && data?.id === 'default') {
return rootFolderNameMap[props.source] || i18n_name(node?.label || data?.name)
}
return i18n_name(node?.label || data?.name)
}
const permissionPrecise = computed(() => {
return permissionMap[resourceType.value!]['workspace']
})

View File

@ -83,53 +83,6 @@
{{ $t('layout.apiKey') }}
</el-dropdown-item>
</div>
<el-dropdown-item
style="padding: 0"
@click.stop
v-if="
hasPermission(
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE, RoleConst.USER],
[PermissionConst.SWITCH_LANGUAGE],
[],
'OR',
),
'OR',
)
"
>
<el-dropdown class="w-full" trigger="hover" placement="left-start">
<div class="flex-between w-full" style="line-height: 22px; padding: 12px 11px">
<span> {{ $t('layout.language') }}</span>
<el-icon>
<ArrowRight/>
</el-icon>
</div>
<template #dropdown>
<el-dropdown-menu style="width: 180px">
<el-dropdown-item
v-for="(lang, index) in langList"
:key="index"
:value="lang.value"
@click="changeLang(lang.value)"
class="flex-between"
>
<span :class="lang.value === user.userInfo?.language ? 'primary' : ''">{{
lang.label
}}</span>
<el-icon
:class="lang.value === user.userInfo?.language ? 'primary' : ''"
v-if="lang.value === user.userInfo?.language"
>
<Check/>
</el-icon>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-dropdown-item>
<!-- <el-dropdown-item-->
<!-- @click="openAbout"-->
<!-- v-if="hasPermission([RoleConst.ADMIN, PermissionConst.ABOUT_READ], 'OR')"-->
@ -159,7 +112,6 @@ import AboutDialog from './AboutDialog.vue'
// import UserPwdDialog from '@/views/user-manage/component/UserPwdDialog.vue'
import APIKeyDialog from './APIKeyDialog.vue'
import {ComplexPermission} from '@/utils/permission/type'
import {langList} from '@/locales/index'
import {hasPermission} from '@/utils/permission'
import {PermissionConst, RoleConst, EditionConst} from '@/utils/permission/data'
@ -170,11 +122,6 @@ const AboutDialogRef = ref()
const APIKeyDialogRef = ref()
const resetPasswordRef = ref<InstanceType<typeof ResetPassword>>()
// const { changeLocale } = useLocale()
const changeLang = (lang: string) => {
user.postUserLanguage(lang)
// changeLocale(lang)
}
const openAbout = () => {
AboutDialogRef.value?.open()
}

View File

@ -6,33 +6,6 @@
<div class="login-image" :style="{ backgroundImage: `url(${loginImage})` }"></div>
</el-col>
<el-col :xs="24" :sm="24" :md="14" :lg="14" :xl="14" class="right-container flex-center">
<el-dropdown trigger="click" type="primary" class="lang" v-if="lang">
<template #dropdown>
<el-dropdown-menu style="width: 180px">
<el-dropdown-item
v-for="(lang, index) in langList"
:key="index"
:value="lang.value"
@click="changeLang(lang.value)"
class="flex-between"
>
<span :class="lang.value === user.getLanguage() ? 'primary' : ''">{{
lang.label
}}</span>
<el-icon
:class="lang.value === user.getLanguage() ? 'primary' : ''"
v-if="lang.value === user.getLanguage()"
>
<Check />
</el-icon>
</el-dropdown-item>
</el-dropdown-menu>
</template>
<el-button>
{{ currentLanguage }}<el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>
</el-dropdown>
<slot></slot>
</el-col>
</el-row>
@ -43,24 +16,13 @@
import { computed } from 'vue'
import { getThemeImg } from '@/utils/theme'
import useStore from '@/stores'
import { useLocalStorage } from '@vueuse/core'
import { langList, localeConfigKey, getBrowserLang } from '@/locales/index'
defineProps({
lang: {
type: Boolean,
default: true,
},
})
const { user, theme } = useStore()
const changeLang = (lang: string) => {
useLocalStorage(localeConfigKey, getBrowserLang()).value = lang
window.location.reload()
}
const currentLanguage = computed(() => {
return langList.value?.filter((v: any) => v.value === user.getLanguage())?.[0]?.label
})
const { theme } = useStore()
const fileURL = computed(() => {
if (theme.themeInfo?.loginImage) {

View File

@ -1,4 +1,3 @@
import {useLocalStorage, usePreferredLanguages} from '@vueuse/core'
import {computed} from 'vue'
import {createI18n} from 'vue-i18n'
@ -16,21 +15,10 @@ const langModuleMap = new Map<string, object>()
export const langCode: Array<string> = []
export const localeConfigKey = 'MaxKB-locale'
// 获取浏览器默认语言环境
const languages = usePreferredLanguages()
export const defaultLocale = 'zh-CN'
export function getBrowserLang() {
const browserLang = navigator.language ? navigator.language : languages.value[0]
let defaultBrowserLang = ''
if (browserLang === 'zh-HK' || browserLang === 'zh-TW') {
defaultBrowserLang = 'zh-Hant'
} else if (browserLang === 'zh-CN') {
defaultBrowserLang = 'zh-CN'
} else {
defaultBrowserLang = 'en-US'
}
return defaultBrowserLang
return defaultLocale
}
// 生成语言模块列表
@ -59,8 +47,8 @@ const importMessages = computed(() => {
export const i18n = createI18n({
legacy: false,
locale: useLocalStorage(localeConfigKey, getBrowserLang()).value || getBrowserLang(),
fallbackLocale: getBrowserLang(),
locale: defaultLocale,
fallbackLocale: defaultLocale,
messages: importMessages.value,
globalInjection: true
})

View File

@ -3,7 +3,7 @@ import ChatAPI from '@/api/chat/chat'
import type {ChatProfile, ChatUserProfile} from '@/api/type/chat'
import type {LoginRequest} from '@/api/type/user'
import type {Ref} from 'vue'
import {getBrowserLang} from '@/locales/index'
import {defaultLocale} from '@/locales/index'
interface ChatUser {
// 用户id
@ -28,7 +28,7 @@ const useChatUserStore = defineStore('chat-user', {
}),
actions: {
getLanguage() {
return localStorage.getItem(`${this.accessToken}-locale`) || getBrowserLang()
return defaultLocale
},
setAccessToken(accessToken: string) {
this.accessToken = accessToken
@ -53,7 +53,7 @@ const useChatUserStore = defineStore('chat-user', {
return ChatAPI.applicationProfile().then((ok) => {
console.log('applicationProfile', ok.data)
this.application = ok.data
localStorage.setItem(`${this.accessToken}-locale`, ok.data?.language || this.getLanguage())
localStorage.setItem(`${this.accessToken}-locale`, defaultLocale)
})
},
isAuthentication() {

View File

@ -5,7 +5,7 @@ import UserApi from '@/api/user/user'
import LoginApi from '@/api/user/login'
import {useLocalStorage} from '@vueuse/core'
import {localeConfigKey, getBrowserLang} from '@/locales/index'
import {defaultLocale, localeConfigKey} from '@/locales/index'
import useThemeStore from './theme'
import {defaultPlatformSetting} from '@/utils/theme'
import useLoginStore from './login'
@ -32,7 +32,7 @@ const useUserStore = defineStore('user', {
}),
actions: {
getLanguage() {
return localStorage.getItem('MaxKB-locale') || getBrowserLang()
return defaultLocale
},
setWorkspaceId(workspace_id: string) {
this.workspace_id = workspace_id
@ -126,8 +126,7 @@ const useUserStore = defineStore('user', {
this.setWorkspaceId(workspace_list[0].id)
}
this.workspace_list = workspace_list
useLocalStorage<string>(localeConfigKey, 'en-US').value =
ok?.data?.language || this.getLanguage()
useLocalStorage<string>(localeConfigKey, defaultLocale).value = defaultLocale
const theme = useThemeStore()
theme.setTheme()
return this.asyncGetProfile()
@ -161,9 +160,9 @@ const useUserStore = defineStore('user', {
},
async postUserLanguage(lang: string, loading?: Ref<boolean>) {
return new Promise((resolve, reject) => {
LoginApi.postLanguage({language: lang}, loading)
LoginApi.postLanguage({language: defaultLocale}, loading)
.then(async (ok) => {
useLocalStorage(localeConfigKey, 'en-US').value = lang
useLocalStorage(localeConfigKey, defaultLocale).value = defaultLocale
window.location.reload()
resolve(ok)
})

View File

@ -7,19 +7,6 @@
width="550"
>
<el-form label-position="top" ref="displayFormRef" :model="form">
<el-form-item>
<span>{{
$t('layout.language')
}}</span>
<el-select v-model="form.language" clearable>
<el-option
v-for="item in langList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-space direction="vertical" alignment="start" :size="2">
<el-checkbox
@ -51,7 +38,7 @@ import { ref, watch, computed } from 'vue'
import { useRoute } from 'vue-router'
import type { FormInstance } from 'element-plus'
import { MsgSuccess, MsgError } from '@/utils/message'
import { langList, t } from '@/locales'
import { defaultLocale, t } from '@/locales'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
const route = useRoute()
@ -71,7 +58,7 @@ const displayFormRef = ref()
const form = ref<any>({
show_source: false,
show_exec: false,
language: '',
language: defaultLocale,
})
const detail = ref<any>(null)
@ -84,7 +71,7 @@ watch(dialogVisible, (bool) => {
form.value = {
show_source: false,
show_exec: false,
language: '',
language: defaultLocale,
}
}
})
@ -92,7 +79,7 @@ const open = (data: any, content: any) => {
detail.value = content
form.value.show_source = data.show_source
form.value.show_exec = data.show_exec
form.value.language = data.language
form.value.language = defaultLocale
dialogVisible.value = true
}
@ -101,7 +88,7 @@ const submit = async (formEl: FormInstance | undefined) => {
await formEl.validate((valid, fields) => {
if (valid) {
loadSharedApi({ type: 'application', systemType: apiType.value })
.putAccessToken(id as string, form.value, loading)
.putAccessToken(id as string, { ...form.value, language: defaultLocale }, loading)
.then(() => {
emit('refresh')

View File

@ -197,19 +197,6 @@
<el-color-picker v-model="xpackForm.custom_theme.header_font_color" />
</el-col>
</el-row>
<el-row class="w-full mb-8">
<h5 class="mb-8">
{{ $t('layout.language') }}
</h5>
<el-select v-model="xpackForm.language" clearable>
<el-option
v-for="item in langList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-row>
<!-- 应用 LOGO -->
<el-card shadow="never" class="mb-8">
<div class="flex-between mb-8">
@ -486,7 +473,7 @@ import { useRoute } from 'vue-router'
import type { FormInstance, FormRules, UploadFiles } from 'element-plus'
import { isAppIcon } from '@/utils/common'
import { MsgSuccess, MsgError } from '@/utils/message'
import { langList, t } from '@/locales'
import { defaultLocale, t } from '@/locales'
import { cloneDeep } from 'lodash'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
@ -508,7 +495,7 @@ const emit = defineEmits(['refresh'])
const defaultSetting = {
show_source: false,
show_exec: false,
language: '',
language: defaultLocale,
show_history: true,
draggable: true,
show_guide: true,
@ -541,7 +528,7 @@ const displayFormRef = ref()
const xpackForm = ref<any>({
show_source: false,
show_exec: false,
language: '',
language: defaultLocale,
icon: '',
icon_url: '',
show_history: true,
@ -591,6 +578,7 @@ const customStyle = computed(() => {
function resetForm() {
xpackForm.value = cloneDeep(defaultSetting)
xpackForm.value.language = defaultLocale
imgUrl.value = {
avatar: '',
float_icon: '',
@ -618,7 +606,7 @@ const open = (data: any, content: any) => {
xpackForm.value.show_source = data.show_source
xpackForm.value.show_exec = data.show_exec
xpackForm.value.show_history = data.show_history
xpackForm.value.language = data.language
xpackForm.value.language = defaultLocale
xpackForm.value.draggable = data.draggable
xpackForm.value.show_guide = data.show_guide
imgUrl.value.avatar = data.avatar
@ -664,6 +652,7 @@ const submit = async (formEl: FormInstance | undefined) => {
await formEl.validate((valid, fields) => {
if (valid) {
const fd = new FormData()
xpackForm.value.language = defaultLocale
Object.keys(xpackForm.value).map((item) => {
if (['custom_theme', 'float_location'].includes(item)) {
fd.append(item, JSON.stringify(xpackForm.value[item]))

View File

@ -14,7 +14,11 @@
</template>
<ContentContainer>
<template #header>
<FolderBreadcrumb :folderList="folderList" @click="folderClickHandle" />
<FolderBreadcrumb
:folderList="folderList"
:source="SourceTypeEnum.APPLICATION"
@click="folderClickHandle"
/>
</template>
<template #search>
<div class="flex">

View File

@ -173,7 +173,7 @@
</el-table-column>
<el-table-column prop="asker" :label="$t('views.chatLog.table.user')">
<template #default="{ row }">
{{ row.asker?.username }}
{{ getAskerDisplayName(row.asker) }}
</template>
</el-table-column>
<el-table-column :label="$t('views.chatLog.table.recenTimes')" width="180">
@ -487,6 +487,21 @@ const handleSelectionChange = (val: any[]) => {
multipleSelection.value = val
}
function getAskerDisplayName(asker: any) {
if (!asker) {
return '游客'
}
if (asker.display_name) {
return asker.display_name
}
const nickName = asker.nick_name || asker.name || ''
const username = asker.username || asker.user_name || ''
if (nickName && username && nickName !== username) {
return `${nickName}${username}`
}
return nickName || username || '游客'
}
function getList() {
const obj: any = {
start_time: daterange.value.start_time,

View File

@ -19,7 +19,12 @@
<h2 v-if="folder.currentFolder?.id === 'share'">
{{ $t('views.shared.shared_knowledge') }}
</h2>
<FolderBreadcrumb :folderList="folderList" @click="folderClickHandle" v-else />
<FolderBreadcrumb
:folderList="folderList"
:source="SourceTypeEnum.KNOWLEDGE"
@click="folderClickHandle"
v-else
/>
</template>
</KnowledgeListContainer>
</LayoutContainer>

View File

@ -20,7 +20,12 @@
<h2 v-if="folder.currentFolder?.id === 'share'">
{{ $t('views.shared.shared_tool') }}
</h2>
<FolderBreadcrumb :folderList="folderList" @click="folderClickHandle" v-else />
<FolderBreadcrumb
:folderList="folderList"
:source="SourceTypeEnum.TOOL"
@click="folderClickHandle"
v-else
/>
<el-divider direction="vertical" />
<el-radio-group v-model="toolType" @change="radioChange" class="app-radio-button-group">
<el-radio-button value="">{{ $t('views.tool.all') }}</el-radio-button>