Commit 5835a5f030414fce82bf67a3cf6b11821d0d2d0d
1 parent
8ee3a286
feat: 徽智制联-deepSeek-按照UI稿重新写一版,再增加切换问题功能
Showing
9 changed files
with
296 additions
and
105 deletions
1 | 1 | |
2 | 2 | .mark-down { |
3 | 3 | width: auto; |
4 | - max-width: 60%; | |
4 | + max-width: 355px; | |
5 | 5 | background: #fff; |
6 | - border-radius: 10px; | |
7 | - padding: 20px; | |
6 | + border-radius: 15px; | |
7 | + padding: 12px 28px 12px 16px; | |
8 | 8 | .group { |
9 | 9 | margin-bottom: 10px; |
10 | 10 | } |
11 | 11 | } |
12 | - | |
13 | -.role { | |
14 | - width: 40px; | |
15 | - height: 28px; | |
16 | - line-height: 28px; | |
17 | - font-size: 14px; | |
18 | - border-radius: 14px; | |
19 | - text-align: center; | |
20 | -} | |
21 | - | |
22 | -.text-right { | |
23 | - display: flex; | |
24 | - flex-direction: row-reverse; | |
25 | - padding-right: 2%; | |
26 | - .role { | |
27 | - margin-left: 20px; | |
28 | - background: #0A84FE; | |
29 | - color: #fff; | |
30 | - } | |
31 | - .mark-down { | |
32 | - background: #0A84FE; | |
33 | - color: #fff; | |
34 | - padding: 10px; | |
35 | - } | |
36 | -} | |
37 | -.text-left { | |
38 | - display: flex; | |
39 | - padding-left: 2%; | |
40 | - .role { | |
41 | - margin-right: 20px; | |
42 | - background: #fff; | |
43 | - } | |
44 | - .mark-down { | |
45 | - p { | |
46 | - overflow-wrap:break-word; | |
47 | - word-break: break-all; | |
48 | - } | |
49 | - } | |
50 | -} | |
\ No newline at end of file | ... | ... |
src/pages/deep-seek-index/banner_base.png
0 → 100644
273 KB
src/pages/deep-seek-index/bg_base.jpg
0 → 100644
658 KB
src/pages/deep-seek-index/head_base.png
0 → 100644
53.8 KB
1 | 1 | import React, {useState, useEffect, useRef} from "react"; |
2 | 2 | import './style.less' |
3 | -import { Button, Input } from 'antd'; | |
3 | +import { Input, Button } from '@chatui/core'; | |
4 | 4 | import Markdown from '@/components/deepSeekIndex/Markdown' |
5 | -import laBanner from "./laBanner.png"; | |
5 | +import banner from "./banner_base.png"; | |
6 | +import bg from "./bg_base.jpg"; | |
7 | +import refresh from "./refresh.png"; | |
8 | +import _ from 'lodash'; | |
9 | + | |
10 | +export const getTime = () => { | |
11 | + // 创建 Date 对象 | |
12 | + const now = new Date(); | |
13 | + | |
14 | + // 获取月份(0-11,需 +1 转为实际月份) | |
15 | + const month = String(now.getMonth() + 1).padStart(2, '0'); // 补零成两位数 | |
16 | + // 获取日期 | |
17 | + const day = String(now.getDate()).padStart(2, '0'); | |
18 | + // 获取小时 | |
19 | + const hours = String(now.getHours()).padStart(2, '0'); | |
20 | + // 获取分钟 | |
21 | + const minutes = String(now.getMinutes()).padStart(2, '0'); | |
22 | + // 组合成目标格式 | |
23 | + const result = `${month}月${day}日 ${hours}:${minutes}`; | |
24 | + | |
25 | + console.log(result); // 示例输出:07月15日 08:30 | |
26 | + | |
27 | + return result; | |
28 | +} | |
29 | +const example_1 = [ | |
30 | + { | |
31 | + name: '联通提供哪些服务?', | |
32 | + id: '1' | |
33 | + }, | |
34 | + { | |
35 | + name: '怎么办理国际漫游服务?', | |
36 | + id: '2' | |
37 | + }, | |
38 | + { | |
39 | + name: '如何办理宽带?', | |
40 | + id: '3' | |
41 | + } | |
42 | +] | |
43 | + | |
44 | +const example_2 = [ | |
45 | + { | |
46 | + name: '5G套餐推荐', | |
47 | + id: '4' | |
48 | + }, | |
49 | + { | |
50 | + name: '如何办理语音通话和短信的基础套餐?', | |
51 | + id: '5' | |
52 | + }, | |
53 | + { | |
54 | + name: '上不了网怎么办?', | |
55 | + id: '6' | |
56 | + } | |
57 | +] | |
6 | 58 | |
7 | 59 | const DeepSeekIndex: React.FC = () => { |
8 | 60 | const [searchValue, setSearchValue] = useState<any>(); |
61 | + const [disabled, setDisabled] = useState<boolean>(false); | |
62 | + const [exampleFlag, setExampleFlag] = useState<string>('1') | |
9 | 63 | |
10 | 64 | const [chatHistory, setChatHistory] = useState<any[]>([]); |
11 | 65 | const [currentResponse, setCurrentResponse] = useState<string>(''); |
12 | 66 | |
13 | 67 | const chatRef = useRef(null); |
14 | 68 | |
15 | - const send = async () => { | |
69 | + useEffect(() => { | |
70 | + const _time = getTime(); | |
71 | + const newHistory = [...chatHistory, | |
72 | + {role: 'time', answer: _time}, | |
73 | + { | |
74 | + role: 'example', | |
75 | + answer: example_1 | |
76 | + } | |
77 | + ]; | |
78 | + console.log('newHistory', newHistory) | |
79 | + setChatHistory(newHistory); | |
80 | + }, []) | |
81 | + | |
82 | + const refreshFun = () => { | |
83 | + const _exampleFlag = _.cloneDeep(exampleFlag); | |
84 | + const _example = _exampleFlag === '1' ? example_2 : example_1; | |
85 | + const _chatHistory = _.cloneDeep(chatHistory); | |
86 | + _chatHistory?.forEach((item: any) => { | |
87 | + if (item?.role === 'example') { | |
88 | + item.answer = _example | |
89 | + } | |
90 | + }) | |
91 | + setChatHistory(_chatHistory); | |
92 | + const _newExampleFlag = _exampleFlag === '1' ? '2' : '1' | |
93 | + setExampleFlag(_newExampleFlag); | |
94 | + } | |
95 | + | |
96 | + const send = async (_searchValue?: string) => { | |
97 | + if (disabled) return; | |
98 | + setDisabled(true); | |
16 | 99 | console.log('searchValue', searchValue) |
100 | + const _value = _searchValue ? _searchValue : searchValue; | |
17 | 101 | const data = { |
18 | - "query": searchValue, | |
102 | + "query": _value, | |
19 | 103 | "inputs": {}, |
20 | 104 | "response_mode": "streaming", |
21 | 105 | "user": "11", |
22 | 106 | "conversation_id": "" |
23 | 107 | } |
24 | 108 | const customHeaders = { |
25 | - 'Authorization': 'Bearer app-nvw8Y9taTRWuKg9ZfaoMXwNC' | |
109 | + 'Authorization': 'Bearer app-XxOBqpQr6u4c43tIl6by4vpK' | |
26 | 110 | } |
27 | 111 | |
28 | - const newHistory = [...chatHistory, { role: 'user', answer: searchValue }]; | |
112 | + const _time = getTime(); | |
113 | + const newHistory = [...chatHistory, | |
114 | + {role: 'time', answer: _time}, | |
115 | + {role: 'user', answer: _value} | |
116 | + ]; | |
29 | 117 | setChatHistory(newHistory); |
30 | 118 | setSearchValue(''); |
31 | 119 | let _currentResponse = ''; |
32 | 120 | |
33 | 121 | try { |
34 | - const response = await fetch('http://10.9.5.168/v1/chat-messages', { | |
122 | + const response = await fetch('http://36.34.99.80:8088/v1/chat-messages', { | |
35 | 123 | method: 'POST', |
36 | 124 | headers: { |
37 | 125 | 'Content-Type': 'application/json', |
38 | 126 | // 这里可以添加自定义请求头 |
39 | 127 | ...customHeaders |
40 | 128 | }, |
41 | - body: JSON.stringify({...data }) | |
129 | + body: JSON.stringify({...data}) | |
42 | 130 | }); |
43 | 131 | |
44 | 132 | if (!response.ok) { |
... | ... | @@ -50,9 +138,9 @@ const DeepSeekIndex: React.FC = () => { |
50 | 138 | let buffer = ''; |
51 | 139 | |
52 | 140 | while (true) { |
53 | - const { done, value } = await reader.read(); | |
141 | + const {done, value} = await reader.read(); | |
54 | 142 | if (done) break; |
55 | - buffer += decoder?.decode(value, { stream: true }); | |
143 | + buffer += decoder?.decode(value, {stream: true}); | |
56 | 144 | const lines = buffer?.split('\n'); |
57 | 145 | buffer = lines.pop(); |
58 | 146 | lines.forEach(line => { |
... | ... | @@ -64,10 +152,12 @@ const DeepSeekIndex: React.FC = () => { |
64 | 152 | }); |
65 | 153 | } |
66 | 154 | |
67 | - const updatedHistory = [...newHistory, { role: 'assistant', answer: _currentResponse }]; | |
155 | + const updatedHistory = [...newHistory, {role: 'assistant', answer: _currentResponse}]; | |
68 | 156 | setChatHistory(updatedHistory); |
69 | 157 | setCurrentResponse(''); |
70 | 158 | |
159 | + setDisabled(false); | |
160 | + | |
71 | 161 | |
72 | 162 | } catch (error) { |
73 | 163 | console.error('Stream error:', error); |
... | ... | @@ -87,45 +177,68 @@ const DeepSeekIndex: React.FC = () => { |
87 | 177 | console.log('chatHistory', chatHistory) |
88 | 178 | }, [chatHistory]) |
89 | 179 | |
180 | + const handleKeyDown = (e) => { | |
181 | + if (e.key === 'Enter' && !e.shiftKey) { | |
182 | + e.preventDefault(); // 阻止默认换行 | |
183 | + send(); | |
184 | + } | |
185 | + }; | |
186 | + | |
90 | 187 | return ( |
91 | - <div className={'deep-seek'}> | |
92 | - <div className={'deep-seek_header'}> | |
93 | - <img src={laBanner} alt=""/> | |
94 | - </div> | |
95 | - <div | |
96 | - ref={chatRef} | |
97 | - className={'deep-seek_content'} | |
98 | - > | |
99 | - {chatHistory?.map((msg, index) => ( | |
100 | - <p key={index} className={`mb-2 ${msg.role === 'user' ? 'text-right' : 'text-left'}`}> | |
101 | - { | |
102 | - msg?.role ? <div className={'role'}> | |
103 | - {msg?.role === 'user' ? '问' : msg?.role === 'assistant' ? '答' : ''} | |
104 | - </div> : '' | |
105 | - } | |
106 | - <Markdown content={msg?.answer}/> | |
107 | - </p> | |
108 | - ))} | |
109 | - {currentResponse && ( | |
110 | - <p className="mb-2 text-left current"> | |
111 | - <div className={'role'}>答</div> | |
112 | - <Markdown content={currentResponse} /> | |
113 | - </p> | |
114 | - )} | |
115 | - </div> | |
116 | - <div className={'deep-seek_footer'}> | |
117 | - <Input | |
118 | - type="text" | |
119 | - value={searchValue} | |
120 | - className="company-list-search-input" | |
121 | - placeholder="输入..." | |
122 | - onChange={(e) => setSearchValue(e.target.value)} | |
123 | - onPressEnter={send} | |
124 | - /> | |
125 | - <Button type={'primary'} onClick={send}>发送</Button> | |
126 | - </div> | |
127 | - | |
128 | - </div> | |
188 | + <div className={'deep-seek'}> | |
189 | + <img className={'deep-seek_bg'} src={bg} alt=""/> | |
190 | + <div | |
191 | + ref={chatRef} | |
192 | + className={'deep-seek_content'} | |
193 | + > | |
194 | + <div className={'deep-seek_header'}> | |
195 | + <img src={banner} alt=""/> | |
196 | + </div> | |
197 | + {chatHistory?.map((msg, index) => ( | |
198 | + <div key={index} className={`${msg.role === 'user' ? 'content-right' : msg.role === 'assistant' ? 'content-left' : 'content-center' }`}> | |
199 | + { | |
200 | + msg.role === 'time' ? | |
201 | + <div className={'current-time'}>{msg?.answer}</div>: | |
202 | + msg.role === 'example'? <div className={'current-example'}> | |
203 | + <div className={'current-example_head'}> | |
204 | + <p className={'current-example_head-name'}>你想了解什么呢</p> | |
205 | + <img src={refresh} className={'refresh'} alt="" onClick={refreshFun}/> | |
206 | + </div> | |
207 | + { | |
208 | + msg?.answer?.map((item:any) => { | |
209 | + return <div | |
210 | + className={'omit1 current-example_title'} | |
211 | + key={item?.id} | |
212 | + onClick={() => send(item?.name || '')} | |
213 | + >{item?.name}</div> | |
214 | + }) | |
215 | + } | |
216 | + </div> : | |
217 | + msg.role === 'user' ? <p className={'current-ask'}>{msg?.answer}</p> : | |
218 | + <Markdown content={msg?.answer}/> | |
219 | + } | |
220 | + | |
221 | + </div> | |
222 | + ))} | |
223 | + {currentResponse && ( | |
224 | + <div className="content-left"> | |
225 | + <Markdown content={currentResponse}/> | |
226 | + </div> | |
227 | + )} | |
228 | + </div> | |
229 | + <div className={'deep-seek_footer'}> | |
230 | + <Input | |
231 | + type="text" | |
232 | + value={searchValue} | |
233 | + className="deep-seek_footer-search" | |
234 | + placeholder="输入..." | |
235 | + onChange={(val: string) => setSearchValue(val)} | |
236 | + onKeyDown={handleKeyDown} | |
237 | + /> | |
238 | + <Button className={`deep-seek_footer-send ${disabled ? 'disabled' : ''}`} color={'primary'} onClick={send}>发送</Button> | |
239 | + </div> | |
240 | + | |
241 | + </div> | |
129 | 242 | ); |
130 | 243 | }; |
131 | 244 | ... | ... |
src/pages/deep-seek-index/laBanner.png
deleted
100644 → 0
696 KB
src/pages/deep-seek-index/refresh.png
0 → 100644
852 Bytes
1 | 1 | .deep-seek { |
2 | - background: #f1f1f1; | |
3 | 2 | height: 100%; |
4 | 3 | width: 100%; |
5 | - padding-bottom: calc( 2% + 50px); | |
6 | - padding-top: calc( 2% + 300px); | |
4 | + padding-bottom: calc(71px + constant(safe-area-inset-bottom)); | |
5 | + padding-bottom: calc(71px + env(safe-area-inset-bottom)); | |
7 | 6 | |
8 | - &_header { | |
7 | + &_bg { | |
9 | 8 | position: fixed; |
10 | 9 | top: 0; |
11 | 10 | left: 0; |
12 | 11 | right: 0; |
12 | + bottom: 0; | |
13 | + z-index: 1; | |
14 | + width: 100vw; | |
15 | + height: 100vh; | |
16 | + } | |
17 | + | |
18 | + &_header { | |
19 | + padding-top: 15px; | |
20 | + margin-bottom: 4px; | |
13 | 21 | img { |
14 | - height: 300px; | |
15 | - width: auto; | |
22 | + width: 345px; | |
23 | + height: 96px; | |
24 | + margin: 0 auto; | |
16 | 25 | } |
17 | 26 | } |
18 | 27 | |
19 | 28 | &_footer { |
20 | 29 | position: fixed; |
21 | - bottom: 2%; | |
22 | - left: 0; | |
23 | - right: 0; | |
30 | + bottom: calc(10px + constant(safe-area-inset-bottom)); | |
31 | + bottom: calc(10px + env(safe-area-inset-bottom)); | |
32 | + left: 10px; | |
33 | + right: 10px; | |
24 | 34 | display: flex; |
25 | 35 | align-items: center; |
26 | 36 | justify-content: space-between; |
27 | - padding: 0 20%; | |
28 | - input { | |
29 | - width: 80%; | |
37 | + z-index: 3; | |
38 | + background: #FFFFFF; | |
39 | + border-radius: 15px; | |
40 | + padding: 11px 12px 11px 20px; | |
41 | + &-search { | |
42 | + width: 250px; | |
43 | + height: 32px; | |
44 | + line-height: 32px; | |
45 | + border: none; | |
46 | + padding: 0; | |
47 | + font-size: 16px; | |
48 | + } | |
49 | + &-send { | |
50 | + width: 54px; | |
51 | + min-width: 54px; | |
52 | + background: #F53F3F !important; | |
53 | + border-radius: 5px; | |
54 | + height: 32px; | |
55 | + line-height: 32px; | |
56 | + font-size: 14px; | |
57 | + color: #FFFFFF; | |
58 | + text-align: center; | |
59 | + padding: 0; | |
60 | + &.disabled { | |
61 | + opacity: 0.5; | |
62 | + } | |
30 | 63 | } |
31 | 64 | } |
32 | 65 | |
... | ... | @@ -37,6 +70,9 @@ |
37 | 70 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); |
38 | 71 | transition-duration: 0.3s; |
39 | 72 | |
73 | + position: relative; | |
74 | + z-index: 2; | |
75 | + | |
40 | 76 | /* Webkit 浏览器(Chrome, Safari, Edge) */ |
41 | 77 | ::-webkit-scrollbar { |
42 | 78 | width: 5px; /* 或者 height: 12px; 对于水平滚动条 */ |
... | ... | @@ -56,3 +92,84 @@ |
56 | 92 | } |
57 | 93 | } |
58 | 94 | |
95 | +.content-center { | |
96 | + .current-time { | |
97 | + height: 48px; | |
98 | + line-height: 48px; | |
99 | + font-size: 12px; | |
100 | + color: #999999; | |
101 | + text-align: center; | |
102 | + } | |
103 | + .current-example { | |
104 | + margin: 0 10px 4px; | |
105 | + background: #FFFFFF; | |
106 | + box-shadow: 0 4px 0 0 rgba(217,217,217,0.50); | |
107 | + border-radius: 15px; | |
108 | + overflow: hidden; | |
109 | + &_head { | |
110 | + height: 50px; | |
111 | + width: 100%; | |
112 | + background: url("./head_base.png") center center no-repeat; | |
113 | + background-size: cover; | |
114 | + padding-left: 109px; | |
115 | + padding-top: 17px; | |
116 | + display: flex; | |
117 | + justify-content: space-between; | |
118 | + padding-right: 15px; | |
119 | + | |
120 | + &-name { | |
121 | + font-size: 16px; | |
122 | + color: #1C1C1C; | |
123 | + line-height: 24px; | |
124 | + height: 24px; | |
125 | + font-weight: 500; | |
126 | + } | |
127 | + .refresh { | |
128 | + width: 20px; | |
129 | + height: 20px; | |
130 | + margin-top: 2px; | |
131 | + } | |
132 | + } | |
133 | + &_title { | |
134 | + width: 327px; | |
135 | + margin: 0 12px 12px; | |
136 | + background: #F5F9FE; | |
137 | + border-radius: 3px; | |
138 | + font-size: 15px; | |
139 | + color: #00A1FF; | |
140 | + line-height: 48px; | |
141 | + height: 48px; | |
142 | + text-align: center; | |
143 | + padding: 0 10px; | |
144 | + cursor: pointer; | |
145 | + } | |
146 | + } | |
147 | +} | |
148 | + | |
149 | +.content-right { | |
150 | + padding-right: 10px; | |
151 | + display: flex; | |
152 | + justify-content: flex-end; | |
153 | + margin-bottom: 14px; | |
154 | + .current-ask { | |
155 | + display: inline-block; | |
156 | + background: #F8E4DD; | |
157 | + padding: 12px 16px; | |
158 | + line-height: 22px; | |
159 | + width: auto; | |
160 | + max-width: 355px; | |
161 | + border-radius: 15px; | |
162 | + font-size: 16px; | |
163 | + color: #1C1C1C; | |
164 | + } | |
165 | +} | |
166 | +.content-left { | |
167 | + display: flex; | |
168 | + padding-left: 10px; | |
169 | + .mark-down { | |
170 | + p { | |
171 | + overflow-wrap:break-word; | |
172 | + word-break: break-all; | |
173 | + } | |
174 | + } | |
175 | +} | ... | ... |
... | ... | @@ -3,8 +3,8 @@ import react from '@vitejs/plugin-react' |
3 | 3 | import postCssPxToViewport from 'postcss-px-to-viewport'; |
4 | 4 | |
5 | 5 | const _pxToViewPort = { |
6 | - viewportWidth: 750, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750 | |
7 | - viewportHeight: 1334, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置 | |
6 | + viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750 | |
7 | + viewportHeight: 667, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置 | |
8 | 8 | unitPrecision: 5, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除) |
9 | 9 | viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw |
10 | 10 | selectorBlackList: [], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名 | ... | ... |