Browse Source

训练语料

CzRger 2 months ago
parent
commit
e0f1e209ec

+ 247 - 193
web/app/components/skill/corpus/detail-modal.tsx

@@ -1,169 +1,191 @@
 'use client'
-import React, { useCallback, useState } from 'react'
+import React, { useEffect, useState } from 'react'
 import { RiCloseLine } from '@remixicon/react'
 import Modal from '@/app/components/base/modal'
 import Button from '@/app/components/base/button'
-import { delCorpusQuestion, fetchIntent, fetchIntentType } from '@/service/common'
+import {
+  addCorpus, addCorpusQuestion, delBatchCorpusQuestion, editCorpus as editCorpusFunc, editCorpusQuestion, fetchCorpusQuestion,
+  fetchIntent,
+  getCorpus,
+} from '@/service/common'
 import 'react-multi-email/dist/style.css'
 import Input from '@/app/components/base/input'
-import { SimpleSelect } from '@/app/components/base/select'
-import useSWR from 'swr'
-import { v4 as uuid4 } from 'uuid'
 import Checkbox from '@/app/components/base/checkbox'
 import cn from '@/utils/classnames'
 import { useContext } from 'use-context-selector'
 import { ToastContext } from '@/app/components/base/toast'
 import Confirm from '@/app/components/base/confirm'
-import Textarea from '@/app/components/base/textarea'
+import { Cascader as AntdCascader } from 'antd'
+import { Textarea } from '@/app/components/base/textarea'
 
 const DetailModal = ({
   transfer,
   onCancel,
   onSend,
+  onRefresh,
 }: any) => {
   const { notify } = useContext(ToastContext)
-  const [questionRelation, setQuestionRelation] = useState<string>('')
-  const [questionFilter, setQuestionFilter] = useState<string>('') // the input value
-  const [question, setQuestion] = useState<string>(transfer.row?.question || '') // the input value
-  const [intentType, setIntentType] = useState<string>(transfer.row?.intentType || '') // the input value
-  const { data: dataOptionsIntentType }: any = useSWR(
-    {
-      url: '/xxx',
+  const [intentCascader, setIntentCascader] = useState<any>([])
+  useEffect(() => {
+    fetchIntent({
+      url: '/intentions',
       params: {
         page: 1,
         limit: 99999,
       },
-    },
-    fetchIntentType,
-  )
-  const optionsIntentType: any = dataOptionsIntentType?.data.map((v: any) => ({ name: v.name, value: v.id })) || []
-  const [intentName, setIntentName] = useState<string>(transfer.row?.intentName || '') // the input value
-  const { data: dataOptionsIntentName }: any = useSWR(
-    {
-      url: '/xxx',
+    }).then((res: any) => {
+      const map = new Map()
+      res.data.forEach((v: any) => {
+        if (map.has(v.type_id)) {
+          const parent = map.get(v.type_id)
+          parent.children.push(v)
+          map.set(v.type_id, parent)
+        }
+        else {
+          map.set(v.type_id, {
+            id: v.type_id,
+            name: v.type_name,
+            children: [v],
+          })
+        }
+      })
+      setIntentCascader(Array.from(map.values()))
+    })
+  }, [])
+  const [question, setQuestion] = useState<string>('')
+  const [intentValue, setIntentValue] = useState<any>([])
+  const [questionCorpus, setQuestionCorpus] = useState<any>('')
+  const [similarityList, setSimilarityList] = useState<any>([])
+  const handleSave = async () => {
+    try {
+      let res
+      if (transfer.mode === 'add') {
+        res = await addCorpus({
+          url: '/intentions/corpus',
+          body: { question, intention_id: intentValue[intentValue.length - 1] },
+        })
+      }
+      else {
+        res = await editCorpusFunc({
+          url: `/intentions/corpus/${transfer.row.id}`,
+          body: { question, intention_id: intentValue[intentValue.length - 1], question_config: questionCorpus },
+        })
+      }
+      const { id }: any = res
+      if (id) {
+        if (transfer.mode === 'add')
+          onRefresh(id)
+        else
+          onSend()
+      }
+    }
+    catch (e) { }
+  }
+  useEffect(() => {
+    if (transfer.row?.id) {
+      getCorpus({ url: `/intentions/corpus/${transfer.row.id}` }).then((res: any) => {
+        setQuestion(res.question)
+        setIntentValue([res.intention.type_id, res.intention.id])
+        setQuestionCorpus(res.question_config || '')
+        setSimilarityList(res.similarity_questions)
+      })
+    }
+  }, [])
+  const [similarityQuestion, setSimilarityQuestion] = useState<string>('')
+  const [similarityFilter, setSimilarityFilter] = useState<string>('')
+  const refreshSimilarity = async () => {
+    const res = await fetchCorpusQuestion({
+      url: `/intentions/corpus/${transfer.row.id}/similarity_questions`,
       params: {
         page: 1,
         limit: 99999,
-        intentType,
       },
-    },
-    fetchIntent,
-  )
-  const optionsIntentName: any = dataOptionsIntentName?.data.map((v: any) => ({ name: v.name, value: v.id })) || []
-  const [questionList, setQuestionList] = useState<any>([{ id: uuid4(), name: '啊啊啊啊啊啊啊啊啊啊' }, { id: uuid4(), name: '啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊' }])
-  const handleAddQuestion = () => {
-    if (!questionRelation)
+    })
+    setSimilarityList(res)
+  }
+  const handleAddSimilarity = async () => {
+    if (!similarityQuestion)
       return
-
-    if (questionList.some((v: any) => v.name === questionRelation)) {
+    if (similarityList.some((v: any) => v.question === similarityQuestion)) {
       notify({ type: 'warning', message: '请勿新增重复数据!' })
       return
     }
-    setQuestionList([...questionList, { id: uuid4(), name: questionRelation }])
-    setQuestionRelation('')
+    const { id }: any = await addCorpusQuestion({
+      url: `/intentions/corpus/${transfer.row.id}/similarity_questions`,
+      body: {
+        question: similarityQuestion,
+      },
+    })
+    if (id) {
+      await refreshSimilarity()
+      setSimilarityQuestion('')
+    }
   }
-  const [questionSelectMap, setQuestionSelectMap] = useState<any>(new Map())
-  const addQuestionSelectMap = (key: any, value: any) => {
-    setQuestionSelectMap((prevMap: any) => {
+  const [similaritySelectMap, setSimilaritySelectMap] = useState<any>(new Map())
+  const addSimilaritySelectMap = (key: any, value: any) => {
+    setSimilaritySelectMap((prevMap: any) => {
       const newMap = new Map(prevMap)
       newMap.set(key, value)
       return newMap
     })
   }
-  const delQuestionSelectMap = (key: any) => {
-    setQuestionSelectMap((prevMap: any) => {
+  const delSimilaritySelectMap = (key: any) => {
+    setSimilaritySelectMap((prevMap: any) => {
       const newMap = new Map(prevMap)
       newMap.delete(key)
       return newMap
     })
   }
+  const [similarityRow, setSimilarityRow] = useState<any>({})
+  const [showSimilarityEdit, setShowSimilarityEdit] = useState(false)
+  const [editSimilarity, setEditSimilarity] = useState<string>('')
+  const [showSimilarityCorpus, setShowSimilarityCorpus] = useState(false)
+  const [editSimilarityCorpus, setEditSimilarityCorpus] = useState('')
+  const handleSaveSimilarity = async () => {
+    if (similarityList.some((v: any) => v.name === editSimilarity)) {
+      notify({ type: 'warning', message: '请勿新增重复数据!' })
+      return
+    }
+    const params: any = {
+      corpus_id: similarityRow.corpus_id,
+    }
+    if (showSimilarityEdit) {
+      params.question = editSimilarity
+    }
+    else if (showSimilarityCorpus) {
+      params.question = similarityRow.question
+      params.question_config = editSimilarityCorpus
+    }
+
+    const { id }: any = await editCorpusQuestion({
+      url: `/intentions/similarity_questions/${similarityRow.id}`,
+      body: params,
+    })
+    if (id) {
+      await refreshSimilarity()
+      setShowSimilarityEdit(false)
+      setShowSimilarityCorpus(false)
+    }
+  }
   const [showConfirmDelete, setShowConfirmDelete] = useState(false)
-  const [row, setRow] = useState<any>({})
-  const handleDelQuestion = async () => {
+  const [delBatch, setDelBatch] = useState(false)
+  const handleDelSimilarity = async () => {
     try {
-      await delCorpusQuestion({
-        url: `/xxx/${row.id}`,
-        body: {},
+      await delBatchCorpusQuestion({
+        url: '/intentions/similarity_questions/batch',
+        body: {
+          method: 'delete',
+          data: delBatch ? Array.from(similaritySelectMap.keys()) : [similarityRow.id],
+        },
       })
       setShowConfirmDelete(false)
-      // mutate()
+      setSimilaritySelectMap(new Map())
+      refreshSimilarity()
     }
     catch (e) { }
   }
-  const [showQuestionEdit, setShowQuestionEdit] = useState(false)
-  const [editQuestion, setEditQuestion] = useState<string>('')
   const [showCorpus, setShowCorpus] = useState(false)
-  const [corpusRow, setCorpusRow] = useState<any>({})
-  const [corpusInput, setCorpusInput] = useState<string>('')
-  const handleSave = useCallback(async () => {
-    // try {
-    //   let res
-    //   if (transfer.mode === 'add') {
-    //     res = await addCorpus({
-    //       url: '/xxx',
-    //       body: { name, type: 'knowledge_category' },
-    //     })
-    //   }
-    //   else {
-    //     res = await editCorpus({
-    //       url: '/xxx',
-    //       body: { name },
-    //     })
-    //   }
-    //   const { id }: any = res
-    //   if (id) {
-    //     onCancel()
-    //     onSend()
-    //   }
-    // }
-    // catch (e) { }
-  }, [name, onCancel, onSend, transfer])
-  const handleSaveQuestion = useCallback(async () => {
-    // try {
-    //   let res
-    //   if (transfer.mode === 'add') {
-    //     res = await addCorpus({
-    //       url: '/xxx',
-    //       body: { name, type: 'knowledge_category' },
-    //     })
-    //   }
-    //   else {
-    //     res = await editCorpus({
-    //       url: '/xxx',
-    //       body: { name },
-    //     })
-    //   }
-    //   const { id }: any = res
-    //   if (id) {
-    //     onCancel()
-    //     onSend()
-    //   }
-    // }
-    // catch (e) { }
-  }, [name, onCancel, onSend, transfer])
-  const handleSaveCorpus = useCallback(async () => {
-    // try {
-    //   let res
-    //   if (transfer.mode === 'add') {
-    //     res = await addCorpus({
-    //       url: '/xxx',
-    //       body: { name, type: 'knowledge_category' },
-    //     })
-    //   }
-    //   else {
-    //     res = await editCorpus({
-    //       url: '/xxx',
-    //       body: { name },
-    //     })
-    //   }
-    //   const { id }: any = res
-    //   if (id) {
-    //     onCancel()
-    //     onSend()
-    //   }
-    // }
-    // catch (e) { }
-  }, [name, onCancel, onSend, transfer])
+  const [editCorpus, setEditCorpus] = useState('')
   return (
     <div>
       <Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px] w-[800px] max-w-[800px]">
@@ -186,11 +208,7 @@ const DetailModal = ({
               {
                 transfer.mode === 'edit' && (
                   <Button variant='ghost-accent' size='small' className={cn('shrink-0')} onClick={() => {
-                    setRow({
-                      type: 'main',
-                      name: question,
-                    })
-                    setCorpusInput(question)
+                    setEditCorpus(questionCorpus)
                     setShowCorpus(true)
                   }}>
                     语料配置
@@ -201,27 +219,16 @@ const DetailModal = ({
             <div className="flex w-full items-center">
               <div className="w-[80px]">意图名称</div>
               <div className="flex flex-1">
-                <SimpleSelect
-                  className="h-[32px] w-[200px]"
-                  defaultValue={intentType}
-                  onSelect={(i: any) => {
-                    setIntentType(i.value)
-                    setIntentName('')
-                  }}
-                  items={optionsIntentType}
-                  allowSearch={false}
-                  placeholder="请选择意图类型"
-                />
-                <SimpleSelect
-                  className="h-[32px] w-[200px]"
-                  defaultValue={intentName}
-                  onSelect={(i: any) => {
-                    setIntentName(i.value)
+                <AntdCascader
+                  value={intentValue}
+                  className="h-[32px] w-full"
+                  options={intentCascader}
+                  onChange={(val: any) => {
+                    setIntentValue(val)
                   }}
-                  items={optionsIntentName}
-                  allowSearch={false}
-                  placeholder="请选择意图名称"
-                  disabled={!intentType}
+                  placeholder="请选择"
+                  fieldNames={{ label: 'name', value: 'id' }}
+                  showSearch={true}
                 />
               </div>
             </div>
@@ -232,11 +239,11 @@ const DetailModal = ({
                   <div className="flex-1">
                     <Input
                       showClearIcon
-                      value={questionRelation}
-                      onChange={e => setQuestionRelation(e.target.value)}
-                      onClear={() => setQuestionRelation('')}
+                      value={similarityQuestion}
+                      onChange={e => setSimilarityQuestion(e.target.value)}
+                      onClear={() => setSimilarityQuestion('')}
                       placeholder='输入后Enter以添加'
-                      onEnter={handleAddQuestion}
+                      onEnter={handleAddSimilarity}
                     />
                   </div>
                 </div>
@@ -250,64 +257,67 @@ const DetailModal = ({
                   <div className='flex items-center' onClick={e => e.stopPropagation()}>
                     <Checkbox
                       className='mr-2 shrink-0'
-                      checked={questionList.every((v: any) => questionSelectMap.has(v.id))}
+                      checked={similarityList.every((v: any) => similaritySelectMap.has(v.id))}
                       onCheck={() => {
-                        questionList.every((v: any) => questionSelectMap.has(v.id))
-                          ? setQuestionSelectMap(new Map())
-                          : questionList.forEach((v: any) => addQuestionSelectMap(v.id, v))
+                        similarityList.every((v: any) => similaritySelectMap.has(v.id))
+                          ? setSimilaritySelectMap(new Map())
+                          : similarityList.forEach((v: any) => addSimilaritySelectMap(v.id, v))
                       }}
-                      disabled={questionList.length === 0}
+                      disabled={similarityList.length === 0}
                     />
                     全选
                   </div>
                   <div className="ml-auto w-[200px]">
                     <Input
                       showClearIcon
-                      value={questionFilter}
-                      onChange={e => setQuestionFilter(e.target.value)}
-                      onClear={() => setQuestionFilter('')}
+                      value={similarityFilter}
+                      onChange={e => setSimilarityFilter(e.target.value)}
+                      onClear={() => setSimilarityFilter('')}
                       placeholder='请输入相似问题名称进行过滤'
                     />
                   </div>
-                  <Button variant='primary' className={cn('shrink-0')}>
+                  <Button variant='primary' className={cn('shrink-0')} onClick={() => {
+                    setDelBatch(true)
+                    setShowConfirmDelete(true)
+                  }}>
                     批量删除
                   </Button>
                 </div>
                 <div className="flex h-[300px] flex-col gap-2 overflow-y-auto border-2 border-solid border-[#F6F8FC] p-2">
                   {
-                    questionList.filter((v: any) => !questionFilter || v.name.includes(questionFilter)).map((item: any) => (
+                    similarityList.filter((v: any) => !similarityFilter || v.question.includes(similarityFilter)).map((item: any) => (
                       <div key={item.id} className="flex items-center">
                         <Checkbox
                           className='mr-2 shrink-0'
-                          checked={questionSelectMap.has(item.id)}
+                          checked={similaritySelectMap.has(item.id)}
                           onCheck={() => {
-                            questionSelectMap.has(item.id)
-                              ? delQuestionSelectMap(item.id)
-                              : addQuestionSelectMap(item.id, item)
+                            similaritySelectMap.has(item.id)
+                              ? delSimilaritySelectMap(item.id)
+                              : addSimilaritySelectMap(item.id, item)
                           }}
-                          disabled={questionList.length === 0}
+                          disabled={similarityList.length === 0}
                         />
                         <div className="flex-1">
-                          {item.name}
+                          {item.question}
                         </div>
                         <Button variant='ghost-accent' size='small' className={cn('shrink-0')} onClick={() => {
-                          setRow(item)
-                          setCorpusInput(item.name)
-                          setShowCorpus(true)
+                          setSimilarityRow(item)
+                          setEditSimilarityCorpus(item.question_config || '')
+                          setShowSimilarityCorpus(true)
                         }}>
                           语料配置
                         </Button>
                         <Button variant='ghost-accent' size='small' className={cn('shrink-0')}
                           onClick={() => {
-                            setRow(item)
-                            setEditQuestion(item.name)
-                            setShowQuestionEdit(true)
+                            setSimilarityRow(item)
+                            setEditSimilarity(item.question)
+                            setShowSimilarityEdit(true)
                           }}>
                           编辑
                         </Button>
                         <Button variant='ghost' size='small' className={cn('shrink-0 text-red-600')}
                           onClick={() => {
-                            setRow(item)
+                            setSimilarityRow(item)
                             setShowConfirmDelete(true)
                           }}>
                           刪除
@@ -317,8 +327,8 @@ const DetailModal = ({
                   }
                 </div>
                 <div className="flex border-2 border-t-0 border-solid border-[#F6F8FC] p-2 text-xs">
-                  <div>共{questionList.length}条</div>
-                  <div className="ml-4">已选择{questionSelectMap.size}条</div>
+                  <div>共{similarityList.length}条</div>
+                  <div className="ml-4">已选择{similaritySelectMap.size}条</div>
                 </div>
               </div>
             )
@@ -327,7 +337,7 @@ const DetailModal = ({
             tabIndex={0}
             className='mt-4 w-full'
             onClick={handleSave}
-            disabled={!question.length || !intentType.length || !intentName.length}
+            disabled={!question.length || !intentValue.length}
             variant='primary'
           >
             保存
@@ -335,11 +345,11 @@ const DetailModal = ({
         </div>
       </Modal>
       {
-        showQuestionEdit && (
+        showSimilarityEdit && (
           <Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px] w-[400px]">
             <div className='mb-2 flex justify-between'>
               <div className='text-xl font-semibold text-text-primary'>编辑相似问题</div>
-              <RiCloseLine className='h-4 w-4 cursor-pointer text-text-tertiary' onClick={() => setShowQuestionEdit(false)} />
+              <RiCloseLine className='h-4 w-4 cursor-pointer text-text-tertiary' onClick={() => setShowSimilarityEdit(false)} />
             </div>
             <div>
               <div className={cn('flex flex-wrap items-center justify-between py-4')}>
@@ -347,8 +357,8 @@ const DetailModal = ({
                   相似问题
                 </div>
                 <Input
-                  value={editQuestion}
-                  onChange={e => setEditQuestion(e.target.value)}
+                  value={editSimilarity}
+                  onChange={e => setEditSimilarity(e.target.value)}
                   className='h-9'
                   placeholder='请输入相似问题'
                 />
@@ -356,8 +366,8 @@ const DetailModal = ({
               <Button
                 tabIndex={0}
                 className='w-full'
-                onClick={handleSaveQuestion}
-                disabled={!editQuestion.length}
+                onClick={handleSaveSimilarity}
+                disabled={!editSimilarity.length}
                 variant='primary'
               >
                 保存
@@ -366,6 +376,15 @@ const DetailModal = ({
           </Modal>
         )
       }
+      {showConfirmDelete && (
+        <Confirm
+          title="删除确认"
+          content={`请确认是否删除${delBatch ? `${similaritySelectMap.size}条相似问题` : similarityRow.question}?`}
+          isShow={showConfirmDelete}
+          onConfirm={handleDelSimilarity}
+          onCancel={() => setShowConfirmDelete(false)}
+        />
+      )}
       {
         showCorpus && (
           <Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px]  max-w-[800px]">
@@ -376,11 +395,55 @@ const DetailModal = ({
             <div>
               <div className={cn('flex flex-wrap items-center justify-between py-4')}>
                 <div className='shrink-0 py-2 text-sm font-medium leading-[20px] text-text-primary'>
-                  当前标注问题:{row.name}
+                  当前标注问题:{question}
+                </div>
+                <Textarea
+                  value={editCorpus}
+                  onChange={e => setEditCorpus(e.target.value)}
+                  className='resize-none'
+                  placeholder='请输入语料配置'
+                  rows={30}
+                />
+              </div>
+              <div className="flex gap-2">
+                <Button
+                  className='w-full'
+                  onClick={() => setEditCorpus(questionCorpus)}
+                  variant='warning'
+                >
+                  重置
+                </Button>
+                <Button
+                  className='w-full'
+                  onClick={() => {
+                    setQuestionCorpus(editCorpus)
+                    setShowCorpus(false)
+                  }}
+                  disabled={!editCorpus.length}
+                  variant='primary'
+                >
+                  保存
+                </Button>
+              </div>
+            </div>
+          </Modal>
+        )
+      }
+      {
+        showSimilarityCorpus && (
+          <Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px]  max-w-[800px]">
+            <div className='mb-2 flex justify-between'>
+              <div className='text-xl font-semibold text-text-primary'>训练语料配置</div>
+              <RiCloseLine className='h-4 w-4 cursor-pointer text-text-tertiary' onClick={() => setShowSimilarityCorpus(false)} />
+            </div>
+            <div>
+              <div className={cn('flex flex-wrap items-center justify-between py-4')}>
+                <div className='shrink-0 py-2 text-sm font-medium leading-[20px] text-text-primary'>
+                  当前标注问题:{similarityRow.question}
                 </div>
                 <Textarea
-                  value={corpusInput}
-                  onChange={e => setCorpusInput(e.target.value)}
+                  value={editSimilarityCorpus}
+                  onChange={e => setEditSimilarityCorpus(e.target.value)}
                   className='resize-none'
                   placeholder='请输入语料配置'
                   rows={30}
@@ -389,15 +452,15 @@ const DetailModal = ({
               <div className="flex gap-2">
                 <Button
                   className='w-full'
-                  onClick={() => setCorpusInput(row.name)}
+                  onClick={() => setEditSimilarityCorpus(similarityRow.question_config || '')}
                   variant='warning'
                 >
                   重置
                 </Button>
                 <Button
                   className='w-full'
-                  onClick={handleSaveCorpus}
-                  disabled={!corpusInput.length}
+                  onClick={handleSaveSimilarity}
+                  disabled={!editSimilarityCorpus.length}
                   variant='primary'
                 >
                   保存
@@ -407,15 +470,6 @@ const DetailModal = ({
           </Modal>
         )
       }
-      {showConfirmDelete && (
-        <Confirm
-          title="删除确认"
-          content={`请确认是否删除${row.name}?`}
-          isShow={showConfirmDelete}
-          onConfirm={handleDelQuestion}
-          onCancel={() => setShowConfirmDelete(false)}
-        />
-      )}
     </div>
   )
 }

+ 0 - 1
web/app/components/skill/corpus/index.tsx

@@ -47,7 +47,6 @@ const CorpusIndex = () => {
   const [list, setList] = useState<any>([])
   const [total, setTotal] = useState(0)
   const handlePage = () => {
-    console.log(intentName)
     const params: any = {
       page: page + 1,
       limit,

+ 6 - 3
web/app/components/skill/corpus/list.tsx

@@ -28,7 +28,7 @@ const CorpusPageList: FC<PageListProps> = ({
   const handleDel = async () => {
     try {
       await delCorpus({
-        url: `/tags/${row.id}`,
+        url: `/intentions/corpus/${row.id}`,
         body: {},
       })
       setShowConfirmDelete(false)
@@ -101,7 +101,7 @@ const CorpusPageList: FC<PageListProps> = ({
       {showConfirmDelete && (
         <Confirm
           title="删除确认"
-          content={`请确认是否删除${row.name}?`}
+          content={`请确认是否删除${row.question}?`}
           isShow={showConfirmDelete}
           onConfirm={handleDel}
           onCancel={() => setShowConfirmDelete(false)}
@@ -111,7 +111,10 @@ const CorpusPageList: FC<PageListProps> = ({
         detailModalVisible && (
           <DetailModal
             transfer={transfer}
-            onCancel={() => setDetailModalVisible(false)}
+            onCancel={() => {
+              setDetailModalVisible(false)
+              onUpdate()
+            }}
             onSend={() => {
               setDetailModalVisible(false)
               onUpdate()

+ 33 - 3
web/app/components/skill/intent/detail-modal.tsx

@@ -16,6 +16,7 @@ import cn from '@/utils/classnames'
 import { useContext } from 'use-context-selector'
 import { ToastContext } from '@/app/components/base/toast'
 import Confirm from '@/app/components/base/confirm'
+import { Textarea } from '@/app/components/base/textarea'
 
 const DetailModal = ({
   transfer,
@@ -99,14 +100,12 @@ const DetailModal = ({
   const [showKeywordEdit, setShowKeywordEdit] = useState(false)
   const [editKeyword, setEditKeyword] = useState<string>('')
   const handleSaveKeyword = async () => {
-    if (!editKeyword)
-      return
     if (keywordsList.some((v: any) => v.name === editKeyword)) {
       notify({ type: 'warning', message: '请勿新增重复数据!' })
       return
     }
     const { id }: any = await editIntentKeyword({
-      url: `intentions/keywords/${keywordRow.id}`,
+      url: `/intentions/keywords/${keywordRow.id}`,
       body: {
         name: editKeyword,
         intention_id: keywordRow.intention_id,
@@ -159,6 +158,8 @@ const DetailModal = ({
     }
     catch (e) { }
   }
+  const [similarityRow, setSimilarityRow] = useState<any>({})
+  const [showSimilarityCorpus, setShowSimilarityCorpus] = useState(false)
   return (
     <div>
       <Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px] w-[800px] max-w-[800px]">
@@ -310,6 +311,12 @@ const DetailModal = ({
                         <div className="flex-1">
                           {item.question}
                         </div>
+                        <Button variant='ghost-accent' size='small' className={cn('shrink-0')} onClick={() => {
+                          setSimilarityRow(item)
+                          setShowSimilarityCorpus(true)
+                        }}>
+                          语料配置
+                        </Button>
                       </div>
                     ))
                   }
@@ -372,6 +379,29 @@ const DetailModal = ({
           onCancel={() => setShowConfirmDelete(false)}
         />
       )}
+      {
+        showSimilarityCorpus && (
+          <Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px]  max-w-[800px]">
+            <div className='mb-2 flex justify-between'>
+              <div className='text-xl font-semibold text-text-primary'>训练语料配置</div>
+              <RiCloseLine className='h-4 w-4 cursor-pointer text-text-tertiary' onClick={() => setShowSimilarityCorpus(false)} />
+            </div>
+            <div>
+              <div className={cn('flex flex-wrap items-center justify-between py-4')}>
+                <div className='shrink-0 py-2 text-sm font-medium leading-[20px] text-text-primary'>
+                  当前标注问题:{similarityRow.question}
+                </div>
+                <Textarea
+                  value={similarityRow.question_config || ''}
+                  className='resize-none'
+                  rows={30}
+                  disabled={true}
+                />
+              </div>
+            </div>
+          </Modal>
+        )
+      }
     </div>
   )
 }

+ 14 - 3
web/service/common.ts

@@ -528,6 +528,7 @@ export const delBatchIntentKeyword = ({ url, body }: any) => {
   console.log('批量删除意图关键词', url, body)
   return post(url, { body })
 }
+
 export const fetchCorpus = ({ url, params }: any) => {
   console.log('查询训练语料列表', url, params)
   return get(url, { params })
@@ -548,6 +549,16 @@ export const delCorpus = ({ url, body }: any) => {
   return del(url, { body })
 }
 
+export const getCorpus = ({ url, params }: any) => {
+  console.log('查询训练语料', url, params)
+  return get(url, { params })
+}
+
+export const fetchCorpusQuestion = ({ url, params }: any) => {
+  console.log('查询训练语料-相似问题', url, params)
+  return get(url, { params })
+}
+
 export const addCorpusQuestion = ({ url, body }: any) => {
   console.log('新增训练语料-相似问题', url, body)
   return post(url, { body })
@@ -558,9 +569,9 @@ export const editCorpusQuestion = ({ url, body }: any) => {
   return patch(url, { body })
 }
 
-export const delCorpusQuestion = ({ url, body }: any) => {
-  console.log('删除训练语料-相似问题', url, body)
-  return del(url, { body })
+export const delBatchCorpusQuestion = ({ url, body }: any) => {
+  console.log('批量删除训练语料-相似问题', url, body)
+  return post(url, { body })
 }
 
 export const fetchDepts = ({ url, params }: any) => {