AiAssistant.js 4.62 KB
/* eslint-disable */
import React, { useState, useContext, useEffect, useRef } from "react";
import { Button, message, Spin } from "antd-v4";
import { SendOutlined } from "@ant-design/icons";
import styles from "../index.less";
import myContext from "../utils/MyContext";
import Services from "../services";

const AiAssistant = () => {
  const { props, dispatch } = useContext(myContext);
  const [inputValue, setInputValue] = useState(""); // 文本框内容
  const [dialogue, setDialogue] = useState([]); // 对话记录内容
  const dialogueRef = useRef(null); // 对话框dom
  const lastContent = useRef(""); // 最后展示的内容
  const lastAnswer = useRef(null); // 最后展示内容的dom
  const [loading, setLoading] = useState(false); // 回答加载状态
  const [textState, setTextState] = useState(true); // 是否可输入状态

  // 获取随机时间
  const getRandomTime = (time = 100) => {
    return Math.floor(Math.random() * time);
  };

  // 内容展示动画效果
  const showCartoon = (content, chartIndex = 0, time = 0) => {
    if (time === 0) {
      setTimeout(() => {
        setLoading(false);
        showCartoon(content, chartIndex, 1);
      }, getRandomTime(2000));
      return;
    }
    lastContent.current += content.charAt(chartIndex);
    lastAnswer.current.innerText = lastContent.current;
    if (
      lastAnswer.current.getBoundingClientRect().width /
        dialogueRef.current.getBoundingClientRect().width >
      0.7
    ) {
      lastAnswer.current.style.display = "flow-root";
    }
    dialogueRef.current.scrollTop = 9999;
    if (chartIndex < content.length - 1) {
      let timedelay = getRandomTime(50);
      if (chartIndex < 10) {
        timedelay = getRandomTime(500);
      }
      if (chartIndex >= 5 && chartIndex < 50) {
        timedelay = getRandomTime();
      }
      if (content.length - chartIndex < 20) {
        timedelay = 20;
      }
      setTimeout(() => {
        showCartoon(content, chartIndex + 1, 1);
      }, timedelay);
    } else {
      setTextState(true);
    }
  };

  // 给回答超过一行的加样式
  useEffect(
    () => {
      if (dialogue.length) {
        const oAnswer = document.querySelectorAll(".dialogue .answer");
        if (oAnswer) {
          oAnswer.forEach(item => {
            if (
              item.firstChild.getBoundingClientRect().width /
                dialogueRef.current.getBoundingClientRect().width >
              0.7
            ) {
              item.firstChild.style.display = "flow-root";
            }
          });
        }
        dialogueRef.current.scrollTop = 9999;
        if (dialogue[dialogue.length - 1].type === "answer") {
          setTextState(false);
          setLoading(true);
          lastAnswer.current.style.display = "";
          showCartoon(dialogue[dialogue.length - 1].content);
        }
      }
    },
    [dialogue]
  );

  // 问题提交事件
  const handleQuestion = async () => {
    if (!textState) {
      message.info("请等待此次对话结束后操作");
      return;
    }
    if (!inputValue) {
      return;
    }
    setDialogue(preState => [
      ...preState,
      { type: "question", content: inputValue }
    ]);
    setInputValue("");
    const result = await Services.getRobotReply(inputValue);
    lastContent.current = "";
    setDialogue(preState => [...preState, { type: "answer", content: result }]);
  };

  return (
    <div className={styles.aiAssistant}>
      <div className="dialogue" ref={dialogueRef}>
        {dialogue.map((item, index) => {
          const { type, content } = item;
          if (
            index === dialogue.length - 1 &&
            type === "answer" &&
            lastContent.current !== content
          ) {
            return (
              <div className={type} key={index}>
                {loading ? (
                  <Spin />
                ) : (
                  <span ref={lastAnswer} style={{ color: "#6055d3" }} />
                )}
              </div>
            );
          } else {
            return (
              <div className={type} key={index}>
                <span>{content}</span>
              </div>
            );
          }
        })}
        <div className="emptyLine" />
      </div>
      <div className="enter">
        <input
          placeholder="请输入..."
          value={inputValue}
          onChange={e => {
            setInputValue(e.target.value);
          }}
          onKeyDown={e => {
            if (e.keyCode === 13) {
              handleQuestion();
            }
          }}
        />
        <Button type="link" icon={<SendOutlined />} onClick={handleQuestion} />
      </div>
    </div>
  );
};

export default AiAssistant;