mirror of https://github.com/langgenius/dify.git
feat: refactor docForm type to use ChuckingMode and improve UI for question/answer sections
This commit is contained in:
parent
14f14420e7
commit
4850a99ce5
|
|
@ -9,7 +9,7 @@ import ChunkContent from './common/chunk-content'
|
|||
import Dot from './common/dot'
|
||||
import { SegmentIndexTag } from './common/segment-index-tag'
|
||||
import { useSegmentListContext } from './index'
|
||||
import type { ChildChunkDetail } from '@/models/datasets'
|
||||
import type { ChildChunkDetail, ChuckingMode } from '@/models/datasets'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
|
@ -21,7 +21,7 @@ type IChildSegmentDetailProps = {
|
|||
childChunkInfo?: Partial<ChildChunkDetail> & { id: string }
|
||||
onUpdate: (segmentId: string, childChunkId: string, content: string) => void
|
||||
onCancel: () => void
|
||||
docForm: string
|
||||
docForm: ChuckingMode
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ChuckingMode } from '@/models/datasets'
|
||||
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/common'
|
||||
|
||||
type IChunkContentProps = {
|
||||
|
|
@ -8,7 +9,7 @@ type IChunkContentProps = {
|
|||
onQuestionChange: (question: string) => void
|
||||
onAnswerChange?: (answer: string) => void
|
||||
isEditMode?: boolean
|
||||
docForm: string
|
||||
docForm: ChuckingMode
|
||||
}
|
||||
|
||||
const ChunkContent: FC<IChunkContentProps> = ({
|
||||
|
|
@ -21,22 +22,22 @@ const ChunkContent: FC<IChunkContentProps> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (docForm === 'qa_model') {
|
||||
if (docForm === ChuckingMode.qa) {
|
||||
return (
|
||||
<>
|
||||
<div className='mb-1 text-xs font-medium text-gray-500'>QUESTION</div>
|
||||
<div className='text-text-tertiary text-xs font-medium'>QUESTION</div>
|
||||
<AutoHeightTextarea
|
||||
outerClassName='mb-4'
|
||||
className='leading-6 text-md text-gray-800'
|
||||
outerClassName='mb-6 mt-1'
|
||||
className='text-text-secondary text-sm tracking-[-0.07px] caret-[#295EFF]'
|
||||
value={question}
|
||||
placeholder={t('datasetDocuments.segment.questionPlaceholder') || ''}
|
||||
onChange={e => onQuestionChange(e.target.value)}
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
<div className='mb-1 text-xs font-medium text-gray-500'>ANSWER</div>
|
||||
<div className='text-text-tertiary text-xs font-medium'>ANSWER</div>
|
||||
<AutoHeightTextarea
|
||||
outerClassName='mb-4'
|
||||
className='leading-6 text-md text-gray-800'
|
||||
outerClassName='mb-6 mt-1'
|
||||
className='text-text-secondary text-sm tracking-[-0.07px] caret-[#295EFF]'
|
||||
value={answer}
|
||||
placeholder={t('datasetDocuments.segment.answerPlaceholder') || ''}
|
||||
onChange={e => onAnswerChange?.(e.target.value)}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import { ToastContext } from '@/app/components/base/toast'
|
|||
import type { Item } from '@/app/components/base/select'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import { updateSegment } from '@/service/datasets'
|
||||
import type { ChildChunkDetail, SegmentDetailModel, SegmentUpdater } from '@/models/datasets'
|
||||
import { type ChildChunkDetail, ChuckingMode, type SegmentDetailModel, type SegmentUpdater } from '@/models/datasets'
|
||||
import NewSegment from '@/app/components/datasets/documents/detail/new-segment'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
|
|
@ -252,7 +252,7 @@ const Completed: FC<ICompletedProps> = ({
|
|||
needRegenerate = false,
|
||||
) => {
|
||||
const params: SegmentUpdater = { content: '' }
|
||||
if (docForm === 'qa_model') {
|
||||
if (docForm === ChuckingMode.qa) {
|
||||
if (!question.trim())
|
||||
return notify({ type: 'error', message: t('datasetDocuments.segment.questionEmpty') })
|
||||
if (!answer.trim())
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import Dot from './common/dot'
|
|||
import { useSegmentListContext } from './index'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import type { ChildChunkDetail, SegmentUpdater } from '@/models/datasets'
|
||||
import { type ChildChunkDetail, ChuckingMode, type SegmentUpdater } from '@/models/datasets'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
|
|
@ -144,7 +144,7 @@ const NewChildSegmentModal: FC<NewChildSegmentModalProps> = ({
|
|||
<div className={classNames('flex grow overflow-hidden', fullScreen ? 'w-full flex-row justify-center px-6 pt-6 gap-x-8' : 'flex-col gap-y-1 py-3 px-4')}>
|
||||
<div className={classNames('break-all overflow-y-auto whitespace-pre-line', fullScreen ? 'w-1/2' : 'grow')}>
|
||||
<ChunkContent
|
||||
docForm=''
|
||||
docForm={ChuckingMode.parentChild}
|
||||
question={content}
|
||||
onQuestionChange={content => setContent(content)}
|
||||
isEditMode={true}
|
||||
|
|
|
|||
|
|
@ -98,13 +98,22 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
|||
if (answer) {
|
||||
return (
|
||||
<>
|
||||
<div className='flex'>
|
||||
<div className='flex gap-x-1'>
|
||||
<div className='w-4 mr-2 text-[13px] font-medium leading-[20px] text-text-tertiary'>Q</div>
|
||||
<div className='text-text-secondary body-md-regular'>{content}</div>
|
||||
<div
|
||||
className={cn('text-text-secondary body-md-regular',
|
||||
isCollapsed ? 'line-clamp-2' : 'line-clamp-20',
|
||||
)}>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex'>
|
||||
<div className='flex gap-x-1'>
|
||||
<div className='w-4 mr-2 text-[13px] font-medium leading-[20px] text-text-tertiary'>A</div>
|
||||
<div className='text-text-secondary body-md-regular'>{answer}</div>
|
||||
<div className={cn('text-text-secondary body-md-regular',
|
||||
isCollapsed ? 'line-clamp-2' : 'line-clamp-20',
|
||||
)}>
|
||||
{answer}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import RegenerationModal from './common/regeneration-modal'
|
|||
import { SegmentIndexTag } from './common/segment-index-tag'
|
||||
import Dot from './common/dot'
|
||||
import { useSegmentListContext } from './index'
|
||||
import type { SegmentDetailModel } from '@/models/datasets'
|
||||
import { ChuckingMode, type SegmentDetailModel } from '@/models/datasets'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
|
@ -23,7 +23,7 @@ type ISegmentDetailProps = {
|
|||
onUpdate: (segmentId: string, q: string, a: string, k: string[], needRegenerate?: boolean) => void
|
||||
onCancel: () => void
|
||||
isEditMode?: boolean
|
||||
docForm: string
|
||||
docForm: ChuckingMode
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -85,12 +85,17 @@ const SegmentDetail: FC<ISegmentDetailProps> = ({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isEditMode])
|
||||
|
||||
const isQAModel = useMemo(() => {
|
||||
return docForm === ChuckingMode.qa
|
||||
}, [docForm])
|
||||
|
||||
const wordCountText = useMemo(() => {
|
||||
const total = formatNumber(isEditMode ? question.length : segInfo!.word_count as number)
|
||||
const count = isEditMode ? question.length : segInfo!.word_count as number
|
||||
const contentLength = isQAModel ? (question.length + answer.length) : question.length
|
||||
const total = formatNumber(isEditMode ? contentLength : segInfo!.word_count as number)
|
||||
const count = isEditMode ? contentLength : segInfo!.word_count as number
|
||||
return `${total} ${t('datasetDocuments.segment.characters', { count })}`
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isEditMode, question.length, segInfo?.word_count])
|
||||
}, [isEditMode, question.length, answer.length, segInfo?.word_count, isQAModel])
|
||||
|
||||
const labelPrefix = useMemo(() => {
|
||||
return isParentChildMode ? t('datasetDocuments.segment.parentChunk') : t('datasetDocuments.segment.chunk')
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import Dot from './completed/common/dot'
|
|||
import { useDocumentContext } from './index'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import type { SegmentUpdater } from '@/models/datasets'
|
||||
import { ChuckingMode, type SegmentUpdater } from '@/models/datasets'
|
||||
import { addSegment } from '@/service/datasets'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
|
|
@ -23,7 +23,7 @@ import Divider from '@/app/components/base/divider'
|
|||
|
||||
type NewSegmentModalProps = {
|
||||
onCancel: () => void
|
||||
docForm: string
|
||||
docForm: ChuckingMode
|
||||
onSave: () => void
|
||||
viewNewlyAddedChunk: () => void
|
||||
}
|
||||
|
|
@ -59,6 +59,10 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
|||
</button>
|
||||
</>
|
||||
|
||||
const isQAModel = useMemo(() => {
|
||||
return docForm === ChuckingMode.qa
|
||||
}, [docForm])
|
||||
|
||||
const handleCancel = (actionType: 'esc' | 'add' = 'esc') => {
|
||||
if (actionType === 'esc' || !addAnother)
|
||||
onCancel()
|
||||
|
|
@ -69,18 +73,30 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
|||
|
||||
const handleSave = async () => {
|
||||
const params: SegmentUpdater = { content: '' }
|
||||
if (docForm === 'qa_model') {
|
||||
if (!question.trim())
|
||||
return notify({ type: 'error', message: t('datasetDocuments.segment.questionEmpty') })
|
||||
if (!answer.trim())
|
||||
return notify({ type: 'error', message: t('datasetDocuments.segment.answerEmpty') })
|
||||
if (isQAModel) {
|
||||
if (!question.trim()) {
|
||||
return notify({
|
||||
type: 'error',
|
||||
message: t('datasetDocuments.segment.questionEmpty'),
|
||||
})
|
||||
}
|
||||
if (!answer.trim()) {
|
||||
return notify({
|
||||
type: 'error',
|
||||
message: t('datasetDocuments.segment.answerEmpty'),
|
||||
})
|
||||
}
|
||||
|
||||
params.content = question
|
||||
params.answer = answer
|
||||
}
|
||||
else {
|
||||
if (!question.trim())
|
||||
return notify({ type: 'error', message: t('datasetDocuments.segment.contentEmpty') })
|
||||
if (!question.trim()) {
|
||||
return notify({
|
||||
type: 'error',
|
||||
message: t('datasetDocuments.segment.contentEmpty'),
|
||||
})
|
||||
}
|
||||
|
||||
params.content = question
|
||||
}
|
||||
|
|
@ -109,19 +125,17 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
|||
}
|
||||
|
||||
const wordCountText = useMemo(() => {
|
||||
const count = question.length
|
||||
const count = isQAModel ? (question.length + answer.length) : question.length
|
||||
return `${formatNumber(count)} ${t('datasetDocuments.segment.characters', { count })}`
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [question.length])
|
||||
}, [question.length, answer.length, isQAModel])
|
||||
|
||||
return (
|
||||
<div className={'flex flex-col h-full'}>
|
||||
<div className={classNames('flex items-center justify-between', fullScreen ? 'py-3 pr-4 pl-6 border border-divider-subtle' : 'pt-3 pr-3 pl-4')}>
|
||||
<div className='flex flex-col'>
|
||||
<div className='text-text-primary system-xl-semibold'>{
|
||||
docForm === 'qa_model'
|
||||
? t('datasetDocuments.segment.newQaSegment')
|
||||
: t('datasetDocuments.segment.addChunk')
|
||||
t('datasetDocuments.segment.addChunk')
|
||||
}</div>
|
||||
<div className='flex items-center gap-x-2'>
|
||||
<SegmentIndexTag label={'New Chunk'} />
|
||||
|
|
|
|||
Loading…
Reference in New Issue