feat: clear all annotation (#22878)

Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
呆萌闷油瓶
2025-07-25 18:06:03 +08:00
committed by GitHub
parent 31985d94fa
commit ee50a2bcd5
8 changed files with 113 additions and 4 deletions

View File

@@ -123,6 +123,17 @@ class AnnotationListApi(Resource):
}
return response, 200
@setup_required
@login_required
@account_initialization_required
def delete(self, app_id):
if not current_user.is_editor:
raise Forbidden()
app_id = str(app_id)
AppAnnotationService.clear_all_annotations(app_id)
return {"result": "success"}, 204
class AnnotationExportApi(Resource):
@setup_required

View File

@@ -440,3 +440,27 @@ class AppAnnotationService:
"embedding_model_name": collection_binding_detail.model_name,
},
}
@classmethod
def clear_all_annotations(cls, app_id: str) -> dict:
app = (
db.session.query(App)
.filter(App.id == app_id, App.tenant_id == current_user.current_tenant_id, App.status == "normal")
.first()
)
if not app:
raise NotFound("App not found")
annotations_query = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app_id)
for annotation in annotations_query.yield_per(100):
annotation_hit_histories_query = db.session.query(AppAnnotationHitHistory).filter(
AppAnnotationHitHistory.annotation_id == annotation.id
)
for annotation_hit_history in annotation_hit_histories_query.yield_per(100):
db.session.delete(annotation_hit_history)
db.session.delete(annotation)
db.session.commit()
return {"result": "success"}

View File

@@ -0,0 +1,32 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Confirm from '@/app/components/base/confirm'
type Props = {
isShow: boolean
onHide: () => void
onConfirm: () => void
}
const ClearAllAnnotationsConfirmModal: FC<Props> = ({
isShow,
onHide,
onConfirm,
}) => {
const { t } = useTranslation()
return (
<Confirm
isShow={isShow}
onCancel={onHide}
onConfirm={onConfirm}
type='danger'
title={t('appAnnotation.table.header.clearAllConfirm')}
/>
)
}
export default React.memo(ClearAllAnnotationsConfirmModal)

View File

@@ -1,9 +1,11 @@
'use client'
import type { FC } from 'react'
import React, { Fragment, useEffect, useState } from 'react'
import ClearAllAnnotationsConfirmModal from '../clear-all-annotations-confirm-modal'
import { useTranslation } from 'react-i18next'
import {
RiAddLine,
RiDeleteBinLine,
RiMoreFill,
} from '@remixicon/react'
import { useContext } from 'use-context-selector'
@@ -22,6 +24,7 @@ import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows
import I18n from '@/context/i18n'
import { fetchExportAnnotationList } from '@/service/annotation'
import { clearAllAnnotations } from '@/service/annotation'
import { LanguagesSupported } from '@/i18n-config/language'
const CSV_HEADER_QA_EN = ['Question', 'Answer']
@@ -76,7 +79,21 @@ const HeaderOptions: FC<Props> = ({
}, [controlUpdateList])
const [showBulkImportModal, setShowBulkImportModal] = useState(false)
const [showClearConfirm, setShowClearConfirm] = useState(false)
const handleClearAll = () => {
setShowClearConfirm(true)
}
const handleConfirmed = async () => {
try {
await clearAllAnnotations(appId)
onAdded()
}
catch (_) {
}
finally {
setShowClearConfirm(false)
}
}
const Operations = () => {
return (
<div className="w-full py-1">
@@ -125,6 +142,15 @@ const HeaderOptions: FC<Props> = ({
</MenuItems>
</Transition>
</Menu>
<button
onClick={handleClearAll}
className='mx-1 flex h-9 w-[calc(100%_-_8px)] cursor-pointer items-center space-x-2 rounded-lg px-3 py-2 text-red-600 hover:bg-red-50 disabled:opacity-50'
>
<RiDeleteBinLine className='h-4 w-4'/>
<span className='system-sm-regular grow text-left'>
{t('appAnnotation.table.header.clearAll')}
</span>
</button>
</div>
)
}
@@ -169,6 +195,15 @@ const HeaderOptions: FC<Props> = ({
/>
)
}
{
showClearConfirm && (
<ClearAllAnnotationsConfirmModal
isShow={showClearConfirm}
onHide={() => setShowClearConfirm(false)}
onConfirm={handleConfirmed}
/>
)
}
</div>
)
}

View File

@@ -16,7 +16,8 @@ const translation = {
addAnnotation: 'Add Annotation',
bulkImport: 'Bulk Import',
bulkExport: 'Bulk Export',
clearAll: 'Clear All Annotation',
clearAll: 'Delete All',
clearAllConfirm: 'Delete all annotations?',
},
},
editModal: {

View File

@@ -18,7 +18,8 @@ const translation = {
addAnnotation: '注釈を追加',
bulkImport: '一括インポート',
bulkExport: '一括エクスポート',
clearAll: 'すべての注釈をクリア',
clearAll: 'すべて削除',
clearAllConfirm: 'すべての寸法を削除?',
},
},
editModal: {

View File

@@ -18,7 +18,8 @@ const translation = {
addAnnotation: '添加标注',
bulkImport: '批量导入',
bulkExport: '批量导出',
clearAll: '删除所有标注',
clearAll: '删除所有',
clearAllConfirm: '删除所有标注?',
},
},
editModal: {

View File

@@ -63,3 +63,7 @@ export const delAnnotation = (appId: string, annotationId: string) => {
export const fetchHitHistoryList = (appId: string, annotationId: string, params: Record<string, any>) => {
return get(`apps/${appId}/annotations/${annotationId}/hit-histories`, { params })
}
export const clearAllAnnotations = (appId: string): Promise<any> => {
return del(`apps/${appId}/annotations`)
}