feat(knowledge): 添加知识库创建者管理和时间信息显示

- 在多语言文件中添加更新时间、基本信息和未知状态的翻译
- 在知识库序列化器中添加用户ID和创建用户字段支持
- 在知识库设置页面添加创建者选择器和时间信息展示卡片
- 实现创建者信息的前后端数据同步和页面更新逻辑
- 添加用户列表获取功能以支持创建者选择
- 优化样式以适配新的创建者信息展示布局
v3.2
tanlianwang 2026-03-30 10:48:15 +08:00
parent 3013efb93c
commit 1c469f36b0
5 changed files with 126 additions and 4 deletions

View File

@ -80,6 +80,8 @@ class KnowledgeEditRequest(serializers.Serializer):
)
file_size_limit = serializers.IntegerField(required=False, label=_('file size limit'))
file_count_limit = serializers.IntegerField(required=False, label=_('file count limit'))
user_id = serializers.UUIDField(required=False, label=_('user id'))
create_user = serializers.UUIDField(required=False, label=_('create user'))
@staticmethod
def get_knowledge_meta_valid_map():
@ -381,6 +383,10 @@ class KnowledgeSerializer(serializers.Serializer):
knowledge.file_size_limit = instance.get('file_size_limit')
if 'file_count_limit' in instance:
knowledge.file_count_limit = instance.get('file_count_limit')
if 'user_id' in instance:
knowledge.user_id = instance.get('user_id')
if 'create_user' in instance:
knowledge.create_user = instance.get('create_user')
if 'application_id_list' in instance and instance.get('application_id_list') is not None:
application_id_list = instance.get('application_id_list')
# 当前用户可修改关联的知识库列表

View File

@ -31,6 +31,7 @@ export default {
upgrade: 'Upgrade',
createDate: 'Create Date',
createTime: 'Create Time',
updateTime: 'Update Time',
operation: 'Action',
character: 'characters',
export: 'Export',
@ -96,6 +97,8 @@ export default {
uploadImagePrompt: 'Please upload an image',
},
info: 'Base Information',
basicInfo: 'Basic Information',
unknown: 'Unknown',
otherSetting: 'Other Settings',
username: 'username',
importCreate: 'Import Create',

View File

@ -32,6 +32,7 @@ export default {
upgrade: '升级',
createDate: '创建日期',
createTime: '创建时间',
updateTime: '更新时间',
operation: '操作',
character: '字符',
export: '导出',
@ -97,6 +98,8 @@ export default {
uploadImagePrompt: '请上传一张图片',
},
info: '基本信息',
basicInfo: '基本信息',
unknown: '未知',
otherSetting: '其他设置',
username: '用户名',
importCreate: '导入创建',

View File

@ -31,6 +31,7 @@ export default {
upgrade: '升級',
createDate: '創建日期',
createTime: '創建時間',
updateTime: '更新時間',
operation: '操作',
character: '字符',
export: '匯出',
@ -96,6 +97,8 @@ export default {
uploadImagePrompt: '請上傳一張圖片',
},
info: '使用者資訊',
basicInfo: '基本資訊',
unknown: '未知',
otherSetting: '其他設定',
username: '用戶名',
importCreate: '導入創建',

View File

@ -9,6 +9,22 @@
{{ $t('common.info') }}
</h4>
<BaseForm ref="BaseFormRef" :data="detail" :apiType="apiType"/>
<el-card shadow="never" class="mb-16 w-full layout-bg">
<el-descriptions :column="3" border class="creator-info-descriptions">
<el-descriptions-item :label="$t('common.creator')" class="creator-info-item">
<el-select v-model="selectedCreator" placeholder="请选择创建者" v-if="!route.path.includes('share/') && permissionPrecise.edit(id)" class="creator-select">
<el-option v-for="user in user_options" :key="user.id" :label="user.nick_name" :value="user.id" />
</el-select>
<span v-else class="creator-text">{{ detail?.nick_name ? i18n_name(detail.nick_name) : detail?.create_user || $t('common.unknown') }}</span>
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')" class="creator-info-item">
<span class="creator-text">{{ detail?.create_time ? new Date(detail.create_time).toLocaleString() : $t('common.unknown') }}</span>
</el-descriptions-item>
<el-descriptions-item :label="$t('common.updateTime')" class="creator-info-item">
<span class="creator-text">{{ detail?.update_time ? new Date(detail.update_time).toLocaleString() : $t('common.unknown') }}</span>
</el-descriptions-item>
</el-descriptions>
</el-card>
<el-form
ref="webFormRef"
@ -185,11 +201,14 @@ import {useRoute} from 'vue-router'
import BaseForm from '@/views/knowledge/component/BaseForm.vue'
import {MsgSuccess, MsgConfirm} from '@/utils/message'
import {t} from '@/locales'
import {i18n_name} from '@/utils/common'
import permissionMap from '@/permission'
import useStore from '@/stores'
import {loadSharedApi} from '@/utils/dynamics-api/shared-api'
const route = useRoute()
const { user } = useStore()
const {
params: {id, folderId},
} = route as any
@ -215,8 +234,10 @@ const isShared = computed(() => {
const webFormRef = ref()
const BaseFormRef = ref()
const loading = ref(false)
const detail = ref<any>({})
const detail = ref<any>({ })
const cloneModelId = ref('')
const user_options = ref<any[]>([])
const selectedCreator = ref('')
const form = ref<any>({
source_url: '',
@ -269,11 +290,15 @@ async function submit() {
meta: form.value,
file_count_limit: form.value.file_count_limit,
file_size_limit: form.value.file_size_limit,
create_user: selectedCreator.value,
user_id: selectedCreator.value,
...BaseFormRef.value.form,
}
: {
file_count_limit: form.value.file_count_limit,
file_size_limit: form.value.file_size_limit,
create_user: selectedCreator.value,
user_id: selectedCreator.value,
...BaseFormRef.value.form,
}
@ -290,6 +315,13 @@ async function submit() {
.putReEmbeddingKnowledge(id)
.then(() => {
MsgSuccess(t('common.saveSuccess'))
//
const selectedUser = user_options.value.find(user => user.id === selectedCreator.value)
if (selectedUser) {
detail.value.create_user = selectedUser.id
detail.value.user_id = selectedUser.id
detail.value.nick_name = selectedUser.nick_name
}
})
})
} else {
@ -300,6 +332,13 @@ async function submit() {
.putReEmbeddingKnowledge(id)
.then(() => {
MsgSuccess(t('common.saveSuccess'))
//
const selectedUser = user_options.value.find(user => user.id === selectedCreator.value)
if (selectedUser) {
detail.value.create_user = selectedUser.id
detail.value.user_id = selectedUser.id
detail.value.nick_name = selectedUser.nick_name
}
})
})
}
@ -312,13 +351,25 @@ async function submit() {
.putLarkKnowledge(id, obj, loading)
.then(() => {
MsgSuccess(t('common.saveSuccess'))
//
const selectedUser = user_options.value.find(user => user.id === selectedCreator.value)
if (selectedUser) {
detail.value.create_user = selectedUser.id
detail.value.nick_name = selectedUser.nick_name
}
})
} else {
loadSharedApi({type: 'knowledge', systemType: apiType.value})
.putKnowledge(id, obj, loading)
.then(() => {
MsgSuccess(t('common.saveSuccess'))
})
.then(() => {
MsgSuccess(t('common.saveSuccess'))
//
const selectedUser = user_options.value.find(user => user.id === selectedCreator.value)
if (selectedUser) {
detail.value.create_user = selectedUser.id
detail.value.nick_name = selectedUser.nick_name
}
})
}
}
}
@ -332,6 +383,8 @@ function getDetail() {
.then((res: any) => {
detail.value = res.data
cloneModelId.value = res.data?.embedding_model_id
// 使user_id使create_user
selectedCreator.value = res.data?.user_id || res.data?.create_user || ''
if (detail.value?.type === 0) {
form.value.file_count_limit = res.data.file_count_limit
form.value.file_size_limit = res.data.file_size_limit
@ -339,11 +392,34 @@ function getDetail() {
if (detail.value?.type === 1 || detail.value?.type === 2) {
form.value = res.data.meta
}
// nick_name
if (user_options.value.length > 0 && selectedCreator.value) {
const selectedUser = user_options.value.find(user => user.id === selectedCreator.value)
if (selectedUser) {
detail.value.nick_name = selectedUser.nick_name
}
}
})
}
function getUserList() {
loadSharedApi({ type: 'workspace', isShared: isShared.value, systemType: apiType.value })
.getAllMemberList(user.getWorkspaceId(), loading)
.then((res: any) => {
user_options.value = res.data
// selectedCreatornick_name
if (selectedCreator.value) {
const selectedUser = user_options.value.find(user => user.id === selectedCreator.value)
if (selectedUser) {
detail.value.nick_name = selectedUser.nick_name
}
}
})
}
onMounted(() => {
getDetail()
getUserList()
})
</script>
<style lang="scss" scoped>
@ -351,4 +427,35 @@ onMounted(() => {
width: 70%;
margin: 0 auto;
}
.creator-info-descriptions {
width: 100%;
}
.creator-info-item {
padding: 8px 12px;
}
.creator-select {
width: 100%;
min-width: 180px;
}
.creator-text {
display: inline-block;
width: 100%;
min-width: 180px;
}
/* 调整描述项标签的宽度,确保标签和内容对齐 */
:deep(.el-descriptions__label) {
padding-right: 8px;
white-space: nowrap;
}
/* 调整描述项内容的宽度,确保内容有足够的空间 */
:deep(.el-descriptions__content) {
padding-left: 8px;
flex: 1;
}
</style>