ChatInputPanel.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { ChatContentContext } from '@/pages/chat';
  2. import { LoadingOutlined } from '@ant-design/icons';
  3. import { Button, Input, Spin } from 'antd';
  4. import classNames from 'classnames';
  5. import { useSearchParams } from 'next/navigation';
  6. import React, { useContext, useMemo, useRef, useState } from 'react';
  7. import { useTranslation } from 'react-i18next';
  8. import ToolsBar from './ToolsBar';
  9. const ChatInputPanel: React.FC<{ ctrl: AbortController }> = ({ ctrl }) => {
  10. const { t } = useTranslation();
  11. const {
  12. scrollRef,
  13. replyLoading,
  14. handleChat,
  15. appInfo,
  16. currentDialogue,
  17. temperatureValue,
  18. resourceValue,
  19. refreshDialogList,
  20. } = useContext(ChatContentContext);
  21. const searchParams = useSearchParams();
  22. // const scene = searchParams?.get('scene') ?? ''; // unused
  23. const select_param = searchParams?.get('select_param') ?? '';
  24. const [userInput, setUserInput] = useState<string>('');
  25. const [isFocus, setIsFocus] = useState<boolean>(false);
  26. const [isZhInput, setIsZhInput] = useState<boolean>(false);
  27. const submitCountRef = useRef(0);
  28. const paramKey: string[] = useMemo(() => {
  29. return appInfo.param_need?.map(i => i.type) || [];
  30. }, [appInfo.param_need]);
  31. const onSubmit = async () => {
  32. submitCountRef.current++;
  33. setTimeout(() => {
  34. scrollRef.current?.scrollTo({
  35. top: scrollRef.current?.scrollHeight,
  36. behavior: 'smooth',
  37. });
  38. setUserInput('');
  39. }, 0);
  40. await handleChat(userInput, {
  41. app_code: appInfo.app_code || '',
  42. ...(paramKey.includes('temperature') && { temperature: temperatureValue }),
  43. select_param,
  44. ...(paramKey.includes('resource') && {
  45. select_param:
  46. typeof resourceValue === 'string'
  47. ? resourceValue
  48. : JSON.stringify(resourceValue) || currentDialogue.select_param,
  49. }),
  50. });
  51. // 如果应用进来第一次对话,刷新对话列表
  52. if (submitCountRef.current === 1) {
  53. await refreshDialogList();
  54. }
  55. };
  56. return (
  57. <div className='flex flex-col w-5/6 mx-auto pt-4 pb-6 bg-transparent'>
  58. <div
  59. className={`flex flex-1 flex-col bg-white dark:bg-[rgba(255,255,255,0.16)] px-5 py-4 pt-2 rounded-xl relative border-t border-b border-l border-r dark:border-[rgba(255,255,255,0.6)] ${
  60. isFocus ? 'border-[#0c75fc]' : ''
  61. }`}
  62. id='input-panel'
  63. >
  64. <ToolsBar ctrl={ctrl} />
  65. <Input.TextArea
  66. placeholder={t('input_tips')}
  67. className='w-full h-20 resize-none border-0 p-0 focus:shadow-none dark:bg-transparent'
  68. value={userInput}
  69. onKeyDown={e => {
  70. if (e.key === 'Enter') {
  71. if (e.shiftKey) {
  72. return;
  73. }
  74. if (isZhInput) {
  75. return;
  76. }
  77. e.preventDefault();
  78. if (!userInput.trim() || replyLoading) {
  79. return;
  80. }
  81. onSubmit();
  82. }
  83. }}
  84. onChange={e => {
  85. setUserInput(e.target.value);
  86. }}
  87. onFocus={() => {
  88. setIsFocus(true);
  89. }}
  90. onBlur={() => setIsFocus(false)}
  91. onCompositionStart={() => setIsZhInput(true)}
  92. onCompositionEnd={() => setIsZhInput(false)}
  93. />
  94. <Button
  95. type='primary'
  96. className={classNames(
  97. 'flex items-center justify-center w-14 h-8 rounded-lg text-sm absolute right-4 bottom-3 bg-button-gradient border-0',
  98. {
  99. 'cursor-not-allowed': !userInput.trim(),
  100. },
  101. )}
  102. onClick={() => {
  103. if (replyLoading || !userInput.trim()) {
  104. return;
  105. }
  106. onSubmit();
  107. }}
  108. >
  109. {replyLoading ? (
  110. <Spin spinning={replyLoading} indicator={<LoadingOutlined className='text-white' />} />
  111. ) : (
  112. t('sent')
  113. )}
  114. </Button>
  115. </div>
  116. </div>
  117. );
  118. };
  119. export default ChatInputPanel;