|
@@ -1,14 +1,17 @@
|
|
|
'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 {
|
|
|
+ addIntent, addIntentKeyword, delBatchIntentKeyword, editIntentKeyword,
|
|
|
+ editIntentType, fetchIntentKeyword,
|
|
|
+ fetchIntentType, getIntent,
|
|
|
+} 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'
|
|
@@ -19,15 +22,17 @@ 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 [intentName, setIntentName] = useState<string>(transfer.row?.intentName || '')
|
|
|
+ const [intentType, setIntentType] = useState<string>(transfer.row?.intentType || '')
|
|
|
+ const [corpusList, setCorpusList] = useState<any>([])
|
|
|
+ const [corpusFilter, setCorpusFilter] = useState<string>('')
|
|
|
+ const [keywordsList, setKeywordsList] = useState<any>([])
|
|
|
const { data: dataOptionsIntentType }: any = useSWR(
|
|
|
{
|
|
|
- url: '/xxx',
|
|
|
+ url: '/intentions/types',
|
|
|
params: {
|
|
|
page: 1,
|
|
|
limit: 1000,
|
|
@@ -36,109 +41,125 @@ const DetailModal = ({
|
|
|
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',
|
|
|
+ useEffect(() => {
|
|
|
+ if (transfer.row?.id) {
|
|
|
+ getIntent({ url: `/intentions/${transfer.row.id}` }).then((res: any) => {
|
|
|
+ setIntentType(res.type.id)
|
|
|
+ setIntentName(res.name)
|
|
|
+ setCorpusList(res.corpus)
|
|
|
+ setKeywordsList(res.keywords)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+ const [keyword, setKeyword] = useState<string>('')
|
|
|
+ const [keywordFilter, setKeywordFilter] = useState<string>('')
|
|
|
+ const refreshKeywords = async () => {
|
|
|
+ const res = await fetchIntentKeyword({
|
|
|
+ url: `/intentions/${transfer.row.id}/keywords`,
|
|
|
params: {
|
|
|
page: 1,
|
|
|
limit: 1000,
|
|
|
- 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)
|
|
|
+ })
|
|
|
+ setKeywordsList(res)
|
|
|
+ }
|
|
|
+ const handleAddKeyword = async () => {
|
|
|
+ if (!keyword)
|
|
|
return
|
|
|
-
|
|
|
- if (questionList.some((v: any) => v.name === questionRelation)) {
|
|
|
+ if (keywordsList.some((v: any) => v.name === keyword)) {
|
|
|
notify({ type: 'warning', message: '请勿新增重复数据!' })
|
|
|
return
|
|
|
}
|
|
|
- setQuestionList([...questionList, { id: uuid4(), name: questionRelation }])
|
|
|
- setQuestionRelation('')
|
|
|
+ const { id }: any = await addIntentKeyword({
|
|
|
+ url: `/intentions/${transfer.row.id}/keywords`,
|
|
|
+ body: {
|
|
|
+ name: keyword,
|
|
|
+ },
|
|
|
+ })
|
|
|
+ if (id) {
|
|
|
+ await refreshKeywords()
|
|
|
+ setKeyword('')
|
|
|
+ }
|
|
|
}
|
|
|
- const [questionSelectMap, setQuestionSelectMap] = useState<any>(new Map())
|
|
|
- const addQuestionSelectMap = (key: any, value: any) => {
|
|
|
- setQuestionSelectMap((prevMap: any) => {
|
|
|
+ const [keywordSelectMap, setKeywordsSelectMap] = useState<any>(new Map())
|
|
|
+ const addKeywordsSelectMap = (key: any, value: any) => {
|
|
|
+ setKeywordsSelectMap((prevMap: any) => {
|
|
|
const newMap = new Map(prevMap)
|
|
|
newMap.set(key, value)
|
|
|
return newMap
|
|
|
})
|
|
|
}
|
|
|
- const delQuestionSelectMap = (key: any) => {
|
|
|
- setQuestionSelectMap((prevMap: any) => {
|
|
|
+ const delKeywordsSelectMap = (key: any) => {
|
|
|
+ setKeywordsSelectMap((prevMap: any) => {
|
|
|
const newMap = new Map(prevMap)
|
|
|
newMap.delete(key)
|
|
|
return newMap
|
|
|
})
|
|
|
}
|
|
|
+ const [keywordRow, setKeywordRow] = useState<any>({})
|
|
|
+ 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}`,
|
|
|
+ body: {
|
|
|
+ name: editKeyword,
|
|
|
+ intention_id: keywordRow.intention_id,
|
|
|
+ },
|
|
|
+ })
|
|
|
+ if (id) {
|
|
|
+ await refreshKeywords()
|
|
|
+ setShowKeywordEdit(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
|
|
- const [row, setRow] = useState<any>({})
|
|
|
- const handleDelQuestion = async () => {
|
|
|
+ const [delBatch, setDelBatch] = useState(false)
|
|
|
+ const handleDelKeyword = async () => {
|
|
|
try {
|
|
|
- await delCorpusQuestion({
|
|
|
- url: `/xxx/${row.id}`,
|
|
|
- body: {},
|
|
|
+ await delBatchIntentKeyword({
|
|
|
+ url: '/intentions/keywords/batch',
|
|
|
+ body: {
|
|
|
+ method: 'delete',
|
|
|
+ delete_data: delBatch ? Array.from(keywordSelectMap.keys()) : [keywordRow.id],
|
|
|
+ },
|
|
|
})
|
|
|
setShowConfirmDelete(false)
|
|
|
- // mutate()
|
|
|
+ setKeywordsSelectMap(new Map())
|
|
|
+ refreshKeywords()
|
|
|
+ }
|
|
|
+ catch (e) { }
|
|
|
+ }
|
|
|
+ const handleSave = async () => {
|
|
|
+ try {
|
|
|
+ let res
|
|
|
+ if (transfer.mode === 'add') {
|
|
|
+ res = await addIntent({
|
|
|
+ url: '/intentions',
|
|
|
+ body: { type_id: intentType, name: intentName },
|
|
|
+ })
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ res = await editIntentType({
|
|
|
+ url: `/intentions/${transfer.row.id}`,
|
|
|
+ body: { type_id: intentType, name: intentName },
|
|
|
+ })
|
|
|
+ }
|
|
|
+ const { id }: any = res
|
|
|
+ if (id) {
|
|
|
+ if (transfer.mode === 'add')
|
|
|
+ onRefresh(id)
|
|
|
+ else
|
|
|
+ onSend()
|
|
|
+ }
|
|
|
}
|
|
|
catch (e) { }
|
|
|
}
|
|
|
- const [showQuestionEdit, setShowQuestionEdit] = useState(false)
|
|
|
- const [editQuestion, setEditQuestion] = useState<string>('')
|
|
|
- const [corpusRow, setCorpusRow] = useState<any>({})
|
|
|
- const [corpusRowConfig, setCorpusRowConfig] = 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])
|
|
|
return (
|
|
|
<div>
|
|
|
<Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px] w-[800px] max-w-[800px]">
|
|
@@ -156,7 +177,6 @@ const DetailModal = ({
|
|
|
defaultValue={intentType}
|
|
|
onSelect={(i: any) => {
|
|
|
setIntentType(i.value)
|
|
|
- setIntentName('')
|
|
|
}}
|
|
|
items={optionsIntentType}
|
|
|
allowSearch={false}
|
|
@@ -169,9 +189,9 @@ const DetailModal = ({
|
|
|
<div className="flex-1">
|
|
|
<Input
|
|
|
showClearIcon
|
|
|
- value={question}
|
|
|
- onChange={e => setQuestion(e.target.value)}
|
|
|
- onClear={() => setQuestion('')}
|
|
|
+ value={intentName}
|
|
|
+ onChange={e => setIntentName(e.target.value)}
|
|
|
+ onClear={() => setIntentName('')}
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -182,11 +202,11 @@ const DetailModal = ({
|
|
|
<div className="flex-1">
|
|
|
<Input
|
|
|
showClearIcon
|
|
|
- value={questionRelation}
|
|
|
- onChange={e => setQuestionRelation(e.target.value)}
|
|
|
- onClear={() => setQuestionRelation('')}
|
|
|
+ value={keyword}
|
|
|
+ onChange={e => setKeyword(e.target.value)}
|
|
|
+ onClear={() => setKeyword('')}
|
|
|
placeholder='输入后Enter以添加'
|
|
|
- onEnter={handleAddQuestion}
|
|
|
+ onEnter={handleAddKeyword}
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -200,57 +220,60 @@ 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={keywordsList.every((v: any) => keywordSelectMap.has(v.id))}
|
|
|
onCheck={() => {
|
|
|
- questionList.every((v: any) => questionSelectMap.has(v.id))
|
|
|
- ? setQuestionSelectMap(new Map())
|
|
|
- : questionList.forEach((v: any) => addQuestionSelectMap(v.id, v))
|
|
|
+ keywordsList.every((v: any) => keywordSelectMap.has(v.id))
|
|
|
+ ? setKeywordsSelectMap(new Map())
|
|
|
+ : keywordsList.forEach((v: any) => addKeywordsSelectMap(v.id, v))
|
|
|
}}
|
|
|
- disabled={questionList.length === 0}
|
|
|
+ disabled={keywordsList.length === 0}
|
|
|
/>
|
|
|
全选
|
|
|
</div>
|
|
|
<div className="ml-auto w-[200px]">
|
|
|
<Input
|
|
|
showClearIcon
|
|
|
- value={questionFilter}
|
|
|
- onChange={e => setQuestionFilter(e.target.value)}
|
|
|
- onClear={() => setQuestionFilter('')}
|
|
|
- placeholder='请输入相似问题名称进行过滤'
|
|
|
+ value={keywordFilter}
|
|
|
+ onChange={e => setKeywordFilter(e.target.value)}
|
|
|
+ onClear={() => setKeywordFilter('')}
|
|
|
+ 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-[150px] 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) => (
|
|
|
+ keywordsList.filter((v: any) => !keywordFilter || v.name.includes(keywordFilter)).map((item: any) => (
|
|
|
<div key={item.id} className="flex items-center">
|
|
|
<Checkbox
|
|
|
className='mr-2 shrink-0'
|
|
|
- checked={questionSelectMap.has(item.id)}
|
|
|
+ checked={keywordSelectMap.has(item.id)}
|
|
|
onCheck={() => {
|
|
|
- questionSelectMap.has(item.id)
|
|
|
- ? delQuestionSelectMap(item.id)
|
|
|
- : addQuestionSelectMap(item.id, item)
|
|
|
+ keywordSelectMap.has(item.id)
|
|
|
+ ? delKeywordsSelectMap(item.id)
|
|
|
+ : addKeywordsSelectMap(item.id, item)
|
|
|
}}
|
|
|
- disabled={questionList.length === 0}
|
|
|
+ disabled={keywordsList.length === 0}
|
|
|
/>
|
|
|
<div className="flex-1">
|
|
|
{item.name}
|
|
|
</div>
|
|
|
<Button variant='ghost-accent' size='small' className={cn('shrink-0')}
|
|
|
onClick={() => {
|
|
|
- setRow(item)
|
|
|
- setEditQuestion(item.name)
|
|
|
- setShowQuestionEdit(true)
|
|
|
+ setKeywordRow(item)
|
|
|
+ setEditKeyword(item.name)
|
|
|
+ setShowKeywordEdit(true)
|
|
|
}}>
|
|
|
编辑
|
|
|
</Button>
|
|
|
<Button variant='ghost' size='small' className={cn('shrink-0 text-red-600')}
|
|
|
onClick={() => {
|
|
|
- setRow(item)
|
|
|
+ setKeywordRow(item)
|
|
|
setShowConfirmDelete(true)
|
|
|
}}>
|
|
|
刪除
|
|
@@ -260,17 +283,49 @@ 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>共{keywordsList.length}条</div>
|
|
|
+ <div className="ml-4">已选择{keywordSelectMap.size}条</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ {
|
|
|
+ transfer.mode === 'edit' && (
|
|
|
+ <div className="mt-3 flex flex-col">
|
|
|
+ <div className="flex h-10 w-full items-center gap-2 border-2 border-[#F6F8FC] bg-[#F6F8FC] px-2">
|
|
|
+ <div>语料列表</div>
|
|
|
+ <div className="ml-auto w-[200px]">
|
|
|
+ <Input
|
|
|
+ showClearIcon
|
|
|
+ value={corpusFilter}
|
|
|
+ onChange={e => setCorpusFilter(e.target.value)}
|
|
|
+ onClear={() => setCorpusFilter('')}
|
|
|
+ placeholder='请输入语料名称进行过滤'
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex h-[150px] flex-col gap-2 overflow-y-auto border-2 border-solid border-[#F6F8FC] p-2">
|
|
|
+ {
|
|
|
+ corpusList.filter((v: any) => !corpusFilter || v.name.includes(corpusFilter)).map((item: any) => (
|
|
|
+ <div key={item.id} className="flex items-center">
|
|
|
+ <div className="flex-1">
|
|
|
+ {item.name}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ <div className="flex border-2 border-t-0 border-solid border-[#F6F8FC] p-2 text-xs">
|
|
|
+ <div>共{corpusList.length}条</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
|
}
|
|
|
<Button
|
|
|
tabIndex={0}
|
|
|
- className='w-full'
|
|
|
+ className='mt-2 w-full'
|
|
|
onClick={handleSave}
|
|
|
- disabled={!question.length || !intentType.length || !intentName.length}
|
|
|
+ disabled={!intentType.length || !intentName.length}
|
|
|
variant='primary'
|
|
|
>
|
|
|
保存
|
|
@@ -278,11 +333,11 @@ const DetailModal = ({
|
|
|
</div>
|
|
|
</Modal>
|
|
|
{
|
|
|
- showQuestionEdit && (
|
|
|
+ showKeywordEdit && (
|
|
|
<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={() => setShowKeywordEdit(false)} />
|
|
|
</div>
|
|
|
<div>
|
|
|
<div className={cn('flex flex-wrap items-center justify-between py-4')}>
|
|
@@ -290,8 +345,8 @@ const DetailModal = ({
|
|
|
关键词
|
|
|
</div>
|
|
|
<Input
|
|
|
- value={editQuestion}
|
|
|
- onChange={e => setEditQuestion(e.target.value)}
|
|
|
+ value={editKeyword}
|
|
|
+ onChange={e => setEditKeyword(e.target.value)}
|
|
|
className='h-9'
|
|
|
placeholder='请输入关键词'
|
|
|
/>
|
|
@@ -299,8 +354,8 @@ const DetailModal = ({
|
|
|
<Button
|
|
|
tabIndex={0}
|
|
|
className='w-full'
|
|
|
- onClick={handleSaveQuestion}
|
|
|
- disabled={!editQuestion.length}
|
|
|
+ onClick={handleSaveKeyword}
|
|
|
+ disabled={!editKeyword.length}
|
|
|
variant='primary'
|
|
|
>
|
|
|
保存
|
|
@@ -312,9 +367,9 @@ const DetailModal = ({
|
|
|
{showConfirmDelete && (
|
|
|
<Confirm
|
|
|
title="删除确认"
|
|
|
- content={`请确认是否删除${row.name}?`}
|
|
|
+ content={`请确认是否删除${delBatch ? `${keywordSelectMap.size}条关键词` : keywordRow.name}?`}
|
|
|
isShow={showConfirmDelete}
|
|
|
- onConfirm={handleDelQuestion}
|
|
|
+ onConfirm={handleDelKeyword}
|
|
|
onCancel={() => setShowConfirmDelete(false)}
|
|
|
/>
|
|
|
)}
|