Spaces:
Runtime error
Runtime error
BraisedPork
commited on
Commit
·
bd1a2b6
1
Parent(s):
e961666
V0.2.0 (#236)
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +1 -1
- Dockerfile +2 -1
- README.md +5 -2
- backend_example.py +17 -14
- docker/msdl/__main__.py +29 -17
- docker/msdl/config.py +1 -0
- docker/setup.py +1 -1
- frontend/React/.gitignore +5 -0
- frontend/React/.prettierignore +1 -1
- frontend/React/README.md +134 -79
- frontend/React/README_zh-CN.md +135 -0
- frontend/React/package.json +6 -0
- frontend/React/src/App.module.less +2 -4
- frontend/React/src/App.tsx +3 -1
- frontend/React/src/components/answer/index.tsx +0 -39
- frontend/React/src/components/chat-right/index.tsx +0 -310
- frontend/React/src/components/mind-map-item/index.module.less +0 -117
- frontend/React/src/components/mind-map-item/index.tsx +0 -50
- frontend/React/src/config/cgi.ts +0 -2
- frontend/React/src/global.d.ts +1 -1
- frontend/React/src/index.less +1 -3
- frontend/React/src/pages/mindsearch/assets/bookmark-icon.svg +4 -0
- frontend/React/src/pages/mindsearch/assets/empty-chat-right.svg +52 -0
- frontend/React/src/pages/mindsearch/assets/fold-icon.svg +3 -0
- frontend/React/src/pages/mindsearch/assets/logo.svg +24 -0
- frontend/React/src/pages/mindsearch/assets/logo1.svg +32 -0
- frontend/React/src/pages/mindsearch/assets/mindsearch-avatar.svg +17 -0
- frontend/React/src/pages/mindsearch/assets/pack-up-disabled.svg +3 -0
- frontend/React/src/pages/mindsearch/assets/pack-up.svg +5 -0
- frontend/React/src/pages/mindsearch/assets/sendIcon.svg +4 -0
- frontend/React/src/pages/mindsearch/assets/think-progress-icon.svg +15 -0
- frontend/React/src/pages/mindsearch/assets/unflod-icon.svg +3 -0
- frontend/React/src/pages/mindsearch/components/answer/index.module.less +110 -0
- frontend/React/src/pages/mindsearch/components/answer/index.tsx +108 -0
- frontend/React/src/{components/answer → pages/mindsearch/components/answer/loading-animation}/index.module.less +16 -85
- frontend/React/src/pages/mindsearch/components/answer/loading-animation/index.tsx +13 -0
- frontend/React/src/pages/mindsearch/components/chat-right/components/empty-placeholder/index.module.less +27 -0
- frontend/React/src/pages/mindsearch/components/chat-right/components/empty-placeholder/index.tsx +17 -0
- frontend/React/src/pages/mindsearch/components/chat-right/components/query-item/index.module.less +30 -0
- frontend/React/src/pages/mindsearch/components/chat-right/components/query-item/index.tsx +11 -0
- frontend/React/src/pages/mindsearch/components/chat-right/components/search-item/index.module.less +101 -0
- frontend/React/src/pages/mindsearch/components/chat-right/components/search-item/index.tsx +30 -0
- frontend/React/src/{components → pages/mindsearch/components}/chat-right/index.module.less +92 -95
- frontend/React/src/pages/mindsearch/components/chat-right/index.tsx +272 -0
- frontend/React/src/pages/mindsearch/components/custom-markdown/index.module.less +168 -0
- frontend/React/src/pages/mindsearch/components/custom-markdown/index.tsx +116 -0
- frontend/React/src/{components → pages/mindsearch/components}/iconfont/index.tsx +3 -3
- frontend/React/src/{components → pages/mindsearch/components}/loading/index.module.less +24 -24
- frontend/React/src/{components → pages/mindsearch/components}/loading/index.tsx +2 -2
- frontend/React/src/pages/mindsearch/components/mind-map-item/index.module.less +150 -0
.gitignore
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
# Byte-compiled / optimized / DLL files
|
2 |
__pycache__/
|
3 |
-
*.py[
|
4 |
*$py.class
|
5 |
|
6 |
# C extensions
|
|
|
1 |
# Byte-compiled / optimized / DLL files
|
2 |
__pycache__/
|
3 |
+
*.py[ciod]
|
4 |
*$py.class
|
5 |
|
6 |
# C extensions
|
Dockerfile
CHANGED
@@ -23,4 +23,5 @@ RUN conda create --name fastapi python=3.10 -y && \
|
|
23 |
EXPOSE 8000
|
24 |
|
25 |
# 启动 FastAPI 服务
|
26 |
-
|
|
|
|
23 |
EXPOSE 8000
|
24 |
|
25 |
# 启动 FastAPI 服务
|
26 |
+
ENTRYPOINT ["conda", "run", "--no-capture-output", "-n", "fastapi"]
|
27 |
+
CMD ["python3", "-m", "mindsearch.app", "--asy", "--host", "0.0.0.0", "--port", "8002"]
|
README.md
CHANGED
@@ -39,7 +39,7 @@ mv .env.example .env
|
|
39 |
Setup FastAPI Server.
|
40 |
|
41 |
```bash
|
42 |
-
python -m mindsearch.app --lang en --model_format internlm_server --search_engine DuckDuckGoSearch
|
43 |
```
|
44 |
|
45 |
- `--lang`: language of the model, `en` for English and `cn` for Chinese.
|
@@ -52,8 +52,10 @@ python -m mindsearch.app --lang en --model_format internlm_server --search_engin
|
|
52 |
- `BingSearch` for Bing search engine.
|
53 |
- `BraveSearch` for Brave search web api engine.
|
54 |
- `GoogleSearch` for Google Serper web search api engine.
|
|
|
55 |
|
56 |
-
Please set your Web Search engine API key as the `WEB_SEARCH_API_KEY` environment variable unless you are using `DuckDuckGo`.
|
|
|
57 |
|
58 |
### Step4: Setup MindSearch Frontend
|
59 |
|
@@ -98,6 +100,7 @@ To use a different type of web search API, modify the `searcher_type` attribute
|
|
98 |
- `DuckDuckGoSearch`
|
99 |
- `BraveSearch`
|
100 |
- `BingSearch`
|
|
|
101 |
|
102 |
For example, to change to the Brave Search API, you would configure it as follows:
|
103 |
|
|
|
39 |
Setup FastAPI Server.
|
40 |
|
41 |
```bash
|
42 |
+
python -m mindsearch.app --lang en --model_format internlm_server --search_engine DuckDuckGoSearch --asy
|
43 |
```
|
44 |
|
45 |
- `--lang`: language of the model, `en` for English and `cn` for Chinese.
|
|
|
52 |
- `BingSearch` for Bing search engine.
|
53 |
- `BraveSearch` for Brave search web api engine.
|
54 |
- `GoogleSearch` for Google Serper web search api engine.
|
55 |
+
- `TencentSearch` for Tencent search api engine.
|
56 |
|
57 |
+
Please set your Web Search engine API key as the `WEB_SEARCH_API_KEY` environment variable unless you are using `DuckDuckGo`, or `TencentSearch` that requires secret id as `TENCENT_SEARCH_SECRET_ID` and secret key as `TENCENT_SEARCH_SECRET_KEY`.
|
58 |
+
- `--asy`: deploy asynchronous agents.
|
59 |
|
60 |
### Step4: Setup MindSearch Frontend
|
61 |
|
|
|
100 |
- `DuckDuckGoSearch`
|
101 |
- `BraveSearch`
|
102 |
- `BingSearch`
|
103 |
+
- `TencentSearch`
|
104 |
|
105 |
For example, to change to the Brave Search API, you would configure it as follows:
|
106 |
|
backend_example.py
CHANGED
@@ -1,34 +1,37 @@
|
|
1 |
import json
|
|
|
2 |
import requests
|
3 |
|
4 |
# Define the backend URL
|
5 |
-
url =
|
6 |
-
headers = {
|
|
|
7 |
|
8 |
# Function to send a query to the backend and get the response
|
9 |
def get_response(query):
|
10 |
# Prepare the input data
|
11 |
-
data = {
|
12 |
-
|
13 |
# Send the request to the backend
|
14 |
response = requests.post(url, headers=headers, data=json.dumps(data), timeout=20, stream=True)
|
15 |
-
|
16 |
# Process the streaming response
|
17 |
-
for chunk in response.iter_lines(chunk_size=8192, decode_unicode=False, delimiter=b
|
18 |
if chunk:
|
19 |
-
decoded = chunk.decode(
|
20 |
-
if decoded ==
|
21 |
continue
|
22 |
-
if decoded[:6] ==
|
23 |
decoded = decoded[6:]
|
24 |
-
elif decoded.startswith(
|
25 |
continue
|
26 |
response_data = json.loads(decoded)
|
27 |
-
agent_return = response_data[
|
28 |
-
node_name = response_data[
|
29 |
print(f"Node: {node_name}, Response: {agent_return['response']}")
|
30 |
|
|
|
31 |
# Example usage
|
32 |
-
if __name__ ==
|
33 |
query = "What is the weather like today in New York?"
|
34 |
-
get_response(query)
|
|
|
1 |
import json
|
2 |
+
|
3 |
import requests
|
4 |
|
5 |
# Define the backend URL
|
6 |
+
url = "http://localhost:8002/solve"
|
7 |
+
headers = {"Content-Type": "application/json"}
|
8 |
+
|
9 |
|
10 |
# Function to send a query to the backend and get the response
|
11 |
def get_response(query):
|
12 |
# Prepare the input data
|
13 |
+
data = {"inputs": query}
|
14 |
+
|
15 |
# Send the request to the backend
|
16 |
response = requests.post(url, headers=headers, data=json.dumps(data), timeout=20, stream=True)
|
17 |
+
|
18 |
# Process the streaming response
|
19 |
+
for chunk in response.iter_lines(chunk_size=8192, decode_unicode=False, delimiter=b"\n"):
|
20 |
if chunk:
|
21 |
+
decoded = chunk.decode("utf-8")
|
22 |
+
if decoded == "\r":
|
23 |
continue
|
24 |
+
if decoded[:6] == "data: ":
|
25 |
decoded = decoded[6:]
|
26 |
+
elif decoded.startswith(": ping - "):
|
27 |
continue
|
28 |
response_data = json.loads(decoded)
|
29 |
+
agent_return = response_data["response"]
|
30 |
+
node_name = response_data["current_node"]
|
31 |
print(f"Node: {node_name}, Response: {agent_return['response']}")
|
32 |
|
33 |
+
|
34 |
# Example usage
|
35 |
+
if __name__ == "__main__":
|
36 |
query = "What is the weather like today in New York?"
|
37 |
+
get_response(query)
|
docker/msdl/__main__.py
CHANGED
@@ -55,8 +55,7 @@ def copy_backend_dockerfile(choice):
|
|
55 |
"BACKEND_DOCKERFILE_COPIED",
|
56 |
source_path=str(source_path),
|
57 |
dest_path=str(dest_path),
|
58 |
-
)
|
59 |
-
)
|
60 |
|
61 |
|
62 |
def copy_frontend_dockerfile():
|
@@ -75,19 +74,30 @@ def copy_frontend_dockerfile():
|
|
75 |
"FRONTEND_DOCKERFILE_COPIED",
|
76 |
source_path=str(source_path),
|
77 |
dest_path=str(dest_path),
|
78 |
-
)
|
79 |
-
)
|
80 |
|
81 |
|
82 |
def get_user_choices():
|
83 |
backend_language_choices = [
|
84 |
-
{
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
]
|
87 |
|
88 |
model_deployment_type = [
|
89 |
-
{
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
]
|
92 |
|
93 |
backend_language = inquirer.select(
|
@@ -103,7 +113,10 @@ def get_user_choices():
|
|
103 |
model_formats = get_model_formats(model)
|
104 |
model_format = inquirer.select(
|
105 |
message=t("MODEL_FORMAT_CHOICE"),
|
106 |
-
choices=[{
|
|
|
|
|
|
|
107 |
).execute()
|
108 |
|
109 |
# If the model is cloud_llm, ask for the API key
|
@@ -118,7 +131,8 @@ def get_user_choices():
|
|
118 |
|
119 |
if existing_api_key:
|
120 |
use_existing = inquirer.confirm(
|
121 |
-
message=t(
|
|
|
122 |
default=True,
|
123 |
).execute()
|
124 |
|
@@ -126,17 +140,16 @@ def get_user_choices():
|
|
126 |
return backend_language, model, model_format
|
127 |
else:
|
128 |
print(
|
129 |
-
t("CONFIRM_OVERWRITE_EXISTING_API_KEY",
|
130 |
-
|
131 |
else:
|
132 |
print(t("PLEASE_INPUT_NEW_API_KEY", ENV_VAR_NAME=env_var_name))
|
133 |
|
134 |
while True:
|
135 |
api_key = inquirer.secret(
|
136 |
message=t(
|
137 |
-
"PLEASE_INPUT_NEW_API_KEY_FROM_ZERO",
|
138 |
-
|
139 |
-
).execute()
|
140 |
cleaned_api_key = clean_api_key(api_key)
|
141 |
|
142 |
if validate_api_key(cleaned_api_key, env_var_name, t):
|
@@ -145,8 +158,7 @@ def get_user_choices():
|
|
145 |
else:
|
146 |
print(t("INVALID_API_KEY_FORMAT"))
|
147 |
retry = inquirer.confirm(
|
148 |
-
message=t("RETRY_API_KEY_INPUT"), default=True
|
149 |
-
).execute()
|
150 |
if not retry:
|
151 |
print(t("API_KEY_INPUT_CANCELLED"))
|
152 |
sys.exit(1)
|
|
|
55 |
"BACKEND_DOCKERFILE_COPIED",
|
56 |
source_path=str(source_path),
|
57 |
dest_path=str(dest_path),
|
58 |
+
))
|
|
|
59 |
|
60 |
|
61 |
def copy_frontend_dockerfile():
|
|
|
74 |
"FRONTEND_DOCKERFILE_COPIED",
|
75 |
source_path=str(source_path),
|
76 |
dest_path=str(dest_path),
|
77 |
+
))
|
|
|
78 |
|
79 |
|
80 |
def get_user_choices():
|
81 |
backend_language_choices = [
|
82 |
+
{
|
83 |
+
"name": t("CHINESE"),
|
84 |
+
"value": "cn"
|
85 |
+
},
|
86 |
+
{
|
87 |
+
"name": t("ENGLISH"),
|
88 |
+
"value": "en"
|
89 |
+
},
|
90 |
]
|
91 |
|
92 |
model_deployment_type = [
|
93 |
+
{
|
94 |
+
"name": t("CLOUD_MODEL"),
|
95 |
+
"value": CLOUD_LLM_DOCKERFILE
|
96 |
+
},
|
97 |
+
{
|
98 |
+
"name": t("LOCAL_MODEL"),
|
99 |
+
"value": LOCAL_LLM_DOCKERFILE
|
100 |
+
},
|
101 |
]
|
102 |
|
103 |
backend_language = inquirer.select(
|
|
|
113 |
model_formats = get_model_formats(model)
|
114 |
model_format = inquirer.select(
|
115 |
message=t("MODEL_FORMAT_CHOICE"),
|
116 |
+
choices=[{
|
117 |
+
"name": format,
|
118 |
+
"value": format
|
119 |
+
} for format in model_formats],
|
120 |
).execute()
|
121 |
|
122 |
# If the model is cloud_llm, ask for the API key
|
|
|
131 |
|
132 |
if existing_api_key:
|
133 |
use_existing = inquirer.confirm(
|
134 |
+
message=t(
|
135 |
+
"CONFIRM_USE_EXISTING_API_KEY", ENV_VAR_NAME=env_var_name),
|
136 |
default=True,
|
137 |
).execute()
|
138 |
|
|
|
140 |
return backend_language, model, model_format
|
141 |
else:
|
142 |
print(
|
143 |
+
t("CONFIRM_OVERWRITE_EXISTING_API_KEY",
|
144 |
+
ENV_VAR_NAME=env_var_name))
|
145 |
else:
|
146 |
print(t("PLEASE_INPUT_NEW_API_KEY", ENV_VAR_NAME=env_var_name))
|
147 |
|
148 |
while True:
|
149 |
api_key = inquirer.secret(
|
150 |
message=t(
|
151 |
+
"PLEASE_INPUT_NEW_API_KEY_FROM_ZERO",
|
152 |
+
ENV_VAR_NAME=env_var_name)).execute()
|
|
|
153 |
cleaned_api_key = clean_api_key(api_key)
|
154 |
|
155 |
if validate_api_key(cleaned_api_key, env_var_name, t):
|
|
|
158 |
else:
|
159 |
print(t("INVALID_API_KEY_FORMAT"))
|
160 |
retry = inquirer.confirm(
|
161 |
+
message=t("RETRY_API_KEY_INPUT"), default=True).execute()
|
|
|
162 |
if not retry:
|
163 |
print(t("API_KEY_INPUT_CANCELLED"))
|
164 |
sys.exit(1)
|
docker/msdl/config.py
CHANGED
@@ -4,6 +4,7 @@ from pathlib import Path
|
|
4 |
|
5 |
|
6 |
class FileSystemManager:
|
|
|
7 |
@staticmethod
|
8 |
def ensure_dir(dir_path):
|
9 |
"""Ensure the directory exists, create if it doesn't"""
|
|
|
4 |
|
5 |
|
6 |
class FileSystemManager:
|
7 |
+
|
8 |
@staticmethod
|
9 |
def ensure_dir(dir_path):
|
10 |
"""Ensure the directory exists, create if it doesn't"""
|
docker/setup.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
from setuptools import
|
2 |
|
3 |
setup(
|
4 |
name="msdl",
|
|
|
1 |
+
from setuptools import find_packages, setup
|
2 |
|
3 |
setup(
|
4 |
name="msdl",
|
frontend/React/.gitignore
CHANGED
@@ -14,7 +14,12 @@
|
|
14 |
|
15 |
# misc
|
16 |
.DS_Store
|
|
|
|
|
|
|
|
|
17 |
|
18 |
npm-debug.log*
|
19 |
yarn-debug.log*
|
20 |
yarn-error.log*
|
|
|
|
14 |
|
15 |
# misc
|
16 |
.DS_Store
|
17 |
+
.env.local
|
18 |
+
.env.development.local
|
19 |
+
.env.test.local
|
20 |
+
.env.production.local
|
21 |
|
22 |
npm-debug.log*
|
23 |
yarn-debug.log*
|
24 |
yarn-error.log*
|
25 |
+
|
frontend/React/.prettierignore
CHANGED
@@ -4,4 +4,4 @@ values
|
|
4 |
node_modules
|
5 |
.gitignore
|
6 |
.prettierignore
|
7 |
-
.husky
|
|
|
4 |
node_modules
|
5 |
.gitignore
|
6 |
.prettierignore
|
7 |
+
.husky
|
frontend/React/README.md
CHANGED
@@ -1,123 +1,181 @@
|
|
1 |
# Notice
|
2 |
-
|
3 |
-
|
4 |
-
## 请使用大于18.0.0的node版本
|
5 |
-
## 准备node.js开发环境
|
6 |
-
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,允许你在服务器端运行 JavaScript。以下是在 Windows、Linux 和 macOS 上安装 Node.js 的详细步骤。
|
7 |
|
8 |
-
|
9 |
-
-
|
10 |
|
11 |
-
|
|
|
|
|
12 |
|
13 |
-
|
14 |
|
15 |
-
|
16 |
-
![windows install](./windows-.png)
|
17 |
|
18 |
-
-
|
19 |
|
20 |
-
|
21 |
|
22 |
-
|
23 |
-
安装完成后,点击“Finish”结束安装。
|
24 |
|
25 |
-
-
|
26 |
|
27 |
-
|
28 |
-
输入 node -v 并回车,如果系统返回了 Node.js 的版本号,说明安装成功。
|
29 |
-
接着,输入 npm -v 并回车,npm 是 Node.js 的包管理器,如果返回了版本号,表示 npm 也已正确安装。
|
30 |
|
31 |
-
|
32 |
-
注意: 由于 Linux 发行版众多,以下以 Ubuntu 为例说明,其他发行版(如 CentOS、Debian 等)的安装方式可能略有不同,可自行查询对应的安装办法。
|
33 |
|
34 |
-
|
35 |
|
36 |
-
|
37 |
|
38 |
-
|
39 |
|
40 |
-
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
45 |
|
46 |
-
-
|
|
|
47 |
|
48 |
-
|
49 |
|
50 |
-
|
51 |
|
52 |
-
|
53 |
-
- 步骤 1: 访问 Node.js 官网
|
54 |
|
55 |
-
|
56 |
|
57 |
-
|
|
|
|
|
|
|
58 |
|
59 |
-
|
|
|
|
|
60 |
|
61 |
-
|
|
|
|
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
|
|
|
66 |
|
67 |
-
-
|
|
|
68 |
|
69 |
-
|
|
|
|
|
70 |
|
71 |
-
|
|
|
72 |
|
73 |
-
|
74 |
-
|
75 |
-
```
|
76 |
-
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
77 |
-
```
|
78 |
-
按照提示输入密码以确认安装。安装过程中,可能需要你同意许可协议等。
|
79 |
|
80 |
-
|
81 |
-
|
|
|
|
|
82 |
|
83 |
-
|
84 |
-
在终端中输入以下命令来安装最新版本的Node.js
|
85 |
-
```
|
86 |
-
brew install node
|
87 |
-
```
|
88 |
-
Homebrew会自动下载Node.js的安装包,并处理相关的依赖项和安装过程。你需要等待一段时间,直到安装完成。
|
89 |
|
90 |
-
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
|
97 |
-
|
98 |
|
99 |
-
|
100 |
-
|
|
|
|
|
101 |
|
102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
|
104 |
-
|
105 |
-
|
|
|
|
|
|
|
106 |
```
|
107 |
npm install
|
108 |
```
|
109 |
|
110 |
-
##
|
111 |
```
|
112 |
npm start
|
113 |
```
|
114 |
|
115 |
-
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
-
|
118 |
-
|
119 |
-
-
|
120 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
```
|
123 |
server: {
|
@@ -129,7 +187,4 @@ Homebrew会自动下载Node.js的安装包,并处理相关的依赖项和安
|
|
129 |
}
|
130 |
}
|
131 |
}
|
132 |
-
```
|
133 |
-
|
134 |
-
## 知悉
|
135 |
-
- 前端服务基于react开发,如需了解react相关知识,可参考:https://react.dev/
|
|
|
1 |
# Notice
|
2 |
+
- If you leave the page (Make the page invisible) and come back again, it will cause sse to reconnect.
|
3 |
+
- the project requires Node.js version >= 18.0.0.
|
|
|
|
|
|
|
4 |
|
5 |
+
# Prepare your dev-environment for frontend
|
6 |
+
[Node.js](https://nodejs.org/en)® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.
|
7 |
|
8 |
+
# Node.js Installation Guide (Windows, Linux, macOS)
|
9 |
+
## Windows Installation
|
10 |
+
- Step 1: Download Node.js
|
11 |
|
12 |
+
1. Open your web browser and visit the [Node.js official website](https://nodejs.org/en).
|
13 |
|
14 |
+
2. Navigate to the "Downloads" section.
|
|
|
15 |
|
16 |
+
3. Select the desired version (LTS recommended for long-term stability). As of August 2024, the latest LTS version might be v20.x.x.
|
17 |
|
18 |
+
4. Click on the "Windows Installer (.msi)" link to download the installation package.
|
19 |
|
20 |
+
- Step 2: Install Node.js
|
|
|
21 |
|
22 |
+
1. Double-click the downloaded .msi file to start the installation wizard.
|
23 |
|
24 |
+
2. Click "Next" to proceed.
|
|
|
|
|
25 |
|
26 |
+
3. Read and accept the license agreement by checking the "I accept the terms in the License Agreement" box.
|
|
|
27 |
|
28 |
+
4. Click "Next" again and select the installation directory. It's recommended to change the default location to avoid installing in the C drive.
|
29 |
|
30 |
+
5. Continue clicking "Next" to use the default settings until you reach the "Install" button.
|
31 |
|
32 |
+
6. Click "Install" to start the installation process.
|
33 |
|
34 |
+
7. Wait for the installation to complete and click "Finish" to exit the installation wizard.
|
35 |
|
36 |
+
- Step 3: Verify Installation
|
37 |
+
1. Open the Command Prompt (cmd) by pressing `Win + R`, typing `cmd`, and pressing Enter.
|
38 |
+
2. Type `node -v` and press Enter. You should see the installed Node.js version displayed.
|
39 |
+
3. Type `npm -v` and press Enter to verify the installed npm version. npm is the package manager that comes bundled with Node.js.
|
40 |
|
41 |
+
- Step 4: Configure npm Global Path (Optional)
|
42 |
+
If you want to change the default global installation path for npm, follow these steps:
|
43 |
|
44 |
+
1. Open the Command Prompt (cmd) as an administrator.
|
45 |
|
46 |
+
2. Navigate to your Node.js installation directory (e.g., C:\Program Files\nodejs).
|
47 |
|
48 |
+
3. Create two new folders named node_global and node_cache.
|
|
|
49 |
|
50 |
+
4. Run the following commands to set the new paths:
|
51 |
|
52 |
+
```bash
|
53 |
+
npm config set prefix "C:\Program Files\nodejs\node_global"
|
54 |
+
npm config set cache "C:\Program Files\nodejs\node_cache"
|
55 |
+
```
|
56 |
|
57 |
+
5. Open the Environment Variables settings in the System Properties.
|
58 |
+
6. Add `C:\Program Files\nodejs\node_global` to the `PATH` variable under User Variables.
|
59 |
+
7. Optionally, create a new system variable named `NODE_PATH` and set its value to ` C:\Program Files\nodejs\node_global\node_modules`.
|
60 |
|
61 |
+
## Linux Installation
|
62 |
+
- Step 1: Update Your System
|
63 |
+
Before installing Node.js, ensure your Linux system is up-to-date:
|
64 |
|
65 |
+
```bash
|
66 |
+
sudo apt-get update
|
67 |
+
sudo apt-get upgrade
|
68 |
+
```
|
69 |
|
70 |
+
- Step 2: Install Dependencies
|
71 |
+
Node.js requires certain dependencies to function properly:
|
72 |
|
73 |
+
```bash
|
74 |
+
sudo apt-get install build-essential libssl-dev
|
75 |
+
```
|
76 |
|
77 |
+
- Step 3: Download and Install Node.js
|
78 |
+
You can download the Node.js source code or use a package manager like `curl` or `wget` to download a pre-built binary. For simplicity, this guide assumes you're using a package manager.
|
79 |
|
80 |
+
1. Navigate to the Node.js download page for package managers.
|
81 |
+
Follow the instructions for your Linux distribution. For example, on Ubuntu, you can use:
|
|
|
|
|
|
|
|
|
82 |
|
83 |
+
```bash
|
84 |
+
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
85 |
+
sudo apt-get install -y nodejs
|
86 |
+
```
|
87 |
|
88 |
+
Replace 20.x with the desired version number if you don't want the latest version.
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
+
- Step 4: Verify Installation
|
91 |
+
1. Open a terminal.
|
92 |
+
2. Type `node -v` and press Enter to check the Node.js version.
|
93 |
+
3. Type `npm -v` and press Enter to verify the npm version.
|
94 |
+
|
95 |
+
|
96 |
+
## Installing Node.js on macOS
|
97 |
+
|
98 |
+
Installing Node.js on macOS is a straightforward process that can be accomplished using the official installer from the Node.js website or through package managers like Homebrew. This guide will cover both methods.
|
99 |
+
|
100 |
+
### Method 1: Using the Official Installer
|
101 |
+
- Visit the Node.js Website
|
102 |
+
- Open your web browser and navigate to https://nodejs.org/.
|
103 |
+
- Download the Installer
|
104 |
+
- Scroll down to the "Downloads" section.
|
105 |
+
- Click on the "macOS Installer" button to download the .pkg file. Ensure you download the latest version, which as of August 2024, might be v20.x.x or higher.
|
106 |
+
- Install Node.js
|
107 |
+
- Once the download is complete, locate the .pkg file in your Downloads folder.
|
108 |
+
- Double-click the file to start the installation process.
|
109 |
+
- Follow the on-screen instructions. Typically, you'll need to agree to the license agreement, select an installation location (the default is usually fine), and click "Continue" or "Install" until the installation is complete.
|
110 |
+
- Verify the Installation
|
111 |
+
- Open the Terminal application by going to "Finder" > "Applications" > "Utilities" > "Terminal" or using Spotlight Search (press `Cmd + Space` and type "Terminal").
|
112 |
+
- Type `node -v` and press Enter. This command should display the installed version of Node.js.
|
113 |
+
- Type `npm -v` and press Enter to verify that npm, the Node.js package manager, is also installed.
|
114 |
+
|
115 |
+
### Method 2: Using Homebrew
|
116 |
+
If you prefer to use a package manager, Homebrew is a popular choice for macOS.
|
117 |
+
|
118 |
+
- Install Homebrew (if not already installed)
|
119 |
|
120 |
+
- Open the Terminal.
|
121 |
|
122 |
+
- Copy and paste the following command into the Terminal and press Enter:
|
123 |
+
```bash
|
124 |
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
125 |
+
```
|
126 |
|
127 |
+
- Follow the on-screen instructions to complete the Homebrew installation.
|
128 |
+
|
129 |
+
- Install Node.js with Homebrew
|
130 |
+
- Once Homebrew is installed, update your package list by running brew update in the Terminal.
|
131 |
+
- To install Node.js, run the following command in the Terminal:
|
132 |
+
```bash
|
133 |
+
brew install node
|
134 |
+
```
|
135 |
+
- Homebrew will download and install the latest version of Node.js and npm.
|
136 |
+
- Verify the Installation
|
137 |
+
- As with the official installer method, you can verify the installation by typing node -v and npm -v in the Terminal and pressing Enter.
|
138 |
+
|
139 |
+
### Additional Configuration (Optional)
|
140 |
+
- Configure npm's Global Installation Path (if desired):
|
141 |
+
- You may want to change the default location where globally installed npm packages are stored. Follow the steps outlined in the Node.js documentation or search for guides online to configure this.
|
142 |
+
- Switch to a Different Node.js Version (if needed):
|
143 |
+
- If you need to switch between multiple Node.js versions, consider using a version manager like nvm (Node Version Manager). Follow the instructions on the nvm GitHub page to install and use it.
|
144 |
+
|
145 |
+
|
146 |
+
By following these steps, you should be able to successfully install Node.js on your system. Remember to keep your Node.js and npm versions up-to-date to take advantage of the latest features and security updates.
|
147 |
|
148 |
+
If your env has been prepared, you can
|
149 |
+
|
150 |
+
# Installation and Setup Instructions
|
151 |
+
|
152 |
+
## Installation
|
153 |
```
|
154 |
npm install
|
155 |
```
|
156 |
|
157 |
+
## Start Server
|
158 |
```
|
159 |
npm start
|
160 |
```
|
161 |
|
162 |
+
## Visit Server
|
163 |
+
```
|
164 |
+
http://localhost:8080
|
165 |
+
```
|
166 |
+
|
167 |
+
pay attention to the real port in your terminal.maybe it won`t be 8080.
|
168 |
|
169 |
+
# Config
|
170 |
+
## How to modify the request URL
|
171 |
+
- Open the file `/src/config/cgi.ts`
|
172 |
+
- Modify the value of `GET_SSE_DATA`, be like:
|
173 |
+
```
|
174 |
+
export const GET_SSE_DATA = `http://${ip}:${port}/${path}`
|
175 |
+
```
|
176 |
+
|
177 |
+
## you can also modify the proxy
|
178 |
+
- Open the file `vite.config.ts`, modify server like:
|
179 |
|
180 |
```
|
181 |
server: {
|
|
|
187 |
}
|
188 |
}
|
189 |
}
|
190 |
+
```
|
|
|
|
|
|
frontend/React/README_zh-CN.md
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Notice
|
2 |
+
问题回答过程中离开页面后再回到页面,会导致sse重连!
|
3 |
+
# 开始
|
4 |
+
## 请使用大于18.0.0的node版本
|
5 |
+
## 准备node.js开发环境
|
6 |
+
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,允许你在服务器端运行 JavaScript。以下是在 Windows、Linux 和 macOS 上安装 Node.js 的详细步骤。
|
7 |
+
|
8 |
+
### 在 Windows 上安装 Node.js
|
9 |
+
- 步骤 1: 访问 Node.js 官网
|
10 |
+
|
11 |
+
打开浏览器,访问 [Node.js](https://nodejs.org/zh-cn/download/prebuilt-installer) 官方网站。
|
12 |
+
|
13 |
+
- 步骤 2: 下载 Node.js 安装包
|
14 |
+
|
15 |
+
选择你需要的nodejs版本,设备的类型,点击下载,示例如下图:
|
16 |
+
![windows install](./windows-.png)
|
17 |
+
|
18 |
+
- 步骤 3: 安装 Node.js
|
19 |
+
|
20 |
+
双击下载的安装包开始安装。
|
21 |
+
|
22 |
+
跟随安装向导的指示进行安装。在安装过程中,你可以选择安装位置、是否将 Node.js 添加到系统 PATH 环境变量等选项。推荐选择“添加到 PATH”以便在任何地方都能通过命令行访问 Node.js。
|
23 |
+
安装完成后,点击“Finish”结束安装。
|
24 |
+
|
25 |
+
- 步骤 4: 验证安装
|
26 |
+
|
27 |
+
打开命令提示符(CMD)或 PowerShell。
|
28 |
+
输入 node -v 并回车,如果系统返回了 Node.js 的版本号,说明安装成功。
|
29 |
+
接着,输入 npm -v 并回车,npm 是 Node.js 的包管理器,如果返回了版本号,表示 npm 也已正确安装。
|
30 |
+
|
31 |
+
### 在 Linux 上安装 Node.js
|
32 |
+
注意: 由于 Linux 发行版众多,以下以 Ubuntu 为例说明,其他发行版(如 CentOS、Debian 等)的安装方式可能略有不同,可自行查询对应的安装办法。
|
33 |
+
|
34 |
+
- 步骤 1: 更新你的包管理器
|
35 |
+
|
36 |
+
打开终端。
|
37 |
+
|
38 |
+
输入 sudo apt update 并回车,以更新 Ubuntu 的包索引。
|
39 |
+
|
40 |
+
- 步骤 2: 安装 Node.js
|
41 |
+
|
42 |
+
对于 Ubuntu 18.04 及更高版本,Node.js 可以直接从 Ubuntu 的仓库中安装。
|
43 |
+
输入 sudo apt install nodejs npm 并回车。
|
44 |
+
对于旧版本的 Ubuntu 或需要安装特定版本的 Node.js,你可能需要使用如 NodeSource 这样的第三方仓库。
|
45 |
+
|
46 |
+
- 步骤 3: 验证安装
|
47 |
+
|
48 |
+
在终端中,输入 node -v 和 npm -v 来验证 Node.js 和 npm 是否已正确安装。
|
49 |
+
|
50 |
+
### 在 macOS 上安装 Node.js
|
51 |
+
|
52 |
+
#### 下载安装
|
53 |
+
- 步骤 1: 访问 Node.js 官网
|
54 |
+
|
55 |
+
打开浏览器,访问 Node.js 官方网站。
|
56 |
+
|
57 |
+
- 步骤 2: 下载 Node.js 安装包
|
58 |
+
|
59 |
+
在首页找到 macOS 对应的安装包(通常是 .pkg 文件),点击下载。
|
60 |
+
|
61 |
+
- 步骤 3: 安装 Node.js
|
62 |
+
|
63 |
+
找到下载的 .pkg 文件,双击打开。
|
64 |
+
跟随安装向导的指示进行安装。
|
65 |
+
安装完成后,点击“Close”结束安装。
|
66 |
+
|
67 |
+
- 步骤 4: 验证安装
|
68 |
+
|
69 |
+
打开终端。
|
70 |
+
|
71 |
+
输入 node -v 和 npm -v 来验证 Node.js 和 npm 是否已正确安装。
|
72 |
+
|
73 |
+
#### 使用HomeBrew安装
|
74 |
+
前提条件:确保你的macOS上已经安装了Homebrew。如果尚未安装,可以通过以下命令进行安装(以终端操作为例):
|
75 |
+
```
|
76 |
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
77 |
+
```
|
78 |
+
按照提示输入密码以确认安装。安装过程中,可能需要你同意许可协议等。
|
79 |
+
|
80 |
+
- 打开终端:
|
81 |
+
在macOS上找到并打开“终端”应用程序。
|
82 |
+
|
83 |
+
- 使用Homebrew安装Node.js:
|
84 |
+
在终端中输入以下命令来安装最新版本的Node.js
|
85 |
+
```
|
86 |
+
brew install node
|
87 |
+
```
|
88 |
+
Homebrew会自动下载Node.js的安装包,并处理相关的依赖项和安装过程。你需要等待一段时间,直到安装完成。
|
89 |
+
|
90 |
+
- 验证安装:
|
91 |
+
安装完成后,你可以通过输入以下命令来验证Node.js是否成功安装:
|
92 |
+
```
|
93 |
+
node -v
|
94 |
+
```
|
95 |
+
如果终端输出了Node.js的版本号,那么表示安装成功。同时,你也可以通过输入npm -v来验证npm(Node.js的包管理器)是否也成功安装。
|
96 |
+
|
97 |
+
完成以上步骤后,你应该能在你的 Windows、Linux 或 macOS 系统上成功安装并运行 Node.js。
|
98 |
+
|
99 |
+
### 更多
|
100 |
+
如需了解更多,可参照:https://nodejs.org/en
|
101 |
+
|
102 |
+
如环境已经准备好,跳转下一步
|
103 |
+
|
104 |
+
## 安装依赖
|
105 |
+
进入前端项目根目录
|
106 |
+
```
|
107 |
+
npm install
|
108 |
+
```
|
109 |
+
|
110 |
+
## 启动
|
111 |
+
```
|
112 |
+
npm start
|
113 |
+
```
|
114 |
+
|
115 |
+
启动成功后,界面将出现可访问的本地url
|
116 |
+
|
117 |
+
## 配置
|
118 |
+
### 接口请求配置
|
119 |
+
- 如您需要配置的服务支持跨域,可至/src/config/cgi.ts中修改请求链接,请求链接为http://ip:port/path;
|
120 |
+
- 如您需要配置的服务不支持跨域,可至vite.config.ts中配置proxy,示例如下:
|
121 |
+
|
122 |
+
```
|
123 |
+
server: {
|
124 |
+
port: 8080,
|
125 |
+
proxy: {
|
126 |
+
"/solve": {
|
127 |
+
target: "https://example.com",
|
128 |
+
changeOrigin: true,
|
129 |
+
}
|
130 |
+
}
|
131 |
+
}
|
132 |
+
```
|
133 |
+
|
134 |
+
## 知悉
|
135 |
+
- 前端服务基于react开发,如需了解react相关知识,可参考:https://react.dev/
|
frontend/React/package.json
CHANGED
@@ -5,7 +5,13 @@
|
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
"start": "vite --host --mode dev",
|
|
|
|
|
|
|
8 |
"build": "tsc && vite build",
|
|
|
|
|
|
|
9 |
"preview": "vite preview",
|
10 |
"prettier": "prettier --write ."
|
11 |
},
|
|
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
"start": "vite --host --mode dev",
|
8 |
+
"start:dev": "vite --host --mode dev",
|
9 |
+
"start:staging": "vite --host --mode staging",
|
10 |
+
"start:prod": "vite --host --mode production",
|
11 |
"build": "tsc && vite build",
|
12 |
+
"build:dev": "tsc && vite build --mode dev",
|
13 |
+
"build:staging": "tsc && vite build --mode staging",
|
14 |
+
"build:prod": "tsc && vite build --mode production",
|
15 |
"preview": "vite preview",
|
16 |
"prettier": "prettier --write ."
|
17 |
},
|
frontend/React/src/App.module.less
CHANGED
@@ -8,12 +8,10 @@
|
|
8 |
}
|
9 |
|
10 |
.content {
|
11 |
-
padding
|
12 |
width: 100%;
|
13 |
height: 100%;
|
14 |
box-sizing: border-box;
|
15 |
-
// display: flex;
|
16 |
-
// justify-content: center;
|
17 |
}
|
18 |
|
19 |
.header {
|
@@ -51,4 +49,4 @@
|
|
51 |
display: flex;
|
52 |
align-items: center;
|
53 |
}
|
54 |
-
}
|
|
|
8 |
}
|
9 |
|
10 |
.content {
|
11 |
+
padding: 64px 0 16px 0;
|
12 |
width: 100%;
|
13 |
height: 100%;
|
14 |
box-sizing: border-box;
|
|
|
|
|
15 |
}
|
16 |
|
17 |
.header {
|
|
|
49 |
display: flex;
|
50 |
align-items: center;
|
51 |
}
|
52 |
+
}
|
frontend/React/src/App.tsx
CHANGED
@@ -1,7 +1,8 @@
|
|
1 |
import style from "./App.module.less";
|
2 |
-
|
3 |
import { BrowserRouter } from "react-router-dom";
|
4 |
import RouterRoutes from "@/routes/routes";
|
|
|
5 |
|
6 |
function App() {
|
7 |
return (
|
@@ -12,6 +13,7 @@ function App() {
|
|
12 |
<img src={Logo} />
|
13 |
</div>
|
14 |
</div>
|
|
|
15 |
<div className={style.content}>
|
16 |
<RouterRoutes />
|
17 |
</div>
|
|
|
1 |
import style from "./App.module.less";
|
2 |
+
|
3 |
import { BrowserRouter } from "react-router-dom";
|
4 |
import RouterRoutes from "@/routes/routes";
|
5 |
+
import Logo from "@/assets/logo.svg";
|
6 |
|
7 |
function App() {
|
8 |
return (
|
|
|
13 |
<img src={Logo} />
|
14 |
</div>
|
15 |
</div>
|
16 |
+
|
17 |
<div className={style.content}>
|
18 |
<RouterRoutes />
|
19 |
</div>
|
frontend/React/src/components/answer/index.tsx
DELETED
@@ -1,39 +0,0 @@
|
|
1 |
-
import styles from './index.module.less';
|
2 |
-
import ReactMarkdown from "react-markdown";
|
3 |
-
import rehypeRaw from 'rehype-raw';
|
4 |
-
import { replaceStr } from '@/utils/tools';
|
5 |
-
import MindMapGraph from '../mind-map';
|
6 |
-
|
7 |
-
interface IProps {
|
8 |
-
renderData: any;
|
9 |
-
isEnd: boolean;
|
10 |
-
response: string;
|
11 |
-
draft: string;
|
12 |
-
chatIsOver: boolean;
|
13 |
-
handleHistory: (info: any) => void;
|
14 |
-
};
|
15 |
-
|
16 |
-
const Answer = ({ renderData, isEnd, chatIsOver, response = '', draft = '', handleHistory }: IProps) => {
|
17 |
-
return <div className={styles.answer}>
|
18 |
-
{
|
19 |
-
renderData?.length > 0 ? <div className={styles.inner}>
|
20 |
-
<MindMapGraph isEnd={isEnd} renderData={renderData} handleHistory={handleHistory} />
|
21 |
-
</div> : <></>
|
22 |
-
}
|
23 |
-
{
|
24 |
-
!response && <div className={styles.draft}>
|
25 |
-
{/* {!draftEnd && draft && <div className={styles.loading}>
|
26 |
-
<div></div>
|
27 |
-
<div></div>
|
28 |
-
<div></div>
|
29 |
-
</div>} */}
|
30 |
-
<ReactMarkdown rehypePlugins={[rehypeRaw]}>{replaceStr(draft)}</ReactMarkdown>
|
31 |
-
</div>
|
32 |
-
}
|
33 |
-
{response && <div className={styles.response}>
|
34 |
-
<ReactMarkdown rehypePlugins={[rehypeRaw]}>{response}</ReactMarkdown>
|
35 |
-
</div>}
|
36 |
-
</div>
|
37 |
-
};
|
38 |
-
|
39 |
-
export default Answer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frontend/React/src/components/chat-right/index.tsx
DELETED
@@ -1,310 +0,0 @@
|
|
1 |
-
import { useEffect, useState } from 'react';
|
2 |
-
import styles from './index.module.less';
|
3 |
-
import classNames from 'classnames';
|
4 |
-
import ReactMarkdown from "react-markdown";
|
5 |
-
import rehypeRaw from 'rehype-raw';
|
6 |
-
import IconFont from '@/components/iconfont';
|
7 |
-
import { replaceStr } from '@/utils/tools';
|
8 |
-
import PackIcon from '@/assets/pack-up.svg';
|
9 |
-
import { Tooltip } from 'antd';
|
10 |
-
import Loading from '../loading';
|
11 |
-
interface IProps {
|
12 |
-
nodeInfo: any;
|
13 |
-
stashInfo?: any;
|
14 |
-
historyNode?: any;
|
15 |
-
toggleRight?: () => void;
|
16 |
-
};
|
17 |
-
|
18 |
-
const ChatRight = ({ nodeInfo, stashInfo = null, historyNode = null, toggleRight }: IProps) => {
|
19 |
-
const [subQuestion, setSubQuestion] = useState('');
|
20 |
-
const [queries, setQuries] = useState<any>([]);
|
21 |
-
const [searchList, setSearchList] = useState<any>([]);
|
22 |
-
const [thought, setThought] = useState('');
|
23 |
-
const [isLoading, setIsLoading] = useState(false);
|
24 |
-
const [selectedIds, setSelectedIds] = useState([]);
|
25 |
-
const [currentStep, setCurrentStep] = useState(0);
|
26 |
-
const [conclusion, setConclusion] = useState('');
|
27 |
-
const [thinkingData, setThinking] = useState<any>(null);
|
28 |
-
const [readingData, setReading] = useState<any>(null);
|
29 |
-
// steps展开收起的信息
|
30 |
-
const [collapseInfo, setCollapseInfo] = useState<boolean[]>([true, true]);
|
31 |
-
const [currentNode, setCurrentNode] = useState<any>();
|
32 |
-
// 展开收起
|
33 |
-
const toggleCard = (index: number) => {
|
34 |
-
const arr = [...collapseInfo];
|
35 |
-
arr[index] = !arr[index];
|
36 |
-
setCollapseInfo(arr);
|
37 |
-
};
|
38 |
-
// 高亮searchList
|
39 |
-
const highLightSearchList = (ids: any) => {
|
40 |
-
// console.log('high light ids----------', ids);
|
41 |
-
const timeout = historyNode ? 0 : 3000;
|
42 |
-
setCurrentStep(1);
|
43 |
-
const highlightArr: any = [...searchList];
|
44 |
-
highlightArr.forEach((item: any) => {
|
45 |
-
if (ids.includes(Number(item.id))) {
|
46 |
-
item.highLight = true;
|
47 |
-
}
|
48 |
-
})
|
49 |
-
highlightArr.sort((item1: any, item2: any) => {
|
50 |
-
if (item1.highLight === item2.highLight) {
|
51 |
-
return 0;
|
52 |
-
}
|
53 |
-
// 如果item1是highlight,放在前面
|
54 |
-
if (item1.highLight) {
|
55 |
-
return -1;
|
56 |
-
}
|
57 |
-
// 如果item2是highlight,放在后面
|
58 |
-
return 1;
|
59 |
-
})
|
60 |
-
setSearchList(highlightArr);
|
61 |
-
setTimeout(() => {
|
62 |
-
setCurrentStep(2);
|
63 |
-
}, timeout);
|
64 |
-
};
|
65 |
-
|
66 |
-
const handleReceiveHistory = () => {
|
67 |
-
setThought(historyNode?.thinkingData?.thought);
|
68 |
-
setThinking(historyNode.thinkingData);
|
69 |
-
setConclusion(historyNode.conclusion);
|
70 |
-
setReading(historyNode.readingData);
|
71 |
-
setQuries(historyNode.queries);
|
72 |
-
setSearchList(historyNode.searchList);
|
73 |
-
setSelectedIds(historyNode.selectedIds);
|
74 |
-
setSubQuestion(historyNode.subQuestion);
|
75 |
-
setCurrentNode(2);
|
76 |
-
setCollapseInfo([true, true]);
|
77 |
-
setIsLoading(false);
|
78 |
-
};
|
79 |
-
|
80 |
-
const stashNodeInfo = () => {
|
81 |
-
// 已完成节点信息存储
|
82 |
-
const nodeInfo = {
|
83 |
-
thinkingData,
|
84 |
-
queries,
|
85 |
-
readingData,
|
86 |
-
searchList,
|
87 |
-
conclusion,
|
88 |
-
selectedIds,
|
89 |
-
subQuestion
|
90 |
-
};
|
91 |
-
const obj: any = JSON.parse(window.localStorage.getItem('nodesInfo') || '{}');
|
92 |
-
console.log('exsited history nodes-----', obj);
|
93 |
-
obj[currentNode] = nodeInfo;
|
94 |
-
window.localStorage.setItem('nodesInfo', JSON.stringify(obj));
|
95 |
-
};
|
96 |
-
|
97 |
-
const resetStatus = () => {
|
98 |
-
// 记录节点信息
|
99 |
-
stashNodeInfo();
|
100 |
-
// 初始化组件状态
|
101 |
-
console.log('reset status-------');
|
102 |
-
setThought('');
|
103 |
-
setCurrentStep(0);
|
104 |
-
setCollapseInfo([true, true]);
|
105 |
-
setSelectedIds([]);
|
106 |
-
setSearchList([]);
|
107 |
-
setThinking(null);
|
108 |
-
setReading(null);
|
109 |
-
setConclusion('');
|
110 |
-
setSubQuestion('');
|
111 |
-
};
|
112 |
-
|
113 |
-
useEffect(() => {
|
114 |
-
if (!historyNode) return;
|
115 |
-
handleReceiveHistory();
|
116 |
-
}, [historyNode]);
|
117 |
-
|
118 |
-
useEffect(() => {
|
119 |
-
if (!selectedIds.length) return;
|
120 |
-
highLightSearchList(selectedIds);
|
121 |
-
}, [selectedIds]);
|
122 |
-
|
123 |
-
useEffect(() => {
|
124 |
-
if (currentStep === 2) {
|
125 |
-
setCollapseInfo([false, false]);
|
126 |
-
} else {
|
127 |
-
setCollapseInfo([true, true]);
|
128 |
-
}
|
129 |
-
}, [conclusion, currentStep]);
|
130 |
-
|
131 |
-
useEffect(() => {
|
132 |
-
// 如果是response节点就不要处理了,跟右边没关系
|
133 |
-
if (nodeInfo?.current_node === 'response') return;
|
134 |
-
if (nodeInfo && !currentNode) {
|
135 |
-
setIsLoading(true);
|
136 |
-
}
|
137 |
-
if (nodeInfo?.current_node) {
|
138 |
-
setCurrentNode(nodeInfo?.current_node);
|
139 |
-
}
|
140 |
-
if (nodeInfo?.current_node !== currentNode) {
|
141 |
-
console.log('current node changed--------------', nodeInfo);
|
142 |
-
}
|
143 |
-
try {
|
144 |
-
if (!(nodeInfo?.current_node && nodeInfo?.response?.nodes?.[nodeInfo.current_node]?.detail)) return;
|
145 |
-
const nodeDetail = nodeInfo?.response?.nodes?.[nodeInfo.current_node]?.detail;
|
146 |
-
// console.log('nodeInfo-------', nodeDetail);
|
147 |
-
if (nodeDetail?.state === 0) {
|
148 |
-
console.log('node is end------', nodeInfo);
|
149 |
-
resetStatus();
|
150 |
-
}
|
151 |
-
if (nodeDetail?.state === 1) {
|
152 |
-
setThought(nodeDetail.response);
|
153 |
-
nodeDetail?.content && setSubQuestion(nodeDetail?.content);
|
154 |
-
// 如果返回thought的时候还没有searchlist,那就是第一步的思考
|
155 |
-
if (nodeDetail.actions?.[0]?.thought) {
|
156 |
-
setThinking({
|
157 |
-
thought: nodeDetail.actions?.[0]?.thought
|
158 |
-
})
|
159 |
-
}
|
160 |
-
// 第二步的思考
|
161 |
-
if (nodeDetail.actions?.[1]?.thought) {
|
162 |
-
setReading({
|
163 |
-
thought: nodeDetail.actions?.[1]?.thought
|
164 |
-
})
|
165 |
-
}
|
166 |
-
setIsLoading(false);
|
167 |
-
if (nodeDetail?.response && nodeDetail?.actions.length === 2) {
|
168 |
-
setConclusion(nodeDetail?.response);
|
169 |
-
}
|
170 |
-
} else {
|
171 |
-
setIsLoading(true);
|
172 |
-
}
|
173 |
-
if (nodeDetail?.actions?.length > 0) {
|
174 |
-
// 搜索词
|
175 |
-
if (nodeDetail.actions?.[0]?.args?.query?.length > 0) {
|
176 |
-
setQuries(nodeDetail.actions[0]?.args?.query);
|
177 |
-
}
|
178 |
-
// 搜索信息
|
179 |
-
if (nodeDetail.actions?.[0].result[0] && searchList.length === 0) {
|
180 |
-
const content = JSON.parse(nodeDetail.actions[0].result[0].content);
|
181 |
-
const arr: any = Object.keys(content).map(item => {
|
182 |
-
return { id: item, ...content[item] };
|
183 |
-
});
|
184 |
-
setSearchList(arr);
|
185 |
-
}
|
186 |
-
if (nodeDetail.actions?.[1]?.args?.select_ids && selectedIds.length === 0) {
|
187 |
-
setSelectedIds(nodeDetail.actions[1]?.args?.select_ids || []);
|
188 |
-
}
|
189 |
-
}
|
190 |
-
} catch (err) {
|
191 |
-
console.log('error from nodeinfo---');
|
192 |
-
}
|
193 |
-
}, [nodeInfo, currentNode]);
|
194 |
-
|
195 |
-
return <div className={styles.rightContent}>
|
196 |
-
{
|
197 |
-
subQuestion && <div className={styles.toggleIcon} onClick={toggleRight}>
|
198 |
-
<Tooltip placement="top" title="收起">
|
199 |
-
<img src={PackIcon} />
|
200 |
-
</Tooltip></div>
|
201 |
-
}
|
202 |
-
<div className={styles.titleNode}>{subQuestion}</div>
|
203 |
-
{
|
204 |
-
thought && <div className={classNames(
|
205 |
-
styles.steps
|
206 |
-
)}>
|
207 |
-
<div className={styles.title}>
|
208 |
-
<i></i>{"思考"}
|
209 |
-
<div className={styles.open} onClick={() => { toggleCard(0) }}>
|
210 |
-
<IconFont type={collapseInfo[0] ? "icon-shouqi" : "icon-xiangxiazhankai"} />
|
211 |
-
</div>
|
212 |
-
</div>
|
213 |
-
<div className={classNames(
|
214 |
-
styles.con,
|
215 |
-
!collapseInfo[0] ? styles.collapsed : ""
|
216 |
-
)}>
|
217 |
-
<div>
|
218 |
-
<ReactMarkdown rehypePlugins={[rehypeRaw]}>{replaceStr(thinkingData?.thought ? thinkingData?.thought : thought)}</ReactMarkdown>
|
219 |
-
</div>
|
220 |
-
{
|
221 |
-
queries.length > 0 && <div className={styles.query}>
|
222 |
-
<div className={styles.subTitle}><IconFont type="icon-SearchOutlined" />搜索关键词</div>
|
223 |
-
{queries.map((item: string, index: number) => (<div key={`query-item-${item}`} className={classNames(styles.queryItem, styles.fadeIn)}>
|
224 |
-
{item}
|
225 |
-
</div>))}
|
226 |
-
</div>
|
227 |
-
}
|
228 |
-
{
|
229 |
-
searchList.length > 0 && currentStep === 0 && <div className={styles.searchList}>
|
230 |
-
<div className={styles.subTitle}><IconFont type="icon-DocOutlined" />信息来源</div>
|
231 |
-
<div className={styles.scrollCon} style={(searchList.length > 5) ? { height: '300px' } : {}}>
|
232 |
-
<div className={styles.inner} style={(searchList.length > 5) ? {} : {}}>
|
233 |
-
{
|
234 |
-
searchList.map((item: any, num: number) => (
|
235 |
-
<div className={classNames(
|
236 |
-
styles.searchItem,
|
237 |
-
item.highLight ? styles.highLight : ""
|
238 |
-
)} key={`search-item-${item.url}`}>
|
239 |
-
<p className={styles.summ}>{item.id}. {item?.title}</p>
|
240 |
-
<p className={styles.url}>{item?.url}</p>
|
241 |
-
</div>
|
242 |
-
))
|
243 |
-
}
|
244 |
-
</div>
|
245 |
-
</div>
|
246 |
-
</div>
|
247 |
-
}
|
248 |
-
</div>
|
249 |
-
</div>
|
250 |
-
}
|
251 |
-
{
|
252 |
-
currentStep > 0 && <div className={classNames(
|
253 |
-
styles.steps
|
254 |
-
)}>
|
255 |
-
<div className={styles.title}>
|
256 |
-
<i></i>{"信息来源"}
|
257 |
-
<div className={styles.open} onClick={() => { toggleCard(1) }}>
|
258 |
-
<IconFont type={collapseInfo[1] ? "icon-shouqi" : "icon-xiangxiazhankai"} />
|
259 |
-
</div>
|
260 |
-
</div>
|
261 |
-
<div className={classNames(
|
262 |
-
styles.con,
|
263 |
-
!collapseInfo[1] ? styles.collapsed : ""
|
264 |
-
)}>
|
265 |
-
<div>
|
266 |
-
<ReactMarkdown rehypePlugins={[rehypeRaw]}>{replaceStr(readingData?.thought ? readingData?.thought : thought)}</ReactMarkdown>
|
267 |
-
</div>
|
268 |
-
{
|
269 |
-
searchList.length > 0 && <div className={styles.searchList}>
|
270 |
-
<div className={styles.subTitle}><IconFont type="icon-DocOutlined" />信息来源</div>
|
271 |
-
<div className={styles.scrollCon} style={(searchList.length > 5) ? { height: '300px' } : {}}>
|
272 |
-
<div className={styles.inner} style={(searchList.length > 5) ? {} : {}}>
|
273 |
-
{
|
274 |
-
searchList.map((item: any, num: number) => (
|
275 |
-
<div className={classNames(
|
276 |
-
styles.searchItem,
|
277 |
-
item.highLight ? styles.highLight : ""
|
278 |
-
)} key={`search-item-${item.url}`}>
|
279 |
-
<p className={styles.summ}>{item.id}. {item?.title}</p>
|
280 |
-
<p className={styles.url}>{item?.url}</p>
|
281 |
-
</div>
|
282 |
-
))
|
283 |
-
}
|
284 |
-
</div>
|
285 |
-
</div>
|
286 |
-
</div>
|
287 |
-
}
|
288 |
-
</div>
|
289 |
-
</div>
|
290 |
-
}
|
291 |
-
{
|
292 |
-
currentStep === 2 && <div className={classNames(
|
293 |
-
styles.steps
|
294 |
-
)}>
|
295 |
-
<div className={styles.title}>
|
296 |
-
<i></i>{"信息整合"}
|
297 |
-
</div>
|
298 |
-
<div className={styles.con}>
|
299 |
-
<ReactMarkdown rehypePlugins={[rehypeRaw]}>{replaceStr(conclusion)}</ReactMarkdown>
|
300 |
-
</div>
|
301 |
-
</div>
|
302 |
-
}
|
303 |
-
|
304 |
-
{
|
305 |
-
isLoading && <Loading />
|
306 |
-
}
|
307 |
-
</div>
|
308 |
-
};
|
309 |
-
|
310 |
-
export default ChatRight;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frontend/React/src/components/mind-map-item/index.module.less
DELETED
@@ -1,117 +0,0 @@
|
|
1 |
-
article {
|
2 |
-
padding: 6px 16px;
|
3 |
-
border-radius: 8px;
|
4 |
-
height: 38px;
|
5 |
-
border: 1px solid transparent;
|
6 |
-
background: #FFF;
|
7 |
-
color: #121316;
|
8 |
-
text-align: center;
|
9 |
-
font-size: 14px;
|
10 |
-
line-height: 24px;
|
11 |
-
position: relative;
|
12 |
-
box-sizing: border-box;
|
13 |
-
|
14 |
-
&.loading {
|
15 |
-
line-height: 20px;
|
16 |
-
border-radius: 8px;
|
17 |
-
overflow: hidden;
|
18 |
-
border: 1px solid transparent;
|
19 |
-
padding: 4px;
|
20 |
-
|
21 |
-
span {
|
22 |
-
color: #2126C0;
|
23 |
-
background-color: #fff;
|
24 |
-
border-radius: 4px;
|
25 |
-
line-height: 24px;
|
26 |
-
padding: 2px 12px;
|
27 |
-
}
|
28 |
-
|
29 |
-
.looping {
|
30 |
-
--border-width: 4px;
|
31 |
-
--follow-panel-linear-border: linear-gradient(91deg,
|
32 |
-
#5551FF 0.58%,
|
33 |
-
#FF87DE 100.36%);
|
34 |
-
|
35 |
-
position: absolute;
|
36 |
-
top: 0;
|
37 |
-
left: 0;
|
38 |
-
width: calc(100% + var(--border-width) * 2 - 8px);
|
39 |
-
height: calc(100%);
|
40 |
-
background: var(--follow-panel-linear-border);
|
41 |
-
background-size: 300% 300%;
|
42 |
-
background-position: 0 50%;
|
43 |
-
animation: moveGradient 4s alternate infinite;
|
44 |
-
}
|
45 |
-
}
|
46 |
-
|
47 |
-
&.disabled {
|
48 |
-
border-radius: 8px;
|
49 |
-
border: 1px solid #D7D8DD;
|
50 |
-
color: rgba(18, 19, 22, 0.35);
|
51 |
-
}
|
52 |
-
|
53 |
-
&.finished {
|
54 |
-
cursor: pointer;
|
55 |
-
border: 1px solid #2126C0;
|
56 |
-
|
57 |
-
.finishDot {
|
58 |
-
position: absolute;
|
59 |
-
top: 6px;
|
60 |
-
right: 6px;
|
61 |
-
width: 6px;
|
62 |
-
height: 6px;
|
63 |
-
background-color: #C9C0FE;
|
64 |
-
border-radius: 50%;
|
65 |
-
}
|
66 |
-
}
|
67 |
-
|
68 |
-
&.init {
|
69 |
-
border: 1px solid transparent;
|
70 |
-
cursor: auto;
|
71 |
-
}
|
72 |
-
|
73 |
-
span {
|
74 |
-
display: block;
|
75 |
-
white-space: nowrap;
|
76 |
-
max-width: 160px;
|
77 |
-
overflow: hidden;
|
78 |
-
text-overflow: ellipsis;
|
79 |
-
position: relative;
|
80 |
-
z-index: 20;
|
81 |
-
}
|
82 |
-
|
83 |
-
span.status {
|
84 |
-
color: #4082FE;
|
85 |
-
}
|
86 |
-
|
87 |
-
}
|
88 |
-
|
89 |
-
ul.onlyone {
|
90 |
-
&:before {
|
91 |
-
opacity: 0;
|
92 |
-
}
|
93 |
-
|
94 |
-
>li {
|
95 |
-
margin-left: 0px;
|
96 |
-
}
|
97 |
-
|
98 |
-
&>li:after {
|
99 |
-
opacity: 0;
|
100 |
-
}
|
101 |
-
|
102 |
-
&>li:before {
|
103 |
-
// left: 0;
|
104 |
-
}
|
105 |
-
}
|
106 |
-
|
107 |
-
.endLine {
|
108 |
-
border-bottom: 1px solid #D7D8DD;
|
109 |
-
width: 3000px;
|
110 |
-
transition: width 1s ease-in-out;
|
111 |
-
}
|
112 |
-
|
113 |
-
@keyframes moveGradient {
|
114 |
-
50% {
|
115 |
-
background-position: 100% 50%;
|
116 |
-
}
|
117 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frontend/React/src/components/mind-map-item/index.tsx
DELETED
@@ -1,50 +0,0 @@
|
|
1 |
-
import { message } from 'antd';
|
2 |
-
import styles from './index.module.less';
|
3 |
-
import classNames from 'classnames';
|
4 |
-
|
5 |
-
// 递归组件用于渲染mindMap中的节点
|
6 |
-
const MindMapItem = ({ item, isEnd, selectNode }: any) => {
|
7 |
-
// 递归渲染子节点
|
8 |
-
const renderChildren = () => {
|
9 |
-
if (item.children && item.children.length > 0) {
|
10 |
-
return (
|
11 |
-
<ul className={item.children.length === 1 ? styles.onlyone : ''}>
|
12 |
-
{item.children.map((child: any) => (
|
13 |
-
<MindMapItem key={child.name} item={child} isEnd={isEnd} selectNode={selectNode} />
|
14 |
-
))}
|
15 |
-
</ul>
|
16 |
-
);
|
17 |
-
}
|
18 |
-
return null;
|
19 |
-
};
|
20 |
-
|
21 |
-
const handleClick = () => {
|
22 |
-
if (item.state !== 3) {
|
23 |
-
message.warning('思考中,请稍后');
|
24 |
-
return;
|
25 |
-
}
|
26 |
-
selectNode(item.name);
|
27 |
-
};
|
28 |
-
|
29 |
-
return (
|
30 |
-
<li>
|
31 |
-
<article
|
32 |
-
onClick={handleClick}
|
33 |
-
className={
|
34 |
-
classNames(
|
35 |
-
item.state === 1 ? styles.loading : item.state === 2 ? styles.disabled : item.state === 3 ? styles.finished : "",
|
36 |
-
item.id === 0 ? styles.init : ''
|
37 |
-
)}>
|
38 |
-
<span>{item.name}</span>
|
39 |
-
{item.state === 1 && <div className={styles.looping}></div>}
|
40 |
-
{item.id !== 0 && <div className={styles.finishDot}></div>}
|
41 |
-
</article>
|
42 |
-
{item.children.length > 0 && renderChildren()}
|
43 |
-
{
|
44 |
-
isEnd && item.children?.length === 0 && <div className={classNames(styles.endLine, "endline")}></div>
|
45 |
-
}
|
46 |
-
</li>
|
47 |
-
);
|
48 |
-
};
|
49 |
-
|
50 |
-
export default MindMapItem;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frontend/React/src/config/cgi.ts
DELETED
@@ -1,2 +0,0 @@
|
|
1 |
-
export const mode = import.meta.env.MODE;
|
2 |
-
export const GET_SSE_DATA = '/solve';
|
|
|
|
|
|
frontend/React/src/global.d.ts
CHANGED
@@ -1 +1 @@
|
|
1 |
-
declare module 'event-source-polyfill';
|
|
|
1 |
+
declare module 'event-source-polyfill';
|
frontend/React/src/index.less
CHANGED
@@ -8,8 +8,6 @@ html,
|
|
8 |
font-family: "PingFang SC";
|
9 |
font-size: 14px;
|
10 |
line-height: 21px;
|
11 |
-
min-width: 1280px;
|
12 |
-
overflow-x: auto;
|
13 |
}
|
14 |
|
15 |
#global__message-container {
|
@@ -61,4 +59,4 @@ li> :nth-last-child(1).f {
|
|
61 |
line-height: 14px;
|
62 |
position: relative;
|
63 |
top: -2px;
|
64 |
-
}
|
|
|
8 |
font-family: "PingFang SC";
|
9 |
font-size: 14px;
|
10 |
line-height: 21px;
|
|
|
|
|
11 |
}
|
12 |
|
13 |
#global__message-container {
|
|
|
59 |
line-height: 14px;
|
60 |
position: relative;
|
61 |
top: -2px;
|
62 |
+
}
|
frontend/React/src/pages/mindsearch/assets/bookmark-icon.svg
ADDED
frontend/React/src/pages/mindsearch/assets/empty-chat-right.svg
ADDED
frontend/React/src/pages/mindsearch/assets/fold-icon.svg
ADDED
frontend/React/src/pages/mindsearch/assets/logo.svg
ADDED
frontend/React/src/pages/mindsearch/assets/logo1.svg
ADDED
frontend/React/src/pages/mindsearch/assets/mindsearch-avatar.svg
ADDED
frontend/React/src/pages/mindsearch/assets/pack-up-disabled.svg
ADDED
frontend/React/src/pages/mindsearch/assets/pack-up.svg
ADDED
frontend/React/src/pages/mindsearch/assets/sendIcon.svg
ADDED
frontend/React/src/pages/mindsearch/assets/think-progress-icon.svg
ADDED
frontend/React/src/pages/mindsearch/assets/unflod-icon.svg
ADDED
frontend/React/src/pages/mindsearch/components/answer/index.module.less
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.answer {
|
2 |
+
display: flex;
|
3 |
+
justify-content: flex-start;
|
4 |
+
align-items: flex-start;
|
5 |
+
width: 100%;
|
6 |
+
|
7 |
+
.avatar {
|
8 |
+
width: 32px;
|
9 |
+
height: 32px;
|
10 |
+
margin-right: 16px;
|
11 |
+
border-radius: 50%;
|
12 |
+
flex-shrink: 0;
|
13 |
+
|
14 |
+
img {
|
15 |
+
width: 100%;
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
19 |
+
.reaponseAarea {
|
20 |
+
display: flex;
|
21 |
+
flex-direction: column;
|
22 |
+
width: calc(100% - 48px);
|
23 |
+
background-color: #F4F5F9;
|
24 |
+
padding: 12px 16px;
|
25 |
+
border-radius: 16px;
|
26 |
+
overflow-x: hidden;
|
27 |
+
}
|
28 |
+
|
29 |
+
.inner {
|
30 |
+
width: 100%;
|
31 |
+
overflow-x: hidden;
|
32 |
+
background-color: #fff;
|
33 |
+
border-radius: 16px;
|
34 |
+
border: 1px solid var(----line-2, #EBECF0);
|
35 |
+
box-sizing: border-box;
|
36 |
+
transition: all 0.5s ease;
|
37 |
+
margin-bottom: 16px;
|
38 |
+
position: relative;
|
39 |
+
}
|
40 |
+
|
41 |
+
.graphIcon {
|
42 |
+
padding: 2px 8px;
|
43 |
+
display: flex;
|
44 |
+
justify-content: center;
|
45 |
+
align-items: center;
|
46 |
+
border-radius: 8px;
|
47 |
+
border: 1px solid var(----line-2, #EBECF0);
|
48 |
+
background: var(---fill-0, #FFF);
|
49 |
+
color: #121316CC;
|
50 |
+
font-size: 14px;
|
51 |
+
line-height: 24px;
|
52 |
+
cursor: pointer;
|
53 |
+
|
54 |
+
svg {
|
55 |
+
margin-left: 4px;
|
56 |
+
}
|
57 |
+
|
58 |
+
&:hover {
|
59 |
+
background-color: #D7D8DD;
|
60 |
+
color: #121316CC;
|
61 |
+
|
62 |
+
svg path {
|
63 |
+
fill: #121316CC;
|
64 |
+
}
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
.showGraph {
|
69 |
+
width: 118px;
|
70 |
+
margin-bottom: 16px;
|
71 |
+
border-radius: 8px;
|
72 |
+
border: 1px solid var(----line-2, #EBECF0);
|
73 |
+
background: var(---fill-0, #FFF);
|
74 |
+
}
|
75 |
+
|
76 |
+
.closeGraph {
|
77 |
+
position: absolute;
|
78 |
+
right: 12px;
|
79 |
+
bottom: 12px;
|
80 |
+
}
|
81 |
+
|
82 |
+
.reaponse {
|
83 |
+
color: #121316;
|
84 |
+
font-size: 14px;
|
85 |
+
line-height: 24px;
|
86 |
+
padding: 18px 42px;
|
87 |
+
}
|
88 |
+
|
89 |
+
// h3 {
|
90 |
+
// font-size: 24px;
|
91 |
+
// font-weight: 600;
|
92 |
+
// line-height: 36px;
|
93 |
+
// margin: 0 0 16px 0;
|
94 |
+
// }
|
95 |
+
|
96 |
+
// h4 {
|
97 |
+
// font-size: 20px;
|
98 |
+
// font-weight: 600;
|
99 |
+
// line-height: 30px;
|
100 |
+
// margin: 0 0 8px 0;
|
101 |
+
// }
|
102 |
+
}
|
103 |
+
|
104 |
+
.draft {
|
105 |
+
width: 100%;
|
106 |
+
white-space: wrap;
|
107 |
+
display: flex;
|
108 |
+
justify-content: flex-start;
|
109 |
+
align-items: flex-start;
|
110 |
+
}
|
frontend/React/src/pages/mindsearch/components/answer/index.tsx
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import styles from './index.module.less';
|
2 |
+
import MindMapGraph from '../mind-map';
|
3 |
+
import { MindsearchContext } from '../../provider/context';
|
4 |
+
import MindSearchAvatar from '../../assets/mindsearch-avatar.svg';
|
5 |
+
import CustomMarkdown from '../custom-markdown';
|
6 |
+
import { useState, useEffect, useContext } from 'react';
|
7 |
+
import classNames from 'classnames';
|
8 |
+
|
9 |
+
interface IProps {
|
10 |
+
adjList: any;
|
11 |
+
isEnd: boolean;
|
12 |
+
response: string;
|
13 |
+
refList: any;
|
14 |
+
listId: number;
|
15 |
+
question: string;
|
16 |
+
handleNodeClick: (info: any, idx: number) => void;
|
17 |
+
}
|
18 |
+
|
19 |
+
const Answer = ({ refList = null, adjList, isEnd, response = '', listId, handleNodeClick, question = '' }: IProps) => {
|
20 |
+
const { chatIsOver } = useContext(MindsearchContext);
|
21 |
+
const [showGraph, setShowGraph] = useState(true);
|
22 |
+
// 整体的渲染树
|
23 |
+
const [renderData, setRenderData] = useState<any[]>([]);
|
24 |
+
|
25 |
+
const toggleGraph = () => {
|
26 |
+
setShowGraph(!showGraph);
|
27 |
+
};
|
28 |
+
|
29 |
+
const handleClick = (node: string) => {
|
30 |
+
handleNodeClick(node, listId);
|
31 |
+
};
|
32 |
+
|
33 |
+
const generateMapData = (arr: []) => {
|
34 |
+
const tempArr: any[] = arr.map((item: { name: string; id: number; state: number }) => {
|
35 |
+
if (item.name && adjList[item.name]) {
|
36 |
+
return {
|
37 |
+
...item,
|
38 |
+
children: generateMapData(adjList?.[item.name]),
|
39 |
+
};
|
40 |
+
}
|
41 |
+
});
|
42 |
+
return tempArr;
|
43 |
+
};
|
44 |
+
|
45 |
+
const convertTreeData = () => {
|
46 |
+
const root: any = {
|
47 |
+
id: 0,
|
48 |
+
name: '原始问题',
|
49 |
+
state: 3,
|
50 |
+
children: generateMapData(adjList?.root || []),
|
51 |
+
};
|
52 |
+
|
53 |
+
// 返回包含根节点的数组
|
54 |
+
// console.log('renderData-----------', [root]);
|
55 |
+
setRenderData([root]);
|
56 |
+
};
|
57 |
+
|
58 |
+
useEffect(() => {
|
59 |
+
if (!adjList || Object.keys(adjList)?.length < 2) {
|
60 |
+
setRenderData([]);
|
61 |
+
return;
|
62 |
+
};
|
63 |
+
convertTreeData();
|
64 |
+
}, [adjList]);
|
65 |
+
|
66 |
+
return <div className={styles.answer}>
|
67 |
+
<div className={styles.avatar}>
|
68 |
+
<img src={MindSearchAvatar} alt="mindsearch-avatar" />
|
69 |
+
</div>
|
70 |
+
<div className={styles.reaponseAarea}>
|
71 |
+
{
|
72 |
+
showGraph ? <>
|
73 |
+
{
|
74 |
+
(renderData?.length > 0) && <div className={styles.inner}>
|
75 |
+
<MindMapGraph
|
76 |
+
listId={listId}
|
77 |
+
isEnd={isEnd}
|
78 |
+
renderData={renderData}
|
79 |
+
handleNodeClick={handleClick}
|
80 |
+
key={`graph-${question}`}
|
81 |
+
/>
|
82 |
+
<div className={classNames(styles.graphIcon, styles.closeGraph)} onClick={toggleGraph}>
|
83 |
+
收起
|
84 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
85 |
+
<path fillRule="evenodd" clipRule="evenodd" d="M2.66667 2C2.29848 2 2 2.29848 2 2.66667V6C2 6.36819 2.29848 6.66667 2.66667 6.66667C3.03486 6.66667 3.33333 6.36819 3.33333 6V3.33333H6C6.36819 3.33333 6.66667 3.03486 6.66667 2.66667C6.66667 2.29848 6.36819 2 6 2H2.66667ZM13.3333 14C13.7015 14 14 13.7015 14 13.3333V10C14 9.63181 13.7015 9.33333 13.3333 9.33333C12.9651 9.33333 12.6667 9.63181 12.6667 10V12.6667H10C9.63181 12.6667 9.33333 12.9651 9.33333 13.3333C9.33333 13.7015 9.63181 14 10 14H13.3333ZM6.25519 9.38392C6.17658 9.35132 6.09039 9.33333 6 9.33333H2.66667C2.29848 9.33333 2 9.63181 2 10C2 10.3682 2.29848 10.6667 2.66667 10.6667H4.39052L2.19526 12.8619C1.93491 13.1223 1.93491 13.5444 2.19526 13.8047C2.45561 14.0651 2.87772 14.0651 3.13807 13.8047L5.33333 11.6095L5.33333 13.3333C5.33333 13.7015 5.63181 14 6 14C6.36819 14 6.66667 13.7015 6.66667 13.3333V10C6.66667 9.81812 6.59383 9.65325 6.47574 9.53297L6.46703 9.52426C6.40414 9.46249 6.33203 9.41571 6.25519 9.38392ZM10.6667 2.66667C10.6667 2.29848 10.3682 2 10 2C9.63181 2 9.33333 2.29848 9.33333 2.66667V6C9.33333 6.36819 9.63181 6.66667 10 6.66667L13.3333 6.66667C13.7015 6.66667 14 6.36819 14 6C14 5.63181 13.7015 5.33333 13.3333 5.33333L11.6095 5.33333L13.8047 3.13807C14.0651 2.87772 14.0651 2.45561 13.8047 2.19526C13.5444 1.93491 13.1223 1.93491 12.8619 2.19526L10.6667 4.39053V2.66667Z" fill="#121316" fillOpacity="0.8" />
|
86 |
+
</svg>
|
87 |
+
</div>
|
88 |
+
</div>
|
89 |
+
}
|
90 |
+
</>
|
91 |
+
: <div className={classNames(styles.graphIcon, styles.showGraph)} onClick={toggleGraph}>
|
92 |
+
查看思考节点
|
93 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
94 |
+
<path fillRule="evenodd" clipRule="evenodd" d="M2.66667 2C2.29848 2 2 2.29848 2 2.66667V6C2 6.36819 2.29848 6.66667 2.66667 6.66667C3.03486 6.66667 3.33333 6.36819 3.33333 6V3.33333H6C6.36819 3.33333 6.66667 3.03486 6.66667 2.66667C6.66667 2.29848 6.36819 2 6 2H2.66667ZM13.3333 14C13.7015 14 14 13.7015 14 13.3333V10C14 9.63181 13.7015 9.33333 13.3333 9.33333C12.9651 9.33333 12.6667 9.63181 12.6667 10V12.6667H10C9.63181 12.6667 9.33333 12.9651 9.33333 13.3333C9.33333 13.7015 9.63181 14 10 14H13.3333ZM6.25519 9.38392C6.17658 9.35132 6.09039 9.33333 6 9.33333H2.66667C2.29848 9.33333 2 9.63181 2 10C2 10.3682 2.29848 10.6667 2.66667 10.6667H4.39052L2.19526 12.8619C1.93491 13.1223 1.93491 13.5444 2.19526 13.8047C2.45561 14.0651 2.87772 14.0651 3.13807 13.8047L5.33333 11.6095L5.33333 13.3333C5.33333 13.7015 5.63181 14 6 14C6.36819 14 6.66667 13.7015 6.66667 13.3333V10C6.66667 9.81812 6.59383 9.65325 6.47574 9.53297L6.46703 9.52426C6.40414 9.46249 6.33203 9.41571 6.25519 9.38392ZM10.6667 2.66667C10.6667 2.29848 10.3682 2 10 2C9.63181 2 9.33333 2.29848 9.33333 2.66667V6C9.33333 6.36819 9.63181 6.66667 10 6.66667L13.3333 6.66667C13.7015 6.66667 14 6.36819 14 6C14 5.63181 13.7015 5.33333 13.3333 5.33333L11.6095 5.33333L13.8047 3.13807C14.0651 2.87772 14.0651 2.45561 13.8047 2.19526C13.5444 1.93491 13.1223 1.93491 12.8619 2.19526L10.6667 4.39053V2.66667Z" fill="#121316" fillOpacity="0.8" />
|
95 |
+
</svg>
|
96 |
+
</div>
|
97 |
+
}
|
98 |
+
|
99 |
+
{response && (
|
100 |
+
<div className={styles.response}>
|
101 |
+
<CustomMarkdown source={response} refList={chatIsOver ? refList : null} quoType="merge" />
|
102 |
+
</div>
|
103 |
+
)}
|
104 |
+
</div>
|
105 |
+
</div>
|
106 |
+
};
|
107 |
+
|
108 |
+
export default Answer;
|
frontend/React/src/{components/answer → pages/mindsearch/components/answer/loading-animation}/index.module.less
RENAMED
@@ -1,79 +1,10 @@
|
|
1 |
-
.
|
2 |
-
|
3 |
-
background: rgba(33, 38, 192, 0.10);
|
4 |
-
padding: 12px;
|
5 |
-
|
6 |
-
.inner {
|
7 |
-
width: 100%;
|
8 |
-
background-color: #fff;
|
9 |
-
border-radius: 4px;
|
10 |
-
padding: 8px;
|
11 |
-
box-sizing: border-box;
|
12 |
-
transition: all 0.5s ease;
|
13 |
-
margin-bottom: 18px;
|
14 |
-
}
|
15 |
-
|
16 |
-
|
17 |
-
.response {
|
18 |
-
color: #121316;
|
19 |
-
font-size: 14px;
|
20 |
-
line-height: 24px;
|
21 |
-
padding: 18px 42px;
|
22 |
-
|
23 |
-
h3 {
|
24 |
-
font-size: 24px;
|
25 |
-
font-weight: 600;
|
26 |
-
line-height: 36px;
|
27 |
-
margin: 0 0 16px 0;
|
28 |
-
}
|
29 |
-
|
30 |
-
h4 {
|
31 |
-
font-size: 20px;
|
32 |
-
font-weight: 600;
|
33 |
-
line-height: 30px;
|
34 |
-
margin: 0 0 8px 0;
|
35 |
-
}
|
36 |
-
|
37 |
-
p {
|
38 |
-
color: rgba(18, 19, 22, 0.80);
|
39 |
-
font-size: 16px;
|
40 |
-
font-weight: 400;
|
41 |
-
line-height: 28px;
|
42 |
-
margin: 0 0 16px 0;
|
43 |
-
}
|
44 |
-
|
45 |
-
ul {
|
46 |
-
margin-bottom: 8px;
|
47 |
-
padding-left: 22px;
|
48 |
-
}
|
49 |
-
|
50 |
-
li {
|
51 |
-
color: rgba(18, 19, 22, 0.80);
|
52 |
-
font-size: 16px;
|
53 |
-
font-weight: 400;
|
54 |
-
line-height: 28px;
|
55 |
-
|
56 |
-
p {
|
57 |
-
margin-bottom: 4px;
|
58 |
-
}
|
59 |
-
}
|
60 |
-
}
|
61 |
-
}
|
62 |
-
|
63 |
-
.draft {
|
64 |
-
width: 100%;
|
65 |
-
white-space: wrap;
|
66 |
-
// display: flex;
|
67 |
-
// justify-content: flex-start;
|
68 |
-
// align-items: flex-start;
|
69 |
-
|
70 |
-
.loading,
|
71 |
-
.loading>div {
|
72 |
position: relative;
|
73 |
box-sizing: border-box;
|
74 |
-
|
75 |
|
76 |
-
|
77 |
display: flex;
|
78 |
justify-content: center;
|
79 |
align-items: center;
|
@@ -85,32 +16,32 @@
|
|
85 |
border-radius: 50%;
|
86 |
margin-right: 3px;
|
87 |
flex-shrink: 0;
|
88 |
-
|
|
|
89 |
|
90 |
-
|
91 |
display: inline-block;
|
92 |
float: none;
|
93 |
background-color: currentColor;
|
94 |
border: 0 solid currentColor;
|
95 |
-
|
96 |
|
97 |
-
|
98 |
animation-delay: -200ms;
|
99 |
-
|
100 |
|
101 |
-
|
102 |
animation-delay: -100ms;
|
103 |
-
|
104 |
|
105 |
-
|
106 |
animation-delay: 0ms;
|
107 |
-
|
108 |
|
109 |
-
|
110 |
width: 3px;
|
111 |
height: 3px;
|
112 |
margin: 2px 1px;
|
113 |
border-radius: 100%;
|
114 |
animation: ball-pulse 1s ease infinite;
|
115 |
-
|
116 |
-
}
|
|
|
1 |
+
.loading,
|
2 |
+
.loading > div {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
position: relative;
|
4 |
box-sizing: border-box;
|
5 |
+
}
|
6 |
|
7 |
+
.loading {
|
8 |
display: flex;
|
9 |
justify-content: center;
|
10 |
align-items: center;
|
|
|
16 |
border-radius: 50%;
|
17 |
margin-right: 3px;
|
18 |
flex-shrink: 0;
|
19 |
+
margin-top: 4px;
|
20 |
+
}
|
21 |
|
22 |
+
.loading > div {
|
23 |
display: inline-block;
|
24 |
float: none;
|
25 |
background-color: currentColor;
|
26 |
border: 0 solid currentColor;
|
27 |
+
}
|
28 |
|
29 |
+
.loading > div:nth-child(1) {
|
30 |
animation-delay: -200ms;
|
31 |
+
}
|
32 |
|
33 |
+
.loading > div:nth-child(2) {
|
34 |
animation-delay: -100ms;
|
35 |
+
}
|
36 |
|
37 |
+
.loading > div:nth-child(3) {
|
38 |
animation-delay: 0ms;
|
39 |
+
}
|
40 |
|
41 |
+
.loading > div {
|
42 |
width: 3px;
|
43 |
height: 3px;
|
44 |
margin: 2px 1px;
|
45 |
border-radius: 100%;
|
46 |
animation: ball-pulse 1s ease infinite;
|
47 |
+
}
|
|
frontend/React/src/pages/mindsearch/components/answer/loading-animation/index.tsx
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import styles from './index.module.less';
|
2 |
+
|
3 |
+
const LoadingAnimation = () => {
|
4 |
+
return (
|
5 |
+
<div className={styles.loading}>
|
6 |
+
<div />
|
7 |
+
<div />
|
8 |
+
<div />
|
9 |
+
</div>
|
10 |
+
);
|
11 |
+
};
|
12 |
+
|
13 |
+
export default LoadingAnimation;
|
frontend/React/src/pages/mindsearch/components/chat-right/components/empty-placeholder/index.module.less
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.emptyDiv {
|
2 |
+
width: 280px;
|
3 |
+
height: 100%;
|
4 |
+
margin: auto;
|
5 |
+
display: flex;
|
6 |
+
justify-content: center;
|
7 |
+
align-items: center;
|
8 |
+
flex-direction: column;
|
9 |
+
|
10 |
+
.pic {
|
11 |
+
margin-bottom: 8px;
|
12 |
+
}
|
13 |
+
|
14 |
+
p {
|
15 |
+
color: var(--80-text-4, rgba(18, 19, 22, 0.80));
|
16 |
+
text-align: center;
|
17 |
+
font-feature-settings: 'liga' off, 'clig' off;
|
18 |
+
|
19 |
+
/* 段落正文/常规text-1-paragraph-regular */
|
20 |
+
font-family: "PingFang SC";
|
21 |
+
font-size: 14px;
|
22 |
+
font-style: normal;
|
23 |
+
font-weight: 400;
|
24 |
+
line-height: 24px;
|
25 |
+
/* 171.429% */
|
26 |
+
}
|
27 |
+
}
|
frontend/React/src/pages/mindsearch/components/chat-right/components/empty-placeholder/index.tsx
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import styles from './index.module.less';
|
2 |
+
import EmptyRightChatImg from '../../../../assets/empty-chat-right.svg';
|
3 |
+
|
4 |
+
const EmptyPlaceHolder = () => {
|
5 |
+
return <>
|
6 |
+
<div className={styles.emptyDiv}>
|
7 |
+
<div className={styles.pic}>
|
8 |
+
<img src={EmptyRightChatImg} />
|
9 |
+
</div>
|
10 |
+
<p>
|
11 |
+
请在节点图中选择节点后查看哦~
|
12 |
+
</p>
|
13 |
+
</div>
|
14 |
+
</>
|
15 |
+
};
|
16 |
+
|
17 |
+
export default EmptyPlaceHolder;
|
frontend/React/src/pages/mindsearch/components/chat-right/components/query-item/index.module.less
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.query {
|
2 |
+
&-Item {
|
3 |
+
display: inline-flex;
|
4 |
+
padding: 6px 12px 6px 0;
|
5 |
+
margin-right: 12px;
|
6 |
+
margin-bottom: 8px;
|
7 |
+
color: rgba(18, 19, 22, 0.8);
|
8 |
+
font-size: 14px;
|
9 |
+
line-height: 24px;
|
10 |
+
box-sizing: border-box;
|
11 |
+
overflow: hidden;
|
12 |
+
position: relative;
|
13 |
+
|
14 |
+
&:last-child {
|
15 |
+
&::after {
|
16 |
+
display: none;
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
&::after {
|
21 |
+
position: absolute;
|
22 |
+
right: 0;
|
23 |
+
top: 10px;
|
24 |
+
width: 1px;
|
25 |
+
height: 16px;
|
26 |
+
border-right: 1px solid #ebecf0;
|
27 |
+
content: '';
|
28 |
+
}
|
29 |
+
}
|
30 |
+
}
|
frontend/React/src/pages/mindsearch/components/chat-right/components/query-item/index.tsx
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import classNames from 'classnames';
|
2 |
+
import styles from './index.module.less';
|
3 |
+
|
4 |
+
interface IQueryItemProps {
|
5 |
+
item: string;
|
6 |
+
}
|
7 |
+
const QueryItem = ({ item }: IQueryItemProps) => {
|
8 |
+
return <div className={classNames(styles.queryItem, styles.fadeIn)}>{item}</div>;
|
9 |
+
};
|
10 |
+
|
11 |
+
export default QueryItem;
|
frontend/React/src/pages/mindsearch/components/chat-right/components/search-item/index.module.less
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.searchItem {
|
2 |
+
border-radius: 12px;
|
3 |
+
margin-bottom: 4px;
|
4 |
+
padding: 8px;
|
5 |
+
transition: all 0.5s ease-in-out;
|
6 |
+
display: flex;
|
7 |
+
justify-content: flex-start;
|
8 |
+
align-items: flex-start;
|
9 |
+
cursor: pointer;
|
10 |
+
|
11 |
+
.inner {
|
12 |
+
width: 100%;
|
13 |
+
overflow: hidden;
|
14 |
+
}
|
15 |
+
|
16 |
+
&:hover {
|
17 |
+
background-color: #ebecf0;
|
18 |
+
}
|
19 |
+
|
20 |
+
.num {
|
21 |
+
color: var(--60-text-3, rgba(18, 19, 22, 0.6));
|
22 |
+
font-size: 12px;
|
23 |
+
font-weight: 600;
|
24 |
+
line-height: 18px;
|
25 |
+
margin-right: 7px;
|
26 |
+
}
|
27 |
+
|
28 |
+
p {
|
29 |
+
white-space: wrap;
|
30 |
+
max-width: 95%;
|
31 |
+
overflow: hidden;
|
32 |
+
text-overflow: ellipsis;
|
33 |
+
margin: 0 !important;
|
34 |
+
}
|
35 |
+
|
36 |
+
// .origin {
|
37 |
+
// display: flex;
|
38 |
+
// justify-content: flex-start;
|
39 |
+
// align-items: center;
|
40 |
+
// margin-bottom: 2px;
|
41 |
+
|
42 |
+
// .icon {
|
43 |
+
// width: 16px;
|
44 |
+
// height: 16px;
|
45 |
+
// border-radius: 4px;
|
46 |
+
// background-color: #6f7f9b;
|
47 |
+
// margin-right: 2px;
|
48 |
+
// }
|
49 |
+
|
50 |
+
// span {
|
51 |
+
// color: var(--60-text-3, rgba(18, 19, 22, 0.60));
|
52 |
+
// font-size: 12px;
|
53 |
+
// font-weight: 400;
|
54 |
+
// line-height: 18px;
|
55 |
+
// }
|
56 |
+
// }
|
57 |
+
|
58 |
+
p.title {
|
59 |
+
overflow: hidden;
|
60 |
+
color: var(---Brand1-5, #3477eb);
|
61 |
+
text-overflow: ellipsis;
|
62 |
+
font-size: 14px;
|
63 |
+
line-height: 24px;
|
64 |
+
margin-bottom: 2px;
|
65 |
+
font-weight: normal;
|
66 |
+
|
67 |
+
a {
|
68 |
+
text-decoration: none;
|
69 |
+
color: var(---Brand1-5, #3477eb);
|
70 |
+
font-weight: normal;
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
p.url {
|
75 |
+
color: var(--60-text-3, rgba(18, 19, 22, 0.6));
|
76 |
+
font-size: 12px;
|
77 |
+
line-height: 18px;
|
78 |
+
height: 18px;
|
79 |
+
overflow: hidden;
|
80 |
+
}
|
81 |
+
|
82 |
+
p.summ {
|
83 |
+
color: rgba(18, 19, 22, 0.8);
|
84 |
+
font-size: 13px;
|
85 |
+
line-height: 20px;
|
86 |
+
white-space: wrap;
|
87 |
+
display: -webkit-box;
|
88 |
+
-webkit-box-orient: vertical;
|
89 |
+
-webkit-line-clamp: 2;
|
90 |
+
overflow: hidden;
|
91 |
+
text-overflow: ellipsis;
|
92 |
+
}
|
93 |
+
|
94 |
+
&.highLight {
|
95 |
+
background: var(--brand-11, #e6f2ff);
|
96 |
+
|
97 |
+
&:hover {
|
98 |
+
background-color: #b3d6ff;
|
99 |
+
}
|
100 |
+
}
|
101 |
+
}
|
frontend/React/src/pages/mindsearch/components/chat-right/components/search-item/index.tsx
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import classNames from 'classnames';
|
2 |
+
import styles from './index.module.less';
|
3 |
+
|
4 |
+
interface ISearchItemProps {
|
5 |
+
item: any;
|
6 |
+
}
|
7 |
+
|
8 |
+
const SearchItem = ({ item }: ISearchItemProps) => {
|
9 |
+
const openLink = (url: string) => {
|
10 |
+
window.open(url);
|
11 |
+
};
|
12 |
+
return (
|
13 |
+
<div
|
14 |
+
className={classNames(styles.searchItem, item.highLight ? styles.highLight : '')}
|
15 |
+
key={`search-item-${item.url}`}
|
16 |
+
onClick={() => {
|
17 |
+
openLink(item.url);
|
18 |
+
}}
|
19 |
+
>
|
20 |
+
<div className={styles.num}>{item.id}</div>
|
21 |
+
<div className={styles.inner}>
|
22 |
+
<p className={styles.url}>{item?.url}</p>
|
23 |
+
<p className={styles.title}>{item?.title}</p>
|
24 |
+
<p className={styles.summ}>{item?.summ}</p>
|
25 |
+
</div>
|
26 |
+
</div>
|
27 |
+
);
|
28 |
+
};
|
29 |
+
|
30 |
+
export default SearchItem;
|
frontend/React/src/{components → pages/mindsearch/components}/chat-right/index.module.less
RENAMED
@@ -1,28 +1,18 @@
|
|
1 |
.rightContent {
|
2 |
width: 44.44%;
|
|
|
3 |
flex-shrink: 0;
|
4 |
box-sizing: border-box;
|
5 |
-
padding: 24px;
|
6 |
-
border-radius:
|
7 |
-
border:
|
8 |
-
background:
|
9 |
-
height:
|
10 |
-
overflow
|
11 |
position: relative;
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
}
|
16 |
-
|
17 |
-
&::-webkit-scrollbar-track {
|
18 |
-
background-color: rgba(255, 255, 255, 0);
|
19 |
-
border-radius: 100px;
|
20 |
-
}
|
21 |
-
|
22 |
-
&::-webkit-scrollbar-thumb {
|
23 |
-
background-color: rgba(255, 255, 255, 0);
|
24 |
-
border-radius: 100px;
|
25 |
-
}
|
26 |
|
27 |
.toggleIcon {
|
28 |
position: absolute;
|
@@ -35,8 +25,33 @@
|
|
35 |
color: #121316;
|
36 |
font-size: 24px;
|
37 |
font-weight: 600;
|
38 |
-
line-height:
|
39 |
-
margin-bottom:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
}
|
41 |
|
42 |
.conclusion {
|
@@ -51,8 +66,6 @@
|
|
51 |
}
|
52 |
|
53 |
.steps {
|
54 |
-
margin-bottom: 24px;
|
55 |
-
|
56 |
.title {
|
57 |
color: var(--100-text-5, #121316);
|
58 |
font-size: 20px;
|
@@ -68,10 +81,11 @@
|
|
68 |
right: 0;
|
69 |
font-size: 20px;
|
70 |
font-weight: normal;
|
|
|
71 |
|
72 |
span {
|
73 |
color: #121316;
|
74 |
-
opacity: 0.6;
|
75 |
}
|
76 |
}
|
77 |
|
@@ -79,8 +93,8 @@
|
|
79 |
width: 12px;
|
80 |
height: 12px;
|
81 |
border-radius: 50%;
|
82 |
-
background-color: #
|
83 |
-
margin-right:
|
84 |
}
|
85 |
}
|
86 |
|
@@ -93,23 +107,61 @@
|
|
93 |
.searchList {
|
94 |
margin-top: 0 !important;
|
95 |
border-radius: 8px;
|
96 |
-
background: var(--fill-2, #
|
97 |
padding: 8px;
|
98 |
}
|
99 |
}
|
100 |
|
101 |
.con {
|
102 |
margin-left: 5px;
|
103 |
-
padding-top:
|
104 |
padding-left: 15px;
|
105 |
-
|
|
|
106 |
height: auto;
|
107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
&.collapsed {
|
109 |
overflow: hidden;
|
110 |
height: 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
padding-top: 0;
|
112 |
-
transition: all 1s;
|
113 |
}
|
114 |
}
|
115 |
}
|
@@ -125,41 +177,23 @@
|
|
125 |
font-size: 14px;
|
126 |
font-weight: 600;
|
127 |
line-height: 24px;
|
128 |
-
margin-bottom:
|
129 |
|
130 |
span {
|
131 |
margin-right: 4px;
|
132 |
}
|
133 |
}
|
134 |
|
135 |
-
.query {
|
136 |
-
&-Item {
|
137 |
-
display: inline-flex;
|
138 |
-
padding: 4px 8px;
|
139 |
-
margin-right: 4px;
|
140 |
-
margin-bottom: 4px;
|
141 |
-
border-radius: 4px;
|
142 |
-
border: 1px solid #EBECF0;
|
143 |
-
color: rgba(18, 19, 22, 0.80);
|
144 |
-
font-size: 14px;
|
145 |
-
line-height: 24px;
|
146 |
-
height: 32px;
|
147 |
-
box-sizing: border-box;
|
148 |
-
overflow: hidden;
|
149 |
-
// animation: fadeIn linear 2s;
|
150 |
-
}
|
151 |
-
}
|
152 |
-
|
153 |
.searchList {
|
154 |
margin-top: 0 !important;
|
155 |
-
border-radius:
|
156 |
-
background: var(--fill-2, #
|
157 |
padding: 8px;
|
158 |
}
|
159 |
|
160 |
.searchList {
|
161 |
.thought {
|
162 |
-
color: rgba(18, 19, 22, 0.
|
163 |
font-size: 14px;
|
164 |
line-height: 24px;
|
165 |
margin-bottom: 16px;
|
@@ -167,7 +201,8 @@
|
|
167 |
|
168 |
.scrollCon {
|
169 |
padding-right: 6px;
|
170 |
-
|
|
|
171 |
overflow-y: auto;
|
172 |
position: relative;
|
173 |
}
|
@@ -182,53 +217,15 @@
|
|
182 |
}
|
183 |
|
184 |
.scrollCon::-webkit-scrollbar-thumb {
|
185 |
-
background-color: #
|
186 |
-
border-radius:
|
187 |
}
|
188 |
|
189 |
.inner {
|
190 |
width: 100%;
|
191 |
border-radius: 8px;
|
192 |
-
background: var(--fill-2, #F4F5F9);
|
193 |
transition: all 0.5s ease;
|
194 |
box-sizing: border-box;
|
195 |
-
padding: 8px;
|
196 |
-
}
|
197 |
-
|
198 |
-
.searchItem {
|
199 |
-
border-radius: 8px;
|
200 |
-
background: var(---fill-0, #FFF);
|
201 |
-
margin-bottom: 6px;
|
202 |
-
padding: 4px 8px;
|
203 |
-
transition: all 0.5s ease-in-out;
|
204 |
-
|
205 |
-
&.highLight {
|
206 |
-
border: 1px solid var(---Success-6, #00B365);
|
207 |
-
background: linear-gradient(0deg, rgba(218, 242, 228, 0.40) 0%, rgba(218, 242, 228, 0.40) 100%), #FFF;
|
208 |
-
}
|
209 |
-
|
210 |
-
p {
|
211 |
-
white-space: nowrap;
|
212 |
-
max-width: 95%;
|
213 |
-
overflow: hidden;
|
214 |
-
text-overflow: ellipsis;
|
215 |
-
margin: 0;
|
216 |
-
}
|
217 |
-
|
218 |
-
p.summ {
|
219 |
-
color: rgba(18, 19, 22, 0.80);
|
220 |
-
font-size: 13px;
|
221 |
-
line-height: 20px;
|
222 |
-
margin-bottom: 2px;
|
223 |
-
}
|
224 |
-
|
225 |
-
p.url {
|
226 |
-
color: var(--60-text-3, rgba(18, 19, 22, 0.60));
|
227 |
-
font-size: 12px;
|
228 |
-
line-height: 18px;
|
229 |
-
padding-left: 20px;
|
230 |
-
}
|
231 |
}
|
232 |
}
|
233 |
-
}
|
234 |
-
|
|
|
1 |
.rightContent {
|
2 |
width: 44.44%;
|
3 |
+
max-width: 800px;
|
4 |
flex-shrink: 0;
|
5 |
box-sizing: border-box;
|
6 |
+
padding: 24px 0 24px 24px;
|
7 |
+
border-radius: 16px;
|
8 |
+
border: 1px solid var(----line-2, #ebecf0);
|
9 |
+
background: var(---fill-0, #fff);
|
10 |
+
height: 100%;
|
11 |
+
overflow: hidden;
|
12 |
position: relative;
|
13 |
+
display: flex;
|
14 |
+
justify-content: flex-start;
|
15 |
+
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
.toggleIcon {
|
18 |
position: absolute;
|
|
|
25 |
color: #121316;
|
26 |
font-size: 24px;
|
27 |
font-weight: 600;
|
28 |
+
line-height: 30px;
|
29 |
+
margin-bottom: 32px;
|
30 |
+
max-width: calc(100% - 40px);
|
31 |
+
}
|
32 |
+
|
33 |
+
.nodeInfo {
|
34 |
+
height: 100%;
|
35 |
+
overflow-y: auto;
|
36 |
+
padding-right: 24px;
|
37 |
+
|
38 |
+
&.forbidScroll {
|
39 |
+
overflow-y: hidden;
|
40 |
+
}
|
41 |
+
|
42 |
+
&::-webkit-scrollbar {
|
43 |
+
width: 6px;
|
44 |
+
}
|
45 |
+
|
46 |
+
&::-webkit-scrollbar-track {
|
47 |
+
background-color: rgba(255, 255, 255, 0);
|
48 |
+
border-radius: 100px;
|
49 |
+
}
|
50 |
+
|
51 |
+
&::-webkit-scrollbar-thumb {
|
52 |
+
background-color: rgba(255, 255, 255, 0);
|
53 |
+
border-radius: 100px;
|
54 |
+
}
|
55 |
}
|
56 |
|
57 |
.conclusion {
|
|
|
66 |
}
|
67 |
|
68 |
.steps {
|
|
|
|
|
69 |
.title {
|
70 |
color: var(--100-text-5, #121316);
|
71 |
font-size: 20px;
|
|
|
81 |
right: 0;
|
82 |
font-size: 20px;
|
83 |
font-weight: normal;
|
84 |
+
cursor: pointer;
|
85 |
|
86 |
span {
|
87 |
color: #121316;
|
88 |
+
// opacity: 0.6;
|
89 |
}
|
90 |
}
|
91 |
|
|
|
93 |
width: 12px;
|
94 |
height: 12px;
|
95 |
border-radius: 50%;
|
96 |
+
background-color: #3477EB;
|
97 |
+
margin-right: 12px;
|
98 |
}
|
99 |
}
|
100 |
|
|
|
107 |
.searchList {
|
108 |
margin-top: 0 !important;
|
109 |
border-radius: 8px;
|
110 |
+
background: var(--fill-2, #f4f5f9);
|
111 |
padding: 8px;
|
112 |
}
|
113 |
}
|
114 |
|
115 |
.con {
|
116 |
margin-left: 5px;
|
117 |
+
padding-top: 12px;
|
118 |
padding-left: 15px;
|
119 |
+
margin-bottom: 24px;
|
120 |
+
border-left: 1px solid #D7D8DD;
|
121 |
height: auto;
|
122 |
|
123 |
+
&.limitHeight {
|
124 |
+
max-height: calc(100vh - 320px);
|
125 |
+
overflow-y: auto;
|
126 |
+
|
127 |
+
&::-webkit-scrollbar {
|
128 |
+
width: 6px;
|
129 |
+
}
|
130 |
+
|
131 |
+
&::-webkit-scrollbar-track {
|
132 |
+
background-color: rgba(255, 255, 255, 0);
|
133 |
+
border-radius: 100px;
|
134 |
+
}
|
135 |
+
|
136 |
+
&::-webkit-scrollbar-thumb {
|
137 |
+
background-color: rgba(255, 255, 255, 0);
|
138 |
+
border-radius: 100px;
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
.draft {
|
143 |
+
margin-bottom: 20px;
|
144 |
+
}
|
145 |
+
|
146 |
+
p {
|
147 |
+
margin: 0;
|
148 |
+
line-height: 24px;
|
149 |
+
}
|
150 |
+
|
151 |
&.collapsed {
|
152 |
overflow: hidden;
|
153 |
height: 0;
|
154 |
+
padding-top: 24px;
|
155 |
+
margin-bottom: 0 !important;
|
156 |
+
|
157 |
+
// transition: all 1s;
|
158 |
+
|
159 |
+
}
|
160 |
+
}
|
161 |
+
|
162 |
+
&:last-child {
|
163 |
+
.collapsed {
|
164 |
padding-top: 0;
|
|
|
165 |
}
|
166 |
}
|
167 |
}
|
|
|
177 |
font-size: 14px;
|
178 |
font-weight: 600;
|
179 |
line-height: 24px;
|
180 |
+
margin-bottom: 12px;
|
181 |
|
182 |
span {
|
183 |
margin-right: 4px;
|
184 |
}
|
185 |
}
|
186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
.searchList {
|
188 |
margin-top: 0 !important;
|
189 |
+
border-radius: 16px;
|
190 |
+
background: var(--fill-2, #f4f5f9);
|
191 |
padding: 8px;
|
192 |
}
|
193 |
|
194 |
.searchList {
|
195 |
.thought {
|
196 |
+
color: rgba(18, 19, 22, 0.8);
|
197 |
font-size: 14px;
|
198 |
line-height: 24px;
|
199 |
margin-bottom: 16px;
|
|
|
201 |
|
202 |
.scrollCon {
|
203 |
padding-right: 6px;
|
204 |
+
height: auto;
|
205 |
+
max-height: 542px;
|
206 |
overflow-y: auto;
|
207 |
position: relative;
|
208 |
}
|
|
|
217 |
}
|
218 |
|
219 |
.scrollCon::-webkit-scrollbar-thumb {
|
220 |
+
background-color: #ebecf0;
|
221 |
+
border-radius: 20px;
|
222 |
}
|
223 |
|
224 |
.inner {
|
225 |
width: 100%;
|
226 |
border-radius: 8px;
|
|
|
227 |
transition: all 0.5s ease;
|
228 |
box-sizing: border-box;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
229 |
}
|
230 |
}
|
231 |
+
}
|
|
frontend/React/src/pages/mindsearch/components/chat-right/index.tsx
ADDED
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useEffect, useState, useRef } from 'react';
|
2 |
+
import styles from './index.module.less';
|
3 |
+
import classNames from 'classnames';
|
4 |
+
import CustomMarkdown from '../custom-markdown';
|
5 |
+
import IconFont from '../iconfont';
|
6 |
+
import PackIcon from '../../assets/pack-up.svg';
|
7 |
+
import PackDisableIcon from '../../assets/pack-up-disabled.svg';
|
8 |
+
import { Tooltip } from 'antd';
|
9 |
+
import QueryItem from './components/query-item';
|
10 |
+
import SearchItem from './components/search-item';
|
11 |
+
import Loading from '../loading';
|
12 |
+
import EmptyPlaceHolder from './components/empty-placeholder';
|
13 |
+
|
14 |
+
interface IProps {
|
15 |
+
nodeInfo: any;
|
16 |
+
stashInfo?: any;
|
17 |
+
historyNode?: any;
|
18 |
+
toggleRight: () => void;
|
19 |
+
chatIsOver?: Boolean;
|
20 |
+
}
|
21 |
+
|
22 |
+
const ChatRight = ({ nodeInfo, historyNode = null, toggleRight, chatIsOver = false }: IProps) => {
|
23 |
+
const [subQuestion, setSubQuestion] = useState('');
|
24 |
+
const [queries, setQuries] = useState<any>([]);
|
25 |
+
const [searchList, setSearchList] = useState<any>([]);
|
26 |
+
const [conclusionRef, setConclusionRef] = useState<any>(null);
|
27 |
+
const [isLoading, setIsLoading] = useState(false);
|
28 |
+
const [selectedIds, setSelectedIds] = useState([]);
|
29 |
+
const [currentStep, setCurrentStep] = useState(0);
|
30 |
+
const [conclusion, setConclusion] = useState('');
|
31 |
+
const [thinkingData, setThinking] = useState<any>(null);
|
32 |
+
const [readingData, setReading] = useState<any>(null);
|
33 |
+
const [isOutputing, setIsOutputing] = useState(false);
|
34 |
+
|
35 |
+
// steps展开收起的信息
|
36 |
+
const [collapseInfo, setCollapseInfo] = useState<boolean[]>([true, true, true]);
|
37 |
+
const [currentNode, setCurrentNode] = useState<any>();
|
38 |
+
// 展开收起
|
39 |
+
const toggleCard = (index: number) => {
|
40 |
+
const arr = [...collapseInfo];
|
41 |
+
arr[index] = !arr[index];
|
42 |
+
setCollapseInfo(arr);
|
43 |
+
};
|
44 |
+
// 高亮searchList
|
45 |
+
const highLightSearchList = (ids: any) => {
|
46 |
+
setCurrentStep(2);
|
47 |
+
const highlightArr: any = [...searchList];
|
48 |
+
highlightArr.forEach((item: any) => {
|
49 |
+
if (ids.includes(Number(item.id))) {
|
50 |
+
item.highLight = true;
|
51 |
+
}
|
52 |
+
});
|
53 |
+
highlightArr.sort((item1: any, item2: any) => {
|
54 |
+
if (item1.highLight === item2.highLight) {
|
55 |
+
return 0;
|
56 |
+
}
|
57 |
+
// 如果item1是highlight,放在前面
|
58 |
+
if (item1.highLight) {
|
59 |
+
return -1;
|
60 |
+
}
|
61 |
+
// 如果item2是highlight,放在后面
|
62 |
+
return 1;
|
63 |
+
});
|
64 |
+
setSearchList(highlightArr);
|
65 |
+
setCollapseInfo([false, false, true]);
|
66 |
+
};
|
67 |
+
|
68 |
+
const handleReceiveHistory = () => {
|
69 |
+
setCurrentNode(2);
|
70 |
+
setCollapseInfo([false, false, true]);
|
71 |
+
setIsLoading(false);
|
72 |
+
setThinking(historyNode.thinkingData);
|
73 |
+
setConclusion(historyNode.conclusion);
|
74 |
+
setReading(historyNode.readingData);
|
75 |
+
setQuries(historyNode.queries);
|
76 |
+
setSearchList(historyNode.searchList);
|
77 |
+
setConclusionRef(historyNode.conclusionRef);
|
78 |
+
setSelectedIds(historyNode.selectedIds);
|
79 |
+
setSubQuestion(historyNode.subQuestion);
|
80 |
+
};
|
81 |
+
|
82 |
+
const resetStatus = () => {
|
83 |
+
// 初始化组件状态
|
84 |
+
console.log('reset status-------');
|
85 |
+
setCurrentStep(0);
|
86 |
+
setCollapseInfo([true, true, true]);
|
87 |
+
setSelectedIds([]);
|
88 |
+
setSearchList([]);
|
89 |
+
setConclusionRef(null);
|
90 |
+
setThinking(null);
|
91 |
+
setReading(null);
|
92 |
+
setConclusion('');
|
93 |
+
setSubQuestion('');
|
94 |
+
};
|
95 |
+
|
96 |
+
const hideRight = () => {
|
97 |
+
if (isOutputing) return;
|
98 |
+
toggleRight();
|
99 |
+
};
|
100 |
+
|
101 |
+
useEffect(() => {
|
102 |
+
if (!historyNode) return;
|
103 |
+
handleReceiveHistory();
|
104 |
+
}, [historyNode]);
|
105 |
+
|
106 |
+
useEffect(() => {
|
107 |
+
if (!selectedIds.length) return;
|
108 |
+
highLightSearchList(selectedIds);
|
109 |
+
}, [selectedIds]);
|
110 |
+
|
111 |
+
useEffect(() => {
|
112 |
+
if (historyNode) return; // 有历史记录,不处理
|
113 |
+
try {
|
114 |
+
if (nodeInfo?.current_node !== currentNode) {
|
115 |
+
setCurrentNode(nodeInfo?.current_node);
|
116 |
+
resetStatus();
|
117 |
+
}
|
118 |
+
setIsOutputing(nodeInfo?.outputing);
|
119 |
+
if (!subQuestion && nodeInfo?.subQuestion) {
|
120 |
+
setSubQuestion(nodeInfo.subQuestion);
|
121 |
+
}
|
122 |
+
if (nodeInfo?.thinkingData) {
|
123 |
+
setThinking(nodeInfo.thinkingData);
|
124 |
+
}
|
125 |
+
if (nodeInfo?.readingData) {
|
126 |
+
setReading(nodeInfo.readingData);
|
127 |
+
}
|
128 |
+
if (nodeInfo?.queries?.length) {
|
129 |
+
setQuries(nodeInfo.queries);
|
130 |
+
}
|
131 |
+
if (nodeInfo?.searchList && !searchList?.length) {
|
132 |
+
setSearchList(nodeInfo.searchList);
|
133 |
+
setCurrentStep(1);
|
134 |
+
setCollapseInfo([false, true, true]);
|
135 |
+
}
|
136 |
+
if (nodeInfo?.selectedIds && !selectedIds?.length) {
|
137 |
+
setSelectedIds(nodeInfo.selectedIds);
|
138 |
+
}
|
139 |
+
if (nodeInfo?.conclusion) {
|
140 |
+
setConclusion(nodeInfo.conclusion);
|
141 |
+
}
|
142 |
+
if (nodeInfo?.conclusionRef) {
|
143 |
+
setConclusionRef(nodeInfo.conclusionRef);
|
144 |
+
}
|
145 |
+
} catch (err) {
|
146 |
+
console.log('[chat right]--error from nodeinfo---', err);
|
147 |
+
}
|
148 |
+
}, [nodeInfo, currentStep]);
|
149 |
+
|
150 |
+
return <div className={styles.rightContent} id="rightContent">
|
151 |
+
<div className={styles.toggleIcon} onClick={hideRight}>
|
152 |
+
<img src={PackIcon} />
|
153 |
+
</div>
|
154 |
+
{
|
155 |
+
currentNode ? <>
|
156 |
+
<div className={styles.titleNode}>{subQuestion}</div>
|
157 |
+
<div className={classNames(
|
158 |
+
styles.nodeInfo,
|
159 |
+
isOutputing ? styles.forbidScroll : ''
|
160 |
+
)}>
|
161 |
+
{thinkingData && (
|
162 |
+
<div className={classNames(styles.steps)}>
|
163 |
+
<div className={styles.title}>
|
164 |
+
<i></i>思考
|
165 |
+
<div
|
166 |
+
className={styles.open}
|
167 |
+
onClick={() => {
|
168 |
+
toggleCard(0);
|
169 |
+
}}
|
170 |
+
>
|
171 |
+
<IconFont type={collapseInfo[0] ? 'icon-shouqi' : 'icon-xiangxiazhankai'} />
|
172 |
+
</div>
|
173 |
+
</div>
|
174 |
+
<div className={classNames(styles.con, !collapseInfo[0] ? styles.collapsed : '')}>
|
175 |
+
<div>
|
176 |
+
<CustomMarkdown source={thinkingData} />
|
177 |
+
</div>
|
178 |
+
{queries.length > 0 && (
|
179 |
+
<div className={styles.query}>
|
180 |
+
<div className={styles.subTitle}>
|
181 |
+
<IconFont type="icon-SearchOutlined" />
|
182 |
+
搜索关键词
|
183 |
+
</div>
|
184 |
+
{queries.map((item: string, index: number) => (
|
185 |
+
<QueryItem key={`query-item-${item}`} item={item} />
|
186 |
+
))}
|
187 |
+
</div>
|
188 |
+
)}
|
189 |
+
{searchList.length > 0 && currentStep === 0 && (
|
190 |
+
<div className={styles.searchList}>
|
191 |
+
<div className={styles.subTitle}>
|
192 |
+
<IconFont type="icon-DocOutlined" />
|
193 |
+
信息来源
|
194 |
+
</div>
|
195 |
+
<div className={styles.scrollCon} style={searchList.length > 5 ? { height: '542px' } : {}}>
|
196 |
+
<div className={styles.inner} style={searchList.length > 5 ? {} : {}}>
|
197 |
+
{searchList.map((item: any) => (
|
198 |
+
<SearchItem item={item} key={`search-item-${item.url}`} />
|
199 |
+
))}
|
200 |
+
</div>
|
201 |
+
</div>
|
202 |
+
</div>
|
203 |
+
)}
|
204 |
+
</div>
|
205 |
+
</div>
|
206 |
+
)}
|
207 |
+
{currentStep > 0 && readingData && (
|
208 |
+
<div className={classNames(styles.steps)}>
|
209 |
+
<div className={styles.title}>
|
210 |
+
<i></i>信息来源
|
211 |
+
<div
|
212 |
+
className={styles.open}
|
213 |
+
onClick={() => {
|
214 |
+
toggleCard(1);
|
215 |
+
}}
|
216 |
+
>
|
217 |
+
<IconFont type={collapseInfo[1] ? 'icon-shouqi' : 'icon-xiangxiazhankai'} />
|
218 |
+
</div>
|
219 |
+
</div>
|
220 |
+
<div className={classNames(styles.con, !collapseInfo[1] ? styles.collapsed : '')}>
|
221 |
+
<div className={styles.draft}>
|
222 |
+
<CustomMarkdown source={readingData} />
|
223 |
+
</div>
|
224 |
+
{searchList.length > 0 && (
|
225 |
+
<div className={styles.searchList}>
|
226 |
+
<div className={styles.scrollCon} style={searchList.length > 5 ? { height: '542px' } : {}}>
|
227 |
+
<div className={styles.inner} style={searchList.length > 5 ? {} : {}}>
|
228 |
+
{searchList.map((item: any) => (
|
229 |
+
<SearchItem item={item} key={`search-item-${item.url}`} />
|
230 |
+
))}
|
231 |
+
</div>
|
232 |
+
</div>
|
233 |
+
</div>
|
234 |
+
)}
|
235 |
+
</div>
|
236 |
+
</div>
|
237 |
+
)}
|
238 |
+
{
|
239 |
+
conclusion && (
|
240 |
+
<div className={classNames(styles.steps)}>
|
241 |
+
<div className={styles.title}>
|
242 |
+
<i></i>信息整合
|
243 |
+
<div
|
244 |
+
className={styles.open}
|
245 |
+
onClick={() => {
|
246 |
+
toggleCard(2);
|
247 |
+
}}
|
248 |
+
>
|
249 |
+
<IconFont type={collapseInfo[2] ? 'icon-shouqi' : 'icon-xiangxiazhankai'} />
|
250 |
+
</div>
|
251 |
+
</div>
|
252 |
+
<div
|
253 |
+
id="nodeConclusionModule"
|
254 |
+
className={classNames(
|
255 |
+
styles.con,
|
256 |
+
!collapseInfo[2] ? styles.collapsed : '',
|
257 |
+
isOutputing ? styles.limitHeight : ''
|
258 |
+
)}
|
259 |
+
>
|
260 |
+
<div id="conclusionInfo">
|
261 |
+
<CustomMarkdown source={conclusion} refList={chatIsOver ? conclusionRef : null} />
|
262 |
+
</div>
|
263 |
+
</div>
|
264 |
+
</div>
|
265 |
+
)}
|
266 |
+
{isLoading && <Loading />}
|
267 |
+
</div></> : <EmptyPlaceHolder />
|
268 |
+
}
|
269 |
+
</div>
|
270 |
+
};
|
271 |
+
|
272 |
+
export default ChatRight;
|
frontend/React/src/pages/mindsearch/components/custom-markdown/index.module.less
ADDED
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.markdownCon {
|
2 |
+
display: flex;
|
3 |
+
flex-direction: column;
|
4 |
+
justify-content: flex-start;
|
5 |
+
|
6 |
+
h1 {
|
7 |
+
font-size: 26px;
|
8 |
+
}
|
9 |
+
|
10 |
+
h2 {
|
11 |
+
font-size: 24px;
|
12 |
+
}
|
13 |
+
|
14 |
+
h3 {
|
15 |
+
font-size: 20px;
|
16 |
+
}
|
17 |
+
|
18 |
+
h4 {
|
19 |
+
font-size: 18px;
|
20 |
+
}
|
21 |
+
|
22 |
+
h5,
|
23 |
+
h6 {
|
24 |
+
font-size: 16px;
|
25 |
+
}
|
26 |
+
|
27 |
+
p {
|
28 |
+
color: rgba(18, 19, 22, 0.8);
|
29 |
+
font-size: 16px;
|
30 |
+
font-weight: 400;
|
31 |
+
line-height: 28px;
|
32 |
+
margin: 0 0 16px 0;
|
33 |
+
}
|
34 |
+
|
35 |
+
ul {
|
36 |
+
margin-bottom: 8px;
|
37 |
+
padding-left: 22px;
|
38 |
+
}
|
39 |
+
|
40 |
+
li {
|
41 |
+
color: rgba(18, 19, 22, 0.8);
|
42 |
+
font-size: 16px;
|
43 |
+
font-weight: 400;
|
44 |
+
line-height: 28px;
|
45 |
+
|
46 |
+
p {
|
47 |
+
margin-bottom: 4px;
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
>p:last-child {
|
52 |
+
margin-bottom: 0;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
.footerFlag {
|
57 |
+
width: 18px;
|
58 |
+
height: 18px;
|
59 |
+
display: inline-flex;
|
60 |
+
justify-content: center;
|
61 |
+
align-items: center;
|
62 |
+
border-radius: 4px;
|
63 |
+
background: var(--fill-2, #f4f5f9);
|
64 |
+
color: var(--35-text-2, rgba(18, 19, 22, 0.35));
|
65 |
+
font-size: 12px;
|
66 |
+
font-weight: 600;
|
67 |
+
margin-left: 2px;
|
68 |
+
cursor: pointer;
|
69 |
+
font-style: normal;
|
70 |
+
|
71 |
+
/* 150% */
|
72 |
+
&:hover {
|
73 |
+
background: var(---Brand1-5, #3477eb);
|
74 |
+
color: #fff;
|
75 |
+
|
76 |
+
svg path {
|
77 |
+
fill: #fff;
|
78 |
+
fill-opacity: 1;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
// .mergeQuoLi {
|
84 |
+
// margin-bottom: 12px;
|
85 |
+
// }
|
86 |
+
|
87 |
+
li {
|
88 |
+
cursor: pointer;
|
89 |
+
|
90 |
+
.url {
|
91 |
+
color: var(--60-text-3, rgba(18, 19, 22, 0.6));
|
92 |
+
font-size: 12px;
|
93 |
+
font-weight: 400;
|
94 |
+
line-height: 18px;
|
95 |
+
max-width: 100%;
|
96 |
+
height: 18px;
|
97 |
+
overflow: hidden;
|
98 |
+
text-overflow: ellipsis;
|
99 |
+
}
|
100 |
+
|
101 |
+
.title {
|
102 |
+
color: var(---Brand1-5, #3477eb);
|
103 |
+
font-size: 14px;
|
104 |
+
line-height: 21px;
|
105 |
+
}
|
106 |
+
|
107 |
+
.summ {
|
108 |
+
white-space: wrap;
|
109 |
+
display: -webkit-box;
|
110 |
+
-webkit-box-orient: vertical;
|
111 |
+
-webkit-line-clamp: 2;
|
112 |
+
overflow: hidden;
|
113 |
+
text-overflow: ellipsis;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
.line {
|
118 |
+
margin: 4px 8px;
|
119 |
+
border: 1px solid #ebecf0;
|
120 |
+
transform: scaleY(0.5);
|
121 |
+
}
|
122 |
+
|
123 |
+
:global {
|
124 |
+
.iQuoPopover {
|
125 |
+
max-width: 420px;
|
126 |
+
}
|
127 |
+
|
128 |
+
.mergeQuoPopover {
|
129 |
+
border-radius: 12px;
|
130 |
+
border: 1px solid var(----line-2, #ebecf0);
|
131 |
+
background: var(---fill-0, #fff);
|
132 |
+
box-shadow: 1px 3px 8px 0px rgba(0, 0, 0, 0.06);
|
133 |
+
max-height: 240px;
|
134 |
+
max-width: 420px;
|
135 |
+
overflow-y: auto;
|
136 |
+
|
137 |
+
.ant-popover-inner {
|
138 |
+
padding: 8px !important;
|
139 |
+
}
|
140 |
+
|
141 |
+
.ant-popover-inner-content .line:last-child {
|
142 |
+
display: none;
|
143 |
+
}
|
144 |
+
|
145 |
+
li {
|
146 |
+
border-radius: 8px;
|
147 |
+
padding: 8px;
|
148 |
+
|
149 |
+
&:hover {
|
150 |
+
background-color: #f4f5f9;
|
151 |
+
}
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
.mergeQuoPopover::-webkit-scrollbar {
|
156 |
+
width: 6px;
|
157 |
+
}
|
158 |
+
|
159 |
+
.mergeQuoPopover::-webkit-scrollbar-track {
|
160 |
+
background-color: rgba(255, 255, 255, 0);
|
161 |
+
border-radius: 100px;
|
162 |
+
}
|
163 |
+
|
164 |
+
.mergeQuoPopover::-webkit-scrollbar-thumb {
|
165 |
+
background-color: #ebecf0;
|
166 |
+
border-radius: 20px;
|
167 |
+
}
|
168 |
+
}
|
frontend/React/src/pages/mindsearch/components/custom-markdown/index.tsx
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import ReactMarkdown from 'react-markdown';
|
2 |
+
import rehypeRaw from 'rehype-raw';
|
3 |
+
import { replaceStr, mergeReplaceToDiv } from '../../utils/tools';
|
4 |
+
import { Popover } from 'antd';
|
5 |
+
import classNames from 'classnames';
|
6 |
+
import styles from './index.module.less';
|
7 |
+
import { useEffect } from 'react';
|
8 |
+
|
9 |
+
interface IMarkdownProps {
|
10 |
+
source: string;
|
11 |
+
refList?: any;
|
12 |
+
quoType?: string;
|
13 |
+
chatIsOver?: boolean;
|
14 |
+
}
|
15 |
+
|
16 |
+
const CustomMarkdown = ({ source, refList = null, quoType = 'single', chatIsOver = false }: IMarkdownProps) => {
|
17 |
+
const linkToExtend = (url: string) => {
|
18 |
+
window.open(url);
|
19 |
+
};
|
20 |
+
|
21 |
+
const CustomI = ({ children, className, ...props }: any) => {
|
22 |
+
const content = refList
|
23 |
+
? Object.keys(refList).map((item) => {
|
24 |
+
if (Number(item) === Number(children)) {
|
25 |
+
return (
|
26 |
+
<li
|
27 |
+
key={`ref-item-${item}`}
|
28 |
+
onClick={() => {
|
29 |
+
linkToExtend(refList[item].url);
|
30 |
+
}}
|
31 |
+
>
|
32 |
+
<div className={styles.url}>{refList[item].url}</div>
|
33 |
+
<div className={styles.title}>{refList[item].title}</div>
|
34 |
+
<div className={styles.summ}>{refList[item].summ}</div>
|
35 |
+
</li>
|
36 |
+
);
|
37 |
+
} else {
|
38 |
+
return null;
|
39 |
+
}
|
40 |
+
})
|
41 |
+
: null;
|
42 |
+
return className.includes('custom') ? (
|
43 |
+
<Popover overlayClassName="iQuoPopover" content={content} arrow={false} key={`iQuoPopover-${children}`}>
|
44 |
+
<b className={styles.footerFlag}>{children}</b>
|
45 |
+
</Popover>
|
46 |
+
) : (
|
47 |
+
<code>{children}</code>
|
48 |
+
);
|
49 |
+
};
|
50 |
+
|
51 |
+
const CustomDiv = ({ children, className, ...props }: any) => {
|
52 |
+
const list = props['data-ids'].split(',');
|
53 |
+
const content = refList
|
54 |
+
? Object.keys(refList).map((item) => {
|
55 |
+
if (list.includes(String(item))) {
|
56 |
+
return (
|
57 |
+
<>
|
58 |
+
<li
|
59 |
+
className={styles.mergeQuoLi}
|
60 |
+
key={`ref-item-${refList[item].title}`}
|
61 |
+
onClick={() => {
|
62 |
+
linkToExtend(refList[item].url);
|
63 |
+
}}
|
64 |
+
>
|
65 |
+
<div className={styles.url}>{refList[item].url}</div>
|
66 |
+
<div className={styles.title}>{refList[item].title}</div>
|
67 |
+
</li>
|
68 |
+
<div className={classNames(styles.line, 'line')} />
|
69 |
+
</>
|
70 |
+
);
|
71 |
+
} else {
|
72 |
+
return null;
|
73 |
+
}
|
74 |
+
})
|
75 |
+
: null;
|
76 |
+
return className.includes('mergeQuo') ? (
|
77 |
+
<Popover content={content} arrow={false} overlayClassName="mergeQuoPopover" key={`custom-dev-${props['data-ids']}`}>
|
78 |
+
<b className={styles.footerFlag}>
|
79 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
|
80 |
+
<path
|
81 |
+
fillRule="evenodd"
|
82 |
+
clipRule="evenodd"
|
83 |
+
d="M5.49491 2.65877C3.29849 4.06934 2.07828 5.71113 1.83428 7.58413C1.45443 10.4999 4.06198 11.9272 5.3832 10.6448C6.70443 9.36248 5.91243 7.73483 4.95574 7.28993C3.99904 6.84499 3.41399 6.99995 3.51604 6.40539C3.6181 5.81085 4.97919 4.16241 6.17496 3.39472C6.2543 3.32725 6.28448 3.19635 6.2081 3.09704C6.15784 3.03174 6.05929 2.90365 5.91243 2.71278C5.78401 2.54586 5.66099 2.55211 5.49491 2.65877Z"
|
84 |
+
fill="#121316"
|
85 |
+
fillOpacity="0.35"
|
86 |
+
/>
|
87 |
+
<path
|
88 |
+
fillRule="evenodd"
|
89 |
+
clipRule="evenodd"
|
90 |
+
d="M11.2801 2.65877C9.08366 4.06934 7.86344 5.71113 7.61943 7.58413C7.2396 10.4999 9.84715 11.9272 11.1684 10.6448C12.4896 9.36248 11.6976 7.73483 10.7409 7.28993C9.78421 6.84499 9.19913 6.99995 9.30121 6.40539C9.40327 5.81085 10.7644 4.16241 11.9601 3.39472C12.0395 3.32725 12.0697 3.19635 11.9932 3.09704C11.943 3.03174 11.8445 2.90365 11.6976 2.71278C11.5692 2.54586 11.4462 2.55211 11.2801 2.65877Z"
|
91 |
+
fill="#121316"
|
92 |
+
fillOpacity="0.35"
|
93 |
+
/>
|
94 |
+
</svg>
|
95 |
+
</b>
|
96 |
+
</Popover>
|
97 |
+
) : (
|
98 |
+
<code>{children}</code>
|
99 |
+
);
|
100 |
+
};
|
101 |
+
|
102 |
+
return (
|
103 |
+
<div className={styles.markdownCon}>
|
104 |
+
<ReactMarkdown rehypePlugins={[rehypeRaw]} components={refList && Object.keys(refList)?.length ? { i: CustomI, span: CustomDiv } : {}}>
|
105 |
+
{
|
106 |
+
refList ?
|
107 |
+
quoType === 'merge' ? mergeReplaceToDiv(source) :
|
108 |
+
replaceStr(source) :
|
109 |
+
source
|
110 |
+
}
|
111 |
+
</ReactMarkdown>
|
112 |
+
</div>
|
113 |
+
);
|
114 |
+
};
|
115 |
+
|
116 |
+
export default CustomMarkdown;
|
frontend/React/src/{components → pages/mindsearch/components}/iconfont/index.tsx
RENAMED
@@ -1,9 +1,9 @@
|
|
1 |
-
import { createFromIconfontCN } from
|
2 |
|
3 |
// //at.alicdn.com/t/c/font_3858115_yl9vl04f0jc.js
|
4 |
const IconFont = createFromIconfontCN({
|
5 |
-
|
6 |
-
|
7 |
});
|
8 |
|
9 |
export default IconFont;
|
|
|
1 |
+
import { createFromIconfontCN } from '@ant-design/icons';
|
2 |
|
3 |
// //at.alicdn.com/t/c/font_3858115_yl9vl04f0jc.js
|
4 |
const IconFont = createFromIconfontCN({
|
5 |
+
// scriptUrl: "//static.openxlab.org.cn/cmg-animation-upload/iconfont.js",
|
6 |
+
scriptUrl: '//at.alicdn.com/t/c/font_3858115_p8dw9q83s0h.js',
|
7 |
});
|
8 |
|
9 |
export default IconFont;
|
frontend/React/src/{components → pages/mindsearch/components}/loading/index.module.less
RENAMED
@@ -3,42 +3,42 @@
|
|
3 |
position: relative;
|
4 |
width: 1px;
|
5 |
height: 1px;
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
position: absolute;
|
11 |
display: inline-block;
|
12 |
width: 15px;
|
13 |
height: 15px;
|
14 |
-
content:
|
15 |
border-radius: 100%;
|
16 |
-
background-color: #
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
left: -15px;
|
21 |
animation: ball-pulse infinite 0.75s -0.4s cubic-bezier(0.2, 0.68, 0.18, 1.08);
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
right: -15px;
|
26 |
animation: ball-pulse infinite 0.75s cubic-bezier(0.2, 0.68, 0.18, 1.08);
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
0% {
|
31 |
-
|
32 |
-
|
33 |
}
|
34 |
-
|
35 |
50% {
|
36 |
-
|
37 |
-
|
38 |
}
|
39 |
-
|
40 |
100% {
|
41 |
-
|
42 |
-
|
43 |
}
|
44 |
-
|
|
|
3 |
position: relative;
|
4 |
width: 1px;
|
5 |
height: 1px;
|
6 |
+
}
|
7 |
+
|
8 |
+
.loading99:before,
|
9 |
+
.loading99:after {
|
10 |
position: absolute;
|
11 |
display: inline-block;
|
12 |
width: 15px;
|
13 |
height: 15px;
|
14 |
+
content: '';
|
15 |
border-radius: 100%;
|
16 |
+
background-color: #5551ff;
|
17 |
+
}
|
18 |
+
|
19 |
+
.loading99:before {
|
20 |
left: -15px;
|
21 |
animation: ball-pulse infinite 0.75s -0.4s cubic-bezier(0.2, 0.68, 0.18, 1.08);
|
22 |
+
}
|
23 |
+
|
24 |
+
.loading99:after {
|
25 |
right: -15px;
|
26 |
animation: ball-pulse infinite 0.75s cubic-bezier(0.2, 0.68, 0.18, 1.08);
|
27 |
+
}
|
28 |
+
|
29 |
+
@keyframes ball-pulse {
|
30 |
0% {
|
31 |
+
transform: scale(1);
|
32 |
+
opacity: 1;
|
33 |
}
|
34 |
+
|
35 |
50% {
|
36 |
+
transform: scale(0.1);
|
37 |
+
opacity: 0.6;
|
38 |
}
|
39 |
+
|
40 |
100% {
|
41 |
+
transform: scale(1);
|
42 |
+
opacity: 1;
|
43 |
}
|
44 |
+
}
|
frontend/React/src/{components → pages/mindsearch/components}/loading/index.tsx
RENAMED
@@ -1,6 +1,6 @@
|
|
1 |
import styles from './index.module.less';
|
2 |
|
3 |
const Loading = () => {
|
4 |
-
return <div className={styles.loading99}
|
5 |
};
|
6 |
-
export default Loading;
|
|
|
1 |
import styles from './index.module.less';
|
2 |
|
3 |
const Loading = () => {
|
4 |
+
return <div className={styles.loading99} />;
|
5 |
};
|
6 |
+
export default Loading;
|
frontend/React/src/pages/mindsearch/components/mind-map-item/index.module.less
ADDED
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
article {
|
2 |
+
padding: 6px 16px;
|
3 |
+
border-radius: 8px;
|
4 |
+
height: 38px;
|
5 |
+
border: 1px solid transparent;
|
6 |
+
background: #fff;
|
7 |
+
color: #121316;
|
8 |
+
text-align: center;
|
9 |
+
font-size: 14px;
|
10 |
+
line-height: 24px;
|
11 |
+
position: relative;
|
12 |
+
box-sizing: border-box;
|
13 |
+
|
14 |
+
&.loading {
|
15 |
+
line-height: 20px;
|
16 |
+
border-radius: 8px;
|
17 |
+
overflow: hidden;
|
18 |
+
border: 1px solid transparent;
|
19 |
+
padding: 4px;
|
20 |
+
|
21 |
+
span {
|
22 |
+
color: #3477eb;
|
23 |
+
background-color: #fff;
|
24 |
+
border-radius: 4px;
|
25 |
+
line-height: 24px;
|
26 |
+
padding: 2px 12px;
|
27 |
+
}
|
28 |
+
|
29 |
+
.looping {
|
30 |
+
--border-width: 4px;
|
31 |
+
--follow-panel-linear-border: linear-gradient(90deg, #3477eb 0.58%, #FFB4BA 100.36%);
|
32 |
+
|
33 |
+
position: absolute;
|
34 |
+
top: 0;
|
35 |
+
left: 0;
|
36 |
+
width: calc(100% + var(--border-width) * 3 - 8px);
|
37 |
+
height: 100%;
|
38 |
+
background: var(--follow-panel-linear-border);
|
39 |
+
background-size: 300% 300%;
|
40 |
+
background-position: 0 50%;
|
41 |
+
animation: moveGradient 4s linear infinite;
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
&.disabled {
|
46 |
+
border-radius: 8px;
|
47 |
+
border: 1px solid #d7d8dd;
|
48 |
+
color: rgba(18, 19, 22, 0.35);
|
49 |
+
}
|
50 |
+
|
51 |
+
&.finished {
|
52 |
+
cursor: pointer;
|
53 |
+
border: 1px solid #3477EB;
|
54 |
+
|
55 |
+
&:hover {
|
56 |
+
background-color: #E6F2FF;
|
57 |
+
}
|
58 |
+
|
59 |
+
.finishDot {
|
60 |
+
position: absolute;
|
61 |
+
top: 6px;
|
62 |
+
right: 6px;
|
63 |
+
width: 6px;
|
64 |
+
height: 6px;
|
65 |
+
background-color: #3477EB;
|
66 |
+
border-radius: 50%;
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
&.forbidden {
|
71 |
+
cursor: not-allowed;
|
72 |
+
}
|
73 |
+
|
74 |
+
&.emptyNode {
|
75 |
+
padding: 0 !important;
|
76 |
+
border: 0;
|
77 |
+
}
|
78 |
+
|
79 |
+
&.active {
|
80 |
+
border-radius: 8px;
|
81 |
+
border: 1px solid var(---Brand1-5, #3477EB);
|
82 |
+
background: var(---Brand1-5, #3477EB);
|
83 |
+
color: #fff;
|
84 |
+
|
85 |
+
&:hover {
|
86 |
+
border: 1px solid var(---Brand1-5, #3477EB);
|
87 |
+
background: var(---Brand1-5, #3477EB);
|
88 |
+
color: #fff;
|
89 |
+
}
|
90 |
+
|
91 |
+
.dot {
|
92 |
+
position: absolute;
|
93 |
+
top: 6px;
|
94 |
+
right: 6px;
|
95 |
+
width: 6px;
|
96 |
+
height: 6px;
|
97 |
+
background-color: #fff;
|
98 |
+
border-radius: 50%;
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
&.init {
|
103 |
+
border: 1px solid transparent;
|
104 |
+
cursor: auto;
|
105 |
+
}
|
106 |
+
|
107 |
+
span {
|
108 |
+
display: block;
|
109 |
+
white-space: nowrap;
|
110 |
+
max-width: 160px;
|
111 |
+
overflow: hidden;
|
112 |
+
text-overflow: ellipsis;
|
113 |
+
position: relative;
|
114 |
+
z-index: 20;
|
115 |
+
}
|
116 |
+
|
117 |
+
span.status {
|
118 |
+
color: #4082fe;
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
ul.onlyone {
|
123 |
+
&:before {
|
124 |
+
opacity: 0;
|
125 |
+
}
|
126 |
+
|
127 |
+
>li {
|
128 |
+
margin-left: 0px;
|
129 |
+
}
|
130 |
+
|
131 |
+
&>li:after {
|
132 |
+
opacity: 0;
|
133 |
+
}
|
134 |
+
|
135 |
+
&>li:before {
|
136 |
+
// left: 0;
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
.endLine {
|
141 |
+
border-bottom: 1px solid #d7d8dd;
|
142 |
+
width: 3000px;
|
143 |
+
transition: width 1s ease-in-out;
|
144 |
+
}
|
145 |
+
|
146 |
+
@keyframes moveGradient {
|
147 |
+
50% {
|
148 |
+
background-position: 100% 50%;
|
149 |
+
}
|
150 |
+
}
|