Unverified Commit 4420281d authored by zxhlyh's avatar zxhlyh Committed by GitHub

Feat/segment add tag (#907)

parent d9afebe2
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<TagInputProps> = ({
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 (
<div className='flex flex-wrap'>
{
items.map((item, index) => (
<div
key={item}
className='flex items-center mr-1 mt-1 px-2 py-1 text-sm text-gray-700 rounded-lg border border-gray-200'>
{item}
{
!disableRemove && (
<X
className='ml-0.5 w-3 h-3 text-gray-500 cursor-pointer'
onClick={() => handleRemove(index)}
/>
)
}
</div>
))
}
{
!disableAdd && (
<AutosizeInput
inputClassName='outline-none appearance-none placeholder:text-gray-300 caret-primary-600 hover:placeholder:text-gray-400'
className={`
mt-1 py-1 rounded-lg border border-transparent text-sm max-w-[300px] overflow-hidden
${focused && 'px-2 border !border-dashed !border-gray-200'}
`}
onFocus={() => setFocused(true)}
onBlur={handleBlur}
value={value}
onChange={(e: ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={t('datasetDocuments.segment.addKeyWord')}
/>
)
}
</div>
)
}
export default TagInput
......@@ -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<SegmentDetailModel> & { id: string }
onChangeSwitch?: (segId: string, enabled: boolean) => Promise<void>
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<ISegmentDetailProps> = memo(({
const [isEditing, setIsEditing] = useState(false)
const [question, setQuestion] = useState(segInfo?.content || '')
const [answer, setAnswer] = useState(segInfo?.answer || '')
const [keywords, setKeywords] = useState<string[]>(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<ISegmentDetailProps> = memo(({
<div className={s.keywordWrapper}>
{!segInfo?.keywords?.length
? '-'
: segInfo?.keywords?.map((word: any) => {
return <div className={s.keyword}>{word}</div>
})}
: (
<TagInput
items={keywords}
onChange={newKeywords => setKeywords(newKeywords)}
disableAdd={!isEditing}
disableRemove={!isEditing || (keywords.length === 1)}
/>
)
}
</div>
<div className={cn(s.footer, s.numberInfo)}>
<div className='flex items-center'>
......@@ -272,7 +281,7 @@ const Completed: FC<ICompletedProps> = ({ 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<ICompletedProps> = ({ 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<ICompletedProps> = ({ 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
......
......@@ -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<NewSegmentModalProps> = memo(({
const [question, setQuestion] = useState('')
const [answer, setAnswer] = useState('')
const { datasetId, documentId } = useParams()
const [keywords, setKeywords] = useState<string[]>([])
const handleCancel = () => {
setQuestion('')
setAnswer('')
onCancel()
setKeywords([])
}
const handleSave = async () => {
......@@ -54,6 +57,9 @@ const NewSegmentModal: FC<NewSegmentModalProps> = 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<NewSegmentModalProps> = memo(({
</span>
</div>
<div className='mb-4 py-1.5 h-[420px] overflow-auto'>{renderContent()}</div>
<div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetDocuments.segment.keywords')}</div>
<div className='mb-8'></div>
<div className='text-xs font-medium text-gray-500'>{t('datasetDocuments.segment.keywords')}</div>
<div className='mb-8'>
<TagInput items={keywords} onChange={newKeywords => setKeywords(newKeywords)} />
</div>
<div className='flex justify-end'>
<Button
className='mr-2 !h-9 !px-4 !py-2 text-sm font-medium text-gray-700 !rounded-lg'
......
declare module 'lamejs';
declare module 'react-18-input-autosize';
\ No newline at end of file
......@@ -308,6 +308,8 @@ const translation = {
segment: {
paragraphs: 'Paragraphs',
keywords: 'Key Words',
addKeyWord: 'Add key word',
keywordError: 'The maximum length of keyword is 20',
characters: 'characters',
hitCount: 'hit count',
vectorHash: 'Vector hash: ',
......
......@@ -307,6 +307,8 @@ const translation = {
segment: {
paragraphs: '段落',
keywords: '关键词',
addKeyWord: '添加关键词',
keywordError: '关键词最大长度为 20',
characters: '字符',
hitCount: '命中次数',
vectorHash: '向量哈希:',
......
......@@ -388,4 +388,5 @@ export type RelatedAppResponse = {
export type SegmentUpdator = {
content: string
answer?: string
keywords?: string[]
}
......@@ -47,6 +47,7 @@
"next": "13.3.1",
"qs": "^6.11.1",
"react": "^18.2.0",
"react-18-input-autosize": "^3.0.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.2",
"react-headless-pagination": "^1.1.4",
......@@ -80,8 +81,8 @@
"@types/crypto-js": "^4.1.1",
"@types/js-cookie": "^3.0.3",
"@types/lodash-es": "^4.17.7",
"@types/node": "18.15.0",
"@types/negotiator": "^0.6.1",
"@types/node": "18.15.0",
"@types/qs": "^6.9.7",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment