From 4420281d9660ac2f4c46a4dc388274ac0483b4ba Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Fri, 18 Aug 2023 17:18:58 +0800 Subject: [PATCH] Feat/segment add tag (#907) --- web/app/components/base/tag-input/index.tsx | 94 +++++++++++++++++++ .../documents/detail/completed/index.tsx | 25 +++-- .../documents/detail/new-segment-modal.tsx | 12 ++- web/global.d.ts | 3 +- web/i18n/lang/dataset-documents.en.ts | 2 + web/i18n/lang/dataset-documents.zh.ts | 2 + web/models/datasets.ts | 1 + web/package.json | 3 +- 8 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 web/app/components/base/tag-input/index.tsx diff --git a/web/app/components/base/tag-input/index.tsx b/web/app/components/base/tag-input/index.tsx new file mode 100644 index 0000000000..576adf9f92 --- /dev/null +++ b/web/app/components/base/tag-input/index.tsx @@ -0,0 +1,94 @@ +import { useState } from 'react' +import type { ChangeEvent, FC, KeyboardEvent } from 'react' +import {} from 'use-context-selector' +import { useTranslation } from 'react-i18next' +import AutosizeInput from 'react-18-input-autosize' +import { X } from '@/app/components/base/icons/src/vender/line/general' +import { useToastContext } from '@/app/components/base/toast' + +type TagInputProps = { + items: string[] + onChange: (items: string[]) => void + disableRemove?: boolean + disableAdd?: boolean +} + +const TagInput: FC = ({ + items, + onChange, + disableAdd, + disableRemove, +}) => { + const { t } = useTranslation() + const { notify } = useToastContext() + const [value, setValue] = useState('') + const [focused, setFocused] = useState(false) + const handleRemove = (index: number) => { + const copyItems = [...items] + copyItems.splice(index, 1) + + onChange(copyItems) + } + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Enter') { + const valueTrimed = value.trim() + if (!valueTrimed || (items.find(item => item === valueTrimed))) + return + + if (valueTrimed.length > 20) { + notify({ type: 'error', message: t('datasetDocuments.segment.keywordError') }) + return + } + + onChange([...items, valueTrimed]) + setValue('') + } + } + + const handleBlur = () => { + setValue('') + setFocused(false) + } + + return ( +
+ { + items.map((item, index) => ( +
+ {item} + { + !disableRemove && ( + handleRemove(index)} + /> + ) + } +
+ )) + } + { + !disableAdd && ( + setFocused(true)} + onBlur={handleBlur} + value={value} + onChange={(e: ChangeEvent) => setValue(e.target.value)} + onKeyDown={handleKeyDown} + placeholder={t('datasetDocuments.segment.addKeyWord')} + /> + ) + } +
+ ) +} + +export default TagInput diff --git a/web/app/components/datasets/documents/detail/completed/index.tsx b/web/app/components/datasets/documents/detail/completed/index.tsx index 298441ea19..cbfc534218 100644 --- a/web/app/components/datasets/documents/detail/completed/index.tsx +++ b/web/app/components/datasets/documents/detail/completed/index.tsx @@ -26,6 +26,7 @@ import { Edit03, XClose } from '@/app/components/base/icons/src/vender/line/gene import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/common' import Button from '@/app/components/base/button' import NewSegmentModal from '@/app/components/datasets/documents/detail/new-segment-modal' +import TagInput from '@/app/components/base/tag-input' export const SegmentIndexTag: FC<{ positionId: string | number; className?: string }> = ({ positionId, className }) => { const localPositionId = useMemo(() => { @@ -45,7 +46,7 @@ export const SegmentIndexTag: FC<{ positionId: string | number; className?: stri type ISegmentDetailProps = { segInfo?: Partial & { id: string } onChangeSwitch?: (segId: string, enabled: boolean) => Promise - onUpdate: (segmentId: string, q: string, a: string) => void + onUpdate: (segmentId: string, q: string, a: string, k: string[]) => void onCancel: () => void } /** @@ -61,14 +62,16 @@ export const SegmentDetail: FC = memo(({ const [isEditing, setIsEditing] = useState(false) const [question, setQuestion] = useState(segInfo?.content || '') const [answer, setAnswer] = useState(segInfo?.answer || '') + const [keywords, setKeywords] = useState(segInfo?.keywords || []) const handleCancel = () => { setIsEditing(false) setQuestion(segInfo?.content || '') setAnswer(segInfo?.answer || '') + setKeywords(segInfo?.keywords || []) } const handleSave = () => { - onUpdate(segInfo?.id || '', question, answer) + onUpdate(segInfo?.id || '', question, answer, keywords) } const renderContent = () => { @@ -148,9 +151,15 @@ export const SegmentDetail: FC = memo(({
{!segInfo?.keywords?.length ? '-' - : segInfo?.keywords?.map((word: any) => { - return
{word}
- })} + : ( + setKeywords(newKeywords)} + disableAdd={!isEditing} + disableRemove={!isEditing || (keywords.length === 1)} + /> + ) + }
@@ -272,7 +281,7 @@ const Completed: FC = ({ showNewSegmentModal, onNewSegmentModal } } - const handleUpdateSegment = async (segmentId: string, question: string, answer: string) => { + const handleUpdateSegment = async (segmentId: string, question: string, answer: string, keywords: string[]) => { const params: SegmentUpdator = { content: '' } if (docForm === 'qa_model') { if (!question.trim()) @@ -290,6 +299,9 @@ const Completed: FC = ({ showNewSegmentModal, onNewSegmentModal params.content = question } + if (keywords.length) + params.keywords = keywords + const res = await updateSegment({ datasetId, documentId, segmentId, body: params }) notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) onCloseModal() @@ -298,6 +310,7 @@ const Completed: FC = ({ showNewSegmentModal, onNewSegmentModal if (seg.id === segmentId) { seg.answer = res.data.answer seg.content = res.data.content + seg.keywords = res.data.keywords seg.word_count = res.data.word_count seg.hit_count = res.data.hit_count seg.index_node_hash = res.data.index_node_hash diff --git a/web/app/components/datasets/documents/detail/new-segment-modal.tsx b/web/app/components/datasets/documents/detail/new-segment-modal.tsx index 2cb1ef0b46..9abe36a9f9 100644 --- a/web/app/components/datasets/documents/detail/new-segment-modal.tsx +++ b/web/app/components/datasets/documents/detail/new-segment-modal.tsx @@ -10,6 +10,7 @@ import { Hash02, XClose } from '@/app/components/base/icons/src/vender/line/gene import { ToastContext } from '@/app/components/base/toast' import type { SegmentUpdator } from '@/models/datasets' import { addSegment } from '@/service/datasets' +import TagInput from '@/app/components/base/tag-input' type NewSegmentModalProps = { isShow: boolean @@ -29,11 +30,13 @@ const NewSegmentModal: FC = memo(({ const [question, setQuestion] = useState('') const [answer, setAnswer] = useState('') const { datasetId, documentId } = useParams() + const [keywords, setKeywords] = useState([]) const handleCancel = () => { setQuestion('') setAnswer('') onCancel() + setKeywords([]) } const handleSave = async () => { @@ -54,6 +57,9 @@ const NewSegmentModal: FC = memo(({ params.content = question } + if (keywords?.length) + params.keywords = keywords + await addSegment({ datasetId, documentId, body: params }) notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) handleCancel() @@ -117,8 +123,10 @@ const NewSegmentModal: FC = memo(({
{renderContent()}
-
{t('datasetDocuments.segment.keywords')}
-
+
{t('datasetDocuments.segment.keywords')}
+
+ setKeywords(newKeywords)} /> +