detail-modal.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. 'use client'
  2. import React, { useCallback, useState } from 'react'
  3. import { RiCloseLine } from '@remixicon/react'
  4. import Modal from '@/app/components/base/modal'
  5. import Button from '@/app/components/base/button'
  6. import { delCorpusQuestion, fetchIntent, fetchIntentType } from '@/service/common'
  7. import 'react-multi-email/dist/style.css'
  8. import Input from '@/app/components/base/input'
  9. import { SimpleSelect } from '@/app/components/base/select'
  10. import useSWR from 'swr'
  11. import { v4 as uuid4 } from 'uuid'
  12. import Checkbox from '@/app/components/base/checkbox'
  13. import cn from '@/utils/classnames'
  14. import { useContext } from 'use-context-selector'
  15. import { ToastContext } from '@/app/components/base/toast'
  16. import Confirm from '@/app/components/base/confirm'
  17. const DetailModal = ({
  18. transfer,
  19. onCancel,
  20. onSend,
  21. }: any) => {
  22. const { notify } = useContext(ToastContext)
  23. const [questionRelation, setQuestionRelation] = useState<string>('')
  24. const [questionFilter, setQuestionFilter] = useState<string>('') // the input value
  25. const [question, setQuestion] = useState<string>(transfer.row?.question || '') // the input value
  26. const [intentType, setIntentType] = useState<string>(transfer.row?.intentType || '') // the input value
  27. const { data: dataOptionsIntentType }: any = useSWR(
  28. {
  29. url: '/xxx',
  30. params: {
  31. page: 1,
  32. limit: 1000,
  33. },
  34. },
  35. fetchIntentType,
  36. )
  37. const optionsIntentType: any = dataOptionsIntentType?.data.map((v: any) => ({ name: v.name, value: v.id })) || []
  38. const [intentName, setIntentName] = useState<string>(transfer.row?.intentName || '') // the input value
  39. const { data: dataOptionsIntentName }: any = useSWR(
  40. {
  41. url: '/xxx',
  42. params: {
  43. page: 1,
  44. limit: 1000,
  45. intentType,
  46. },
  47. },
  48. fetchIntent,
  49. )
  50. const optionsIntentName: any = dataOptionsIntentName?.data.map((v: any) => ({ name: v.name, value: v.id })) || []
  51. const [questionList, setQuestionList] = useState<any>([{ id: uuid4(), name: '啊啊啊啊啊啊啊啊啊啊' }, { id: uuid4(), name: '啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊' }])
  52. const handleAddQuestion = () => {
  53. if (!questionRelation)
  54. return
  55. if (questionList.some((v: any) => v.name === questionRelation)) {
  56. notify({ type: 'warning', message: '请勿新增重复数据!' })
  57. return
  58. }
  59. setQuestionList([...questionList, { id: uuid4(), name: questionRelation }])
  60. setQuestionRelation('')
  61. }
  62. const [questionSelectMap, setQuestionSelectMap] = useState<any>(new Map())
  63. const addQuestionSelectMap = (key: any, value: any) => {
  64. setQuestionSelectMap((prevMap: any) => {
  65. const newMap = new Map(prevMap)
  66. newMap.set(key, value)
  67. return newMap
  68. })
  69. }
  70. const delQuestionSelectMap = (key: any) => {
  71. setQuestionSelectMap((prevMap: any) => {
  72. const newMap = new Map(prevMap)
  73. newMap.delete(key)
  74. return newMap
  75. })
  76. }
  77. const [showConfirmDelete, setShowConfirmDelete] = useState(false)
  78. const [row, setRow] = useState<any>({})
  79. const handleDelQuestion = async () => {
  80. try {
  81. await delCorpusQuestion({
  82. url: `/xxx/${row.id}`,
  83. body: {},
  84. })
  85. setShowConfirmDelete(false)
  86. // mutate()
  87. }
  88. catch (e) { }
  89. }
  90. const [showQuestionEdit, setShowQuestionEdit] = useState(false)
  91. const [editQuestion, setEditQuestion] = useState<string>('')
  92. const [corpusRow, setCorpusRow] = useState<any>({})
  93. const [corpusRowConfig, setCorpusRowConfig] = useState<string>('')
  94. const handleSave = useCallback(async () => {
  95. // try {
  96. // let res
  97. // if (transfer.mode === 'add') {
  98. // res = await addCorpus({
  99. // url: '/xxx',
  100. // body: { name, type: 'knowledge_category' },
  101. // })
  102. // }
  103. // else {
  104. // res = await editCorpus({
  105. // url: '/xxx',
  106. // body: { name },
  107. // })
  108. // }
  109. // const { id }: any = res
  110. // if (id) {
  111. // onCancel()
  112. // onSend()
  113. // }
  114. // }
  115. // catch (e) { }
  116. }, [name, onCancel, onSend, transfer])
  117. const handleSaveQuestion = useCallback(async () => {
  118. // try {
  119. // let res
  120. // if (transfer.mode === 'add') {
  121. // res = await addCorpus({
  122. // url: '/xxx',
  123. // body: { name, type: 'knowledge_category' },
  124. // })
  125. // }
  126. // else {
  127. // res = await editCorpus({
  128. // url: '/xxx',
  129. // body: { name },
  130. // })
  131. // }
  132. // const { id }: any = res
  133. // if (id) {
  134. // onCancel()
  135. // onSend()
  136. // }
  137. // }
  138. // catch (e) { }
  139. }, [name, onCancel, onSend, transfer])
  140. return (
  141. <div>
  142. <Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px] w-[800px] max-w-[800px]">
  143. <div className='mb-2 flex justify-between'>
  144. <div className='text-xl font-semibold text-text-primary'>{transfer.mode === 'add' ? '新增' : '编辑'}意图</div>
  145. <RiCloseLine className='h-4 w-4 cursor-pointer text-text-tertiary' onClick={onCancel} />
  146. </div>
  147. <div className="shrink-0 text-gray-500">
  148. <div className="flex flex-col gap-2">
  149. <div className="flex w-full items-center">
  150. <div className="w-[80px]">意图类型</div>
  151. <div className="flex flex-1">
  152. <SimpleSelect
  153. className="h-[32px] w-[200px]"
  154. defaultValue={intentType}
  155. onSelect={(i: any) => {
  156. setIntentType(i.value)
  157. setIntentName('')
  158. }}
  159. items={optionsIntentType}
  160. allowSearch={false}
  161. placeholder="请选择意图类型"
  162. />
  163. </div>
  164. </div>
  165. <div className="flex w-full items-center">
  166. <div className="w-[80px]">意图名称</div>
  167. <div className="flex-1">
  168. <Input
  169. showClearIcon
  170. value={question}
  171. onChange={e => setQuestion(e.target.value)}
  172. onClear={() => setQuestion('')}
  173. />
  174. </div>
  175. </div>
  176. {
  177. transfer.mode === 'edit' && (
  178. <div className="flex w-full items-center">
  179. <div className="w-[80px]">关键词</div>
  180. <div className="flex-1">
  181. <Input
  182. showClearIcon
  183. value={questionRelation}
  184. onChange={e => setQuestionRelation(e.target.value)}
  185. onClear={() => setQuestionRelation('')}
  186. placeholder='输入后Enter以添加'
  187. onEnter={handleAddQuestion}
  188. />
  189. </div>
  190. </div>
  191. )
  192. }
  193. </div>
  194. {
  195. transfer.mode === 'edit' && (
  196. <div className="mt-3 flex flex-col">
  197. <div className="flex h-10 w-full items-center gap-2 border-2 border-[#F6F8FC] bg-[#F6F8FC] px-2">
  198. <div className='flex items-center' onClick={e => e.stopPropagation()}>
  199. <Checkbox
  200. className='mr-2 shrink-0'
  201. checked={questionList.every((v: any) => questionSelectMap.has(v.id))}
  202. onCheck={() => {
  203. questionList.every((v: any) => questionSelectMap.has(v.id))
  204. ? setQuestionSelectMap(new Map())
  205. : questionList.forEach((v: any) => addQuestionSelectMap(v.id, v))
  206. }}
  207. disabled={questionList.length === 0}
  208. />
  209. 全选
  210. </div>
  211. <div className="ml-auto w-[200px]">
  212. <Input
  213. showClearIcon
  214. value={questionFilter}
  215. onChange={e => setQuestionFilter(e.target.value)}
  216. onClear={() => setQuestionFilter('')}
  217. placeholder='请输入相似问题名称进行过滤'
  218. />
  219. </div>
  220. <Button variant='primary' className={cn('shrink-0')}>
  221. 批量删除
  222. </Button>
  223. </div>
  224. <div className="flex h-[150px] flex-col gap-2 overflow-y-auto border-2 border-solid border-[#F6F8FC] p-2">
  225. {
  226. questionList.filter((v: any) => !questionFilter || v.name.includes(questionFilter)).map((item: any) => (
  227. <div key={item.id} className="flex items-center">
  228. <Checkbox
  229. className='mr-2 shrink-0'
  230. checked={questionSelectMap.has(item.id)}
  231. onCheck={() => {
  232. questionSelectMap.has(item.id)
  233. ? delQuestionSelectMap(item.id)
  234. : addQuestionSelectMap(item.id, item)
  235. }}
  236. disabled={questionList.length === 0}
  237. />
  238. <div className="flex-1">
  239. {item.name}
  240. </div>
  241. <Button variant='ghost-accent' size='small' className={cn('shrink-0')}
  242. onClick={() => {
  243. setRow(item)
  244. setEditQuestion(item.name)
  245. setShowQuestionEdit(true)
  246. }}>
  247. 编辑
  248. </Button>
  249. <Button variant='ghost' size='small' className={cn('shrink-0 text-red-600')}
  250. onClick={() => {
  251. setRow(item)
  252. setShowConfirmDelete(true)
  253. }}>
  254. 刪除
  255. </Button>
  256. </div>
  257. ))
  258. }
  259. </div>
  260. <div className="flex border-2 border-t-0 border-solid border-[#F6F8FC] p-2 text-xs">
  261. <div>共{questionList.length}条</div>
  262. <div className="ml-4">已选择{questionSelectMap.size}条</div>
  263. </div>
  264. </div>
  265. )
  266. }
  267. <Button
  268. tabIndex={0}
  269. className='w-full'
  270. onClick={handleSave}
  271. disabled={!question.length || !intentType.length || !intentName.length}
  272. variant='primary'
  273. >
  274. 保存
  275. </Button>
  276. </div>
  277. </Modal>
  278. {
  279. showQuestionEdit && (
  280. <Modal overflowVisible isShow onClose={() => { }} className="p-[24px 32px] w-[400px]">
  281. <div className='mb-2 flex justify-between'>
  282. <div className='text-xl font-semibold text-text-primary'>编辑关键词</div>
  283. <RiCloseLine className='h-4 w-4 cursor-pointer text-text-tertiary' onClick={() => setShowQuestionEdit(false)} />
  284. </div>
  285. <div>
  286. <div className={cn('flex flex-wrap items-center justify-between py-4')}>
  287. <div className='shrink-0 py-2 text-sm font-medium leading-[20px] text-text-primary'>
  288. 关键词
  289. </div>
  290. <Input
  291. value={editQuestion}
  292. onChange={e => setEditQuestion(e.target.value)}
  293. className='h-9'
  294. placeholder='请输入关键词'
  295. />
  296. </div>
  297. <Button
  298. tabIndex={0}
  299. className='w-full'
  300. onClick={handleSaveQuestion}
  301. disabled={!editQuestion.length}
  302. variant='primary'
  303. >
  304. 保存
  305. </Button>
  306. </div>
  307. </Modal>
  308. )
  309. }
  310. {showConfirmDelete && (
  311. <Confirm
  312. title="删除确认"
  313. content={`请确认是否删除${row.name}?`}
  314. isShow={showConfirmDelete}
  315. onConfirm={handleDelQuestion}
  316. onCancel={() => setShowConfirmDelete(false)}
  317. />
  318. )}
  319. </div>
  320. )
  321. }
  322. export default DetailModal