diff --git a/Makefile b/Makefile index fe488509..239572d1 100644 --- a/Makefile +++ b/Makefile @@ -220,7 +220,7 @@ deps: # Build for production build-prod: - VERSION=$${VERSION:-unknown}; \ + VERSION=$${VERSION:-$$(git describe --tags --abbrev=0 2>/dev/null || echo "unknown")}; \ COMMIT_ID=$${COMMIT_ID:-unknown}; \ BUILD_TIME=$${BUILD_TIME:-unknown}; \ GO_VERSION=$${GO_VERSION:-unknown}; \ diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts index 337f3f4e..4e4b7855 100755 --- a/frontend/src/i18n/locales/en-US.ts +++ b/frontend/src/i18n/locales/en-US.ts @@ -1114,6 +1114,7 @@ export default { questionIndexModeDescription: 'Combined: Standard and similar questions are indexed together. Separate: Each question is indexed independently for more precise retrieval but requires more storage.', entryGuide: 'Each FAQ entry contains a primary question, similar questions, negative examples, and multiple answers. Manage them in the FAQ knowledge base detail view.', tagDesc: 'Select category for FAQ entries', + tagPlaceholder: 'Please select a category', modes: { questionOnly: 'Questions only', questionAnswer: 'Question + answer', diff --git a/frontend/src/i18n/locales/ko-KR.ts b/frontend/src/i18n/locales/ko-KR.ts index 626d9857..5dbec7bf 100755 --- a/frontend/src/i18n/locales/ko-KR.ts +++ b/frontend/src/i18n/locales/ko-KR.ts @@ -1238,6 +1238,7 @@ export default { questionIndexModeDescription: "병합 인덱스: 표준 질문과 유사 질문을 병합 인덱싱; 개별 인덱스: 표준 질문과 각 유사 질문을 독립적으로 인덱싱하여 더 정확하게 검색하지만 더 많은 저장 공간이 필요합니다", entryGuide: "FAQ 항목은 표준 질문, 유사 질문, 반례 및 여러 답변으로 구성됩니다. 지식베이스 세부 정보에서 일괄 가져오기 및 편집할 수 있습니다.", tagDesc: "FAQ 항목에 분류 선택", + tagPlaceholder: "분류를 선택하세요", modes: { questionOnly: "표준 질문/유사 질문만", questionAnswer: "표준 질문 + 답변", diff --git a/frontend/src/i18n/locales/ru-RU.ts b/frontend/src/i18n/locales/ru-RU.ts index 3a8ad7ad..a78b42bc 100755 --- a/frontend/src/i18n/locales/ru-RU.ts +++ b/frontend/src/i18n/locales/ru-RU.ts @@ -1056,6 +1056,7 @@ export default { questionIndexModeDescription: 'Объединенная: стандартные и похожие вопросы индексируются вместе. Раздельная: каждый вопрос индексируется независимо для более точного поиска, но требует больше места.', entryGuide: 'Каждый FAQ включает основной вопрос, похожие вопросы, негативные примеры и несколько ответов. Управляйте ими в деталях FAQ-базы.', tagDesc: 'Выберите категорию для записей FAQ', + tagPlaceholder: 'Пожалуйста, выберите категорию', modes: { questionOnly: 'Только вопросы', questionAnswer: 'Вопрос + ответ', diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index 7bd9da27..516fce3f 100755 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -1463,6 +1463,7 @@ export default { questionIndexModeDescription: "合并索引:标准问和相似问合并索引;分别索引:标准问和每个相似问独立索引,检索更精确但需要更多存储", entryGuide: "FAQ 条目由标准问、相似问、反例和多个答案组成,可在知识库详情中批量导入、编辑。", tagDesc: "为 FAQ 条目选择分类", + tagPlaceholder: "请选择分类", modes: { questionOnly: "仅标准问/相似问", questionAnswer: "标准问 + 答案", diff --git a/frontend/src/views/knowledge/KnowledgeBase.vue b/frontend/src/views/knowledge/KnowledgeBase.vue index 4adce1bd..7ceb815e 100644 --- a/frontend/src/views/knowledge/KnowledgeBase.vue +++ b/frontend/src/views/knowledge/KnowledgeBase.vue @@ -46,7 +46,7 @@ let knowledgeScroll = ref() let page = 1; let pageSize = 35; -const selectedTagId = ref("__untagged__"); +const selectedTagId = ref(''); const tagList = ref([]); const tagLoading = ref(false); const tagSearchQuery = ref(''); @@ -59,7 +59,6 @@ let tagSearchDebounce: ReturnType | null = null; let docSearchDebounce: ReturnType | null = null; const docSearchKeyword = ref(''); const selectedFileType = ref(''); -const UNTAGGED_TAG_ID = '__untagged__'; const fileTypeOptions = computed(() => [ { content: t('knowledgeBase.allFileTypes') || '全部类型', value: '' }, { content: 'PDF', value: 'pdf' }, @@ -71,18 +70,12 @@ const fileTypeOptions = computed(() => [ { content: t('knowledgeBase.typeManual') || '手动创建', value: 'manual' }, ]); type TagInputInstance = ComponentPublicInstance<{ focus: () => void; select: () => void }>; -const tagDropdownOptions = computed(() => { - const options = [ - { content: t('knowledgeBase.untagged') || '未分类', value: UNTAGGED_TAG_ID }, - ...tagList.value - .filter((tag: any) => tag.id !== UNTAGGED_TAG_ID) - .map((tag: any) => ({ - content: tag.name, - value: tag.id, - })), - ]; - return options; -}); +const tagDropdownOptions = computed(() => + tagList.value.map((tag: any) => ({ + content: tag.name, + value: tag.id, + })), +); const tagMap = computed>(() => { const map: Record = {}; tagList.value.forEach((tag) => { @@ -124,10 +117,9 @@ const getPageSize = () => { getPageSize() // 直接调用 API 获取知识库文件列表 const getTagName = (tagId?: string | number) => { - if (!tagId && tagId !== 0) return t('knowledgeBase.untagged') || '未分类'; - if (tagId === UNTAGGED_TAG_ID) return t('knowledgeBase.untagged') || '未分类'; + if (!tagId && tagId !== 0) return ''; const key = String(tagId); - return tagMap.value[key]?.name || (t('knowledgeBase.untagged') || '未分类'); + return tagMap.value[key]?.name || ''; }; const formatDocTime = (time?: string) => { @@ -225,22 +217,6 @@ const handleTagFilterChange = (value: string) => { }; const handleTagRowClick = (tagId: string) => { - const normalizedId = String(tagId); - if (editingTagId.value && editingTagId.value !== normalizedId) { - editingTagId.value = null; - editingTagName.value = ''; - } - if (creatingTag.value) { - creatingTag.value = false; - newTagName.value = ''; - } - if (selectedTagId.value === normalizedId) { - return; - } - handleTagFilterChange(normalizedId); -}; - -const handleUntaggedClick = () => { if (creatingTag.value) { creatingTag.value = false; newTagName.value = ''; @@ -249,8 +225,8 @@ const handleUntaggedClick = () => { editingTagId.value = null; editingTagName.value = ''; } - if (selectedTagId.value === UNTAGGED_TAG_ID) return; - handleTagFilterChange(UNTAGGED_TAG_ID); + if (selectedTagId.value === tagId) return; + handleTagFilterChange(tagId); }; const startCreateTag = () => { @@ -360,7 +336,9 @@ const confirmDeleteTag = (tag: any) => { .then(() => { MessagePlugin.success(t('knowledgeBase.tagDeleteSuccess')); if (selectedTagId.value === tag.id) { - handleTagFilterChange(UNTAGGED_TAG_ID); + // Reset to show all entries when current tag is deleted + selectedTagId.value = ''; + handleTagFilterChange(''); } loadTags(kbId.value); loadKnowledgeFiles(kbId.value); @@ -372,8 +350,8 @@ const confirmDeleteTag = (tag: any) => { const handleKnowledgeTagChange = async (knowledgeId: string, tagValue: string) => { try { - // When selecting "untagged", pass null to clear the tag - const tagIdToUpdate = tagValue === UNTAGGED_TAG_ID ? null : (tagValue || null); + // Pass the tag value directly (empty string means no tag) + const tagIdToUpdate = tagValue || null; await updateKnowledgeTagBatch({ updates: { [knowledgeId]: tagIdToUpdate } }); MessagePlugin.success(t('knowledgeBase.tagUpdateSuccess') || '分类已更新'); loadKnowledgeFiles(kbId.value); @@ -392,7 +370,7 @@ const loadKnowledgeBaseInfo = async (targetKbId: string) => { try { const res: any = await getKnowledgeBaseById(targetKbId); kbInfo.value = res?.data || null; - selectedTagId.value = UNTAGGED_TAG_ID; + selectedTagId.value = ''; if (!isFAQ.value) { loadKnowledgeFiles(targetKbId); } else { @@ -1042,16 +1020,12 @@ async function createNewSession(value: string): Promise { v-for="tag in filteredTags" :key="tag.id" class="tag-list-item" - :class="{ active: selectedTagId === tag.id, editing: editingTagId === tag.id && tag.id !== UNTAGGED_TAG_ID }" - @click="tag.id === UNTAGGED_TAG_ID ? handleUntaggedClick() : handleTagRowClick(tag.id)" + :class="{ active: selectedTagId === tag.id, editing: editingTagId === tag.id }" + @click="handleTagRowClick(tag.id)" >
- - -