mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-09 10:56:52 +08:00
Initial commit
This commit is contained in:
3
web/app/components/datasets/hit-testing/assets/clock.svg
Normal file
3
web/app/components/datasets/hit-testing/assets/clock.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.9166 9.58333L17.2505 11.25L15.5833 9.58333M17.4542 10.8333C17.4845 10.5597 17.5 10.2817 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5C12.3561 17.5 14.4584 16.4136 15.8333 14.7144M10 5.83333V10L12.5 11.6667" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 466 B |
6
web/app/components/datasets/hit-testing/assets/grid.svg
Normal file
6
web/app/components/datasets/hit-testing/assets/grid.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.9 1.75H2.68333C2.35664 1.75 2.19329 1.75 2.06851 1.81358C1.95874 1.86951 1.86951 1.95874 1.81358 2.06851C1.75 2.19329 1.75 2.35664 1.75 2.68333V4.9C1.75 5.2267 1.75 5.39005 1.81358 5.51483C1.86951 5.62459 1.95874 5.71383 2.06851 5.76975C2.19329 5.83333 2.35664 5.83333 2.68333 5.83333H4.9C5.2267 5.83333 5.39005 5.83333 5.51483 5.76975C5.62459 5.71383 5.71383 5.62459 5.76975 5.51483C5.83333 5.39005 5.83333 5.2267 5.83333 4.9V2.68333C5.83333 2.35664 5.83333 2.19329 5.76975 2.06851C5.71383 1.95874 5.62459 1.86951 5.51483 1.81358C5.39005 1.75 5.2267 1.75 4.9 1.75Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.3167 1.75H9.1C8.7733 1.75 8.60995 1.75 8.48517 1.81358C8.37541 1.86951 8.28617 1.95874 8.23025 2.06851C8.16667 2.19329 8.16667 2.35664 8.16667 2.68333V4.9C8.16667 5.2267 8.16667 5.39005 8.23025 5.51483C8.28617 5.62459 8.37541 5.71383 8.48517 5.76975C8.60995 5.83333 8.7733 5.83333 9.1 5.83333H11.3167C11.6434 5.83333 11.8067 5.83333 11.9315 5.76975C12.0413 5.71383 12.1305 5.62459 12.1864 5.51483C12.25 5.39005 12.25 5.2267 12.25 4.9V2.68333C12.25 2.35664 12.25 2.19329 12.1864 2.06851C12.1305 1.95874 12.0413 1.86951 11.9315 1.81358C11.8067 1.75 11.6434 1.75 11.3167 1.75Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.3167 8.16667H9.1C8.7733 8.16667 8.60995 8.16667 8.48517 8.23025C8.37541 8.28617 8.28617 8.37541 8.23025 8.48517C8.16667 8.60995 8.16667 8.7733 8.16667 9.1V11.3167C8.16667 11.6434 8.16667 11.8067 8.23025 11.9315C8.28617 12.0413 8.37541 12.1305 8.48517 12.1864C8.60995 12.25 8.7733 12.25 9.1 12.25H11.3167C11.6434 12.25 11.8067 12.25 11.9315 12.1864C12.0413 12.1305 12.1305 12.0413 12.1864 11.9315C12.25 11.8067 12.25 11.6434 12.25 11.3167V9.1C12.25 8.7733 12.25 8.60995 12.1864 8.48517C12.1305 8.37541 12.0413 8.28617 11.9315 8.23025C11.8067 8.16667 11.6434 8.16667 11.3167 8.16667Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.9 8.16667H2.68333C2.35664 8.16667 2.19329 8.16667 2.06851 8.23025C1.95874 8.28617 1.86951 8.37541 1.81358 8.48517C1.75 8.60995 1.75 8.7733 1.75 9.1V11.3167C1.75 11.6434 1.75 11.8067 1.81358 11.9315C1.86951 12.0413 1.95874 12.1305 2.06851 12.1864C2.19329 12.25 2.35664 12.25 2.68333 12.25H4.9C5.2267 12.25 5.39005 12.25 5.51483 12.1864C5.62459 12.1305 5.71383 12.0413 5.76975 11.9315C5.83333 11.8067 5.83333 11.6434 5.83333 11.3167V9.1C5.83333 8.7733 5.83333 8.60995 5.76975 8.48517C5.71383 8.37541 5.62459 8.28617 5.51483 8.23025C5.39005 8.16667 5.2267 8.16667 4.9 8.16667Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
10
web/app/components/datasets/hit-testing/assets/plugin.svg
Normal file
10
web/app/components/datasets/hit-testing/assets/plugin.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4151_6854)">
|
||||
<path d="M4.37484 2.62484C4.37484 1.81942 5.02776 1.1665 5.83317 1.1665C6.63859 1.1665 7.2915 1.81942 7.2915 2.62484V3.49984H7.87484C8.69023 3.49984 9.09793 3.49984 9.41953 3.63305C9.84833 3.81066 10.189 4.15134 10.3666 4.58014C10.4998 4.90174 10.4998 5.30944 10.4998 6.12484H11.3748C12.1803 6.12484 12.8332 6.77776 12.8332 7.58317C12.8332 8.38859 12.1803 9.0415 11.3748 9.0415H10.4998V10.0332C10.4998 11.0133 10.4998 11.5033 10.3091 11.8777C10.1413 12.2069 9.8736 12.4747 9.54432 12.6424C9.16997 12.8332 8.67993 12.8332 7.69984 12.8332H7.2915V11.8123C7.2915 11.0875 6.70388 10.4998 5.979 10.4998C5.25413 10.4998 4.6665 11.0875 4.6665 11.8123V12.8332H3.9665C2.98641 12.8332 2.49637 12.8332 2.12202 12.6424C1.79274 12.4747 1.52502 12.2069 1.35724 11.8777C1.1665 11.5033 1.1665 11.0133 1.1665 10.0332V9.0415H2.0415C2.84692 9.0415 3.49984 8.38859 3.49984 7.58317C3.49984 6.77776 2.84692 6.12484 2.0415 6.12484H1.1665C1.1665 5.30944 1.1665 4.90174 1.29971 4.58014C1.47733 4.15134 1.81801 3.81066 2.24681 3.63305C2.56841 3.49984 2.97611 3.49984 3.7915 3.49984H4.37484V2.62484Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4151_6854">
|
||||
<rect width="14" height="14" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
99
web/app/components/datasets/hit-testing/hit-detail.tsx
Normal file
99
web/app/components/datasets/hit-testing/hit-detail.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React, { FC } from "react";
|
||||
import cn from "classnames";
|
||||
import { SegmentDetailModel } from "@/models/datasets";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Divider from "@/app/components/base/divider";
|
||||
import { SegmentIndexTag } from "../documents/detail/completed";
|
||||
import s from "../documents/detail/completed/style.module.css";
|
||||
import ReactECharts from "echarts-for-react";
|
||||
|
||||
type IScatterChartProps = {
|
||||
data: Array<number[]>
|
||||
curr: Array<number[]>
|
||||
}
|
||||
|
||||
const ScatterChart: FC<IScatterChartProps> = ({ data, curr }) => {
|
||||
const option = {
|
||||
xAxis: {},
|
||||
yAxis: {},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'effectScatter',
|
||||
symbolSize: 5,
|
||||
data: curr,
|
||||
},
|
||||
{
|
||||
type: 'scatter',
|
||||
symbolSize: 5,
|
||||
data,
|
||||
}
|
||||
]
|
||||
};
|
||||
return (
|
||||
<ReactECharts option={option} style={{ height: 380, width: 430 }} />
|
||||
)
|
||||
}
|
||||
|
||||
type IHitDetailProps = {
|
||||
segInfo?: Partial<SegmentDetailModel> & { id: string };
|
||||
vectorInfo?: { curr: Array<number[]>; points: Array<number[]> };
|
||||
};
|
||||
|
||||
const HitDetail: FC<IHitDetailProps> = ({ segInfo, vectorInfo }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={"flex flex-row"}>
|
||||
<div className="flex-1 bg-gray-25 p-6">
|
||||
<div className="flex items-center">
|
||||
<SegmentIndexTag
|
||||
positionId={segInfo?.position || ""}
|
||||
className="w-fit mr-6"
|
||||
/>
|
||||
<div className={cn(s.commonIcon, s.typeSquareIcon)} />
|
||||
<span className={cn("mr-6", s.numberInfo)}>
|
||||
{segInfo?.word_count} {t("datasetDocuments.segment.characters")}
|
||||
</span>
|
||||
<div className={cn(s.commonIcon, s.targetIcon)} />
|
||||
<span className={s.numberInfo}>
|
||||
{segInfo?.hit_count} {t("datasetDocuments.segment.hitCount")}
|
||||
</span>
|
||||
</div>
|
||||
<Divider />
|
||||
<div className={s.segModalContent}>{segInfo?.content}</div>
|
||||
<div className={s.keywordTitle}>
|
||||
{t("datasetDocuments.segment.keywords")}
|
||||
</div>
|
||||
<div className={s.keywordWrapper}>
|
||||
{!segInfo?.keywords?.length
|
||||
? "-"
|
||||
: segInfo?.keywords?.map((word: any) => {
|
||||
return <div className={s.keyword}>{word}</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 bg-white p-6">
|
||||
<div className="flex items-center">
|
||||
<div className={cn(s.commonIcon, s.bezierCurveIcon)} />
|
||||
<span className={s.numberInfo}>
|
||||
{t("datasetDocuments.segment.vectorHash")}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={cn(s.numberInfo, "w-[400px] truncate text-gray-700 mt-1")}
|
||||
>
|
||||
{segInfo?.index_node_hash}
|
||||
</div>
|
||||
<ScatterChart data={vectorInfo?.points || []} curr={vectorInfo?.curr || []} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HitDetail;
|
||||
174
web/app/components/datasets/hit-testing/index.tsx
Normal file
174
web/app/components/datasets/hit-testing/index.tsx
Normal file
@@ -0,0 +1,174 @@
|
||||
'use client'
|
||||
import React, { useState, FC, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import { fetchTestingRecords } from '@/service/datasets'
|
||||
import { omit } from 'lodash-es'
|
||||
import Pagination from '@/app/components/base/pagination'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import type { HitTestingResponse, HitTesting } from '@/models/datasets'
|
||||
import cn from 'classnames'
|
||||
import dayjs from 'dayjs'
|
||||
import SegmentCard from '../documents/detail/completed/SegmentCard'
|
||||
import docStyle from '../documents/detail/completed/style.module.css'
|
||||
import Textarea from './textarea'
|
||||
import s from './style.module.css'
|
||||
import HitDetail from './hit-detail'
|
||||
|
||||
const limit = 10;
|
||||
|
||||
type Props = {
|
||||
datasetId: string
|
||||
}
|
||||
|
||||
const RecordsEmpty: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
return <div className='bg-gray-50 rounded-2xl p-5'>
|
||||
<div className={s.clockWrapper}>
|
||||
<div className={cn(s.clockIcon, 'w-5 h-5')}></div>
|
||||
</div>
|
||||
<div className='my-2 text-gray-500 text-sm'>{t('datasetHitTesting.noRecentTip')}</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const [hitResult, setHitResult] = useState<HitTestingResponse | undefined>(); // 初始化记录为空数组
|
||||
const [submitLoading, setSubmitLoading] = useState(false);
|
||||
const [currParagraph, setCurrParagraph] = useState<{ paraInfo?: HitTesting; showModal: boolean }>({ showModal: false })
|
||||
const [text, setText] = useState('');
|
||||
|
||||
const [currPage, setCurrPage] = React.useState<number>(0)
|
||||
const { data: recordsRes, error, mutate: recordsMutate } = useSWR({
|
||||
action: 'fetchTestingRecords',
|
||||
datasetId,
|
||||
params: { limit, page: currPage + 1, }
|
||||
}, apiParams => fetchTestingRecords(omit(apiParams, 'action')))
|
||||
|
||||
const total = recordsRes?.total || 0
|
||||
|
||||
const points = useMemo(() => (hitResult?.records.map((v) => [v.tsne_position.x, v.tsne_position.y]) || []), [hitResult?.records])
|
||||
|
||||
const onClickCard = (detail: HitTesting) => {
|
||||
setCurrParagraph({ paraInfo: detail, showModal: true })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={s.container}>
|
||||
<div className={s.leftDiv}>
|
||||
<div className={s.titleWrapper}>
|
||||
<h1 className={s.title}>{t('datasetHitTesting.title')}</h1>
|
||||
<p className={s.desc}>{t('datasetHitTesting.desc')}</p>
|
||||
</div>
|
||||
<Textarea
|
||||
datasetId={datasetId}
|
||||
setHitResult={setHitResult}
|
||||
onUpdateList={recordsMutate}
|
||||
loading={submitLoading}
|
||||
setLoading={setSubmitLoading}
|
||||
setText={setText}
|
||||
text={text}
|
||||
/>
|
||||
<div className={cn(s.title, 'mt-8 mb-2')}>{t('datasetHitTesting.recents')}</div>
|
||||
{!recordsRes && !error ? (
|
||||
<div className='flex-1'><Loading type='app' /></div>
|
||||
) : recordsRes?.data?.length ? (
|
||||
<>
|
||||
<table className={`w-full border-collapse border-0 mt-3 ${s.table}`}>
|
||||
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold">
|
||||
<tr>
|
||||
<td className='w-28'>{t('datasetHitTesting.table.header.source')}</td>
|
||||
<td>{t('datasetHitTesting.table.header.text')}</td>
|
||||
<td className='w-48'>{t('datasetHitTesting.table.header.time')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-gray-500">
|
||||
{recordsRes?.data?.map((record) => {
|
||||
return <tr
|
||||
key={record.id}
|
||||
className='group border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer'
|
||||
onClick={() => setText(record.content)}
|
||||
>
|
||||
<td className='w-24'>
|
||||
<div className='flex items-center'>
|
||||
<div className={cn(s[`${record.source}_icon`], s.commonIcon, 'mr-1')} />
|
||||
<span className='capitalize'>{record.source.replace('_', ' ')}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className='max-w-xs group-hover:text-primary-600'>{record.content}</td>
|
||||
<td className='w-36'>
|
||||
{dayjs.unix(record.created_at).format(t('datasetHitTesting.dateTimeFormat') as string)}
|
||||
</td>
|
||||
</tr>
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
{(total && total > limit)
|
||||
? <Pagination current={currPage} onChange={setCurrPage} total={total} limit={limit} />
|
||||
: null}
|
||||
</>
|
||||
) : (
|
||||
<RecordsEmpty />
|
||||
)}
|
||||
</div>
|
||||
<div className={s.rightDiv}>
|
||||
{submitLoading ?
|
||||
<div className={s.cardWrapper}>
|
||||
<SegmentCard
|
||||
loading={true}
|
||||
scene='hitTesting'
|
||||
className='h-[216px]'
|
||||
/>
|
||||
<SegmentCard
|
||||
loading={true}
|
||||
scene='hitTesting'
|
||||
className='h-[216px]'
|
||||
/>
|
||||
</div> : !hitResult?.records.length ? (
|
||||
<div className='h-full flex flex-col justify-center items-center'>
|
||||
<div className={cn(docStyle.commonIcon, docStyle.targetIcon, '!bg-gray-200 !h-14 !w-14')} />
|
||||
<div className='text-gray-300 text-[13px] mt-3'>
|
||||
{t('datasetHitTesting.hit.emptyTip')}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className='text-gray-600 font-semibold mb-4'>{t('datasetHitTesting.hit.title')}</div>
|
||||
<div className='overflow-auto flex-1'>
|
||||
<div className={s.cardWrapper}>
|
||||
{hitResult?.records.map((record) => {
|
||||
return <SegmentCard
|
||||
loading={false}
|
||||
detail={record.segment as any}
|
||||
score={record.score}
|
||||
scene='hitTesting'
|
||||
className='h-[216px] mb-4'
|
||||
onClick={() => onClickCard(record as any)}
|
||||
/>
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<Modal
|
||||
className='!max-w-[960px] !p-0'
|
||||
closable
|
||||
onClose={() => setCurrParagraph({ showModal: false })}
|
||||
isShow={currParagraph.showModal}
|
||||
>
|
||||
{currParagraph.showModal && <HitDetail
|
||||
segInfo={currParagraph.paraInfo?.segment}
|
||||
vectorInfo={{
|
||||
curr: [[currParagraph.paraInfo?.tsne_position?.x || 0, currParagraph.paraInfo?.tsne_position.y || 0]],
|
||||
points,
|
||||
}}
|
||||
/>}
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HitTesting
|
||||
65
web/app/components/datasets/hit-testing/style.module.css
Normal file
65
web/app/components/datasets/hit-testing/style.module.css
Normal file
@@ -0,0 +1,65 @@
|
||||
.container {
|
||||
@apply flex h-full w-full relative;
|
||||
}
|
||||
.container > div {
|
||||
@apply flex-1 h-full;
|
||||
}
|
||||
.leftDiv {
|
||||
@apply border-r border-gray-100 px-6 py-3 flex flex-col;
|
||||
}
|
||||
.rightDiv {
|
||||
@apply px-8 pt-[42px] pb-[26px] flex flex-col;
|
||||
}
|
||||
.titleWrapper {
|
||||
@apply flex flex-col justify-center gap-1 mb-5;
|
||||
}
|
||||
.title {
|
||||
@apply text-xl font-medium text-gray-900;
|
||||
}
|
||||
.desc {
|
||||
@apply text-sm font-normal text-gray-500;
|
||||
}
|
||||
.textarea {
|
||||
min-height: 96px;
|
||||
@apply border-none resize-none font-normal caret-primary-600 text-gray-700 text-sm w-full bg-gray-25 focus-visible:outline-none placeholder:text-gray-300 placeholder:text-sm placeholder:font-normal !important;
|
||||
}
|
||||
.table {
|
||||
@apply text-[13px] text-gray-500;
|
||||
}
|
||||
.table td {
|
||||
@apply whitespace-nowrap overflow-hidden text-ellipsis;
|
||||
}
|
||||
.commonIcon {
|
||||
@apply w-3.5 h-3.5 inline-block align-middle;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: contain;
|
||||
}
|
||||
.app_icon {
|
||||
background-image: url(./assets/grid.svg);
|
||||
}
|
||||
.hit_testing_icon {
|
||||
background-image: url(../documents/assets/target.svg);
|
||||
}
|
||||
.plugin_icon {
|
||||
background-image: url(./assets/plugin.svg);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
@apply relative border border-primary-600 min-h-[200px] rounded-xl pt-3 pb-14 px-4 bg-gray-25;
|
||||
}
|
||||
|
||||
.cardWrapper {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(284px, auto));
|
||||
grid-gap: 16px;
|
||||
grid-auto-rows: 216px;
|
||||
}
|
||||
.clockWrapper {
|
||||
border: 0.5px solid #eaecf5;
|
||||
@apply rounded-lg w-11 h-11 flex justify-center items-center;
|
||||
}
|
||||
.clockIcon {
|
||||
mask-image: url(./assets/clock.svg);
|
||||
@apply bg-gray-500;
|
||||
}
|
||||
116
web/app/components/datasets/hit-testing/textarea.tsx
Normal file
116
web/app/components/datasets/hit-testing/textarea.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import type { FC } from "react";
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { DocumentTextIcon } from "@heroicons/react/24/solid";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { hitTesting } from "@/service/datasets";
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
import { HitTestingResponse } from "@/models/datasets";
|
||||
import cn from "classnames";
|
||||
import Button from "../../base/button";
|
||||
import Tag from "../../base/tag";
|
||||
import Tooltip from "../../base/tooltip";
|
||||
import s from "./style.module.css";
|
||||
import { asyncRunSafe } from "@/utils";
|
||||
|
||||
type Props = {
|
||||
datasetId: string;
|
||||
onUpdateList: () => void;
|
||||
setHitResult: (res: HitTestingResponse) => void;
|
||||
loading: boolean;
|
||||
setLoading: (v: boolean) => void;
|
||||
text: string;
|
||||
setText: (v: string) => void;
|
||||
};
|
||||
|
||||
const TextAreaWithButton: FC<Props> = ({
|
||||
datasetId,
|
||||
onUpdateList,
|
||||
setHitResult,
|
||||
setLoading,
|
||||
loading,
|
||||
text,
|
||||
setText,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { indexingTechnique } = useContext(DatasetDetailContext)
|
||||
|
||||
// 处理文本框内容变化的函数
|
||||
function handleTextChange(event: any) {
|
||||
setText(event.target.value);
|
||||
}
|
||||
|
||||
// 处理按钮点击的函数
|
||||
const onSubmit = async () => {
|
||||
setLoading(true);
|
||||
const [e, res] = await asyncRunSafe<HitTestingResponse>(
|
||||
hitTesting({ datasetId, queryText: text }) as Promise<HitTestingResponse>
|
||||
);
|
||||
if (!e) {
|
||||
setHitResult(res);
|
||||
onUpdateList?.();
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={s.wrapper}>
|
||||
<div className="flex items-center mb-3">
|
||||
<DocumentTextIcon className="w-4 h-4 text-primary-600 mr-2" />
|
||||
<span className="text-gray-800 font-semibold text-sm">
|
||||
{t("datasetHitTesting.input.title")}
|
||||
</span>
|
||||
</div>
|
||||
<textarea
|
||||
value={text}
|
||||
onChange={handleTextChange}
|
||||
placeholder={t("datasetHitTesting.input.placeholder") as string}
|
||||
className={s.textarea}
|
||||
/>
|
||||
<div className="absolute inset-x-0 bottom-0 flex items-center justify-between mx-4 mt-2 mb-4">
|
||||
{text?.length > 200 ? (
|
||||
<Tooltip
|
||||
content={t("datasetHitTesting.input.countWarning") as string}
|
||||
selector="hit-testing-warning"
|
||||
>
|
||||
<div>
|
||||
<Tag color="red" className="!text-red-600">
|
||||
{text?.length}
|
||||
<span className="text-red-300 mx-0.5">/</span>
|
||||
200
|
||||
</Tag>
|
||||
</div>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tag
|
||||
color="gray"
|
||||
className={cn("!text-gray-500", text?.length ? "" : "opacity-50")}
|
||||
>
|
||||
{text?.length}
|
||||
<span className="text-gray-300 mx-0.5">/</span>
|
||||
200
|
||||
</Tag>
|
||||
)}
|
||||
<Tooltip
|
||||
selector="hit-testing-submit"
|
||||
disabled={indexingTechnique === 'high_quality'}
|
||||
content={t("datasetHitTesting.input.indexWarning") as string}
|
||||
>
|
||||
<div>
|
||||
<Button
|
||||
onClick={onSubmit}
|
||||
type="primary"
|
||||
loading={loading}
|
||||
disabled={indexingTechnique !== 'high_quality' ? true : (!text?.length || text?.length > 200)}
|
||||
>
|
||||
{t("datasetHitTesting.input.testing")}
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextAreaWithButton;
|
||||
Reference in New Issue
Block a user