Commit 5835a5f030414fce82bf67a3cf6b11821d0d2d0d

Authored by 史婷婷
1 parent 8ee3a286

feat: 徽智制联-deepSeek-按照UI稿重新写一版,再增加切换问题功能

1 1
2 .mark-down { 2 .mark-down {
3 width: auto; 3 width: auto;
4 - max-width: 60%; 4 + max-width: 355px;
5 background: #fff; 5 background: #fff;
6 - border-radius: 10px;  
7 - padding: 20px; 6 + border-radius: 15px;
  7 + padding: 12px 28px 12px 16px;
8 .group { 8 .group {
9 margin-bottom: 10px; 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 -}  
1 import React, {useState, useEffect, useRef} from "react"; 1 import React, {useState, useEffect, useRef} from "react";
2 import './style.less' 2 import './style.less'
3 -import { Button, Input } from 'antd'; 3 +import { Input, Button } from '@chatui/core';
4 import Markdown from '@/components/deepSeekIndex/Markdown' 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 const DeepSeekIndex: React.FC = () => { 59 const DeepSeekIndex: React.FC = () => {
8 const [searchValue, setSearchValue] = useState<any>(); 60 const [searchValue, setSearchValue] = useState<any>();
  61 + const [disabled, setDisabled] = useState<boolean>(false);
  62 + const [exampleFlag, setExampleFlag] = useState<string>('1')
9 63
10 const [chatHistory, setChatHistory] = useState<any[]>([]); 64 const [chatHistory, setChatHistory] = useState<any[]>([]);
11 const [currentResponse, setCurrentResponse] = useState<string>(''); 65 const [currentResponse, setCurrentResponse] = useState<string>('');
12 66
13 const chatRef = useRef(null); 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 console.log('searchValue', searchValue) 99 console.log('searchValue', searchValue)
  100 + const _value = _searchValue ? _searchValue : searchValue;
17 const data = { 101 const data = {
18 - "query": searchValue, 102 + "query": _value,
19 "inputs": {}, 103 "inputs": {},
20 "response_mode": "streaming", 104 "response_mode": "streaming",
21 "user": "11", 105 "user": "11",
22 "conversation_id": "" 106 "conversation_id": ""
23 } 107 }
24 const customHeaders = { 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 setChatHistory(newHistory); 117 setChatHistory(newHistory);
30 setSearchValue(''); 118 setSearchValue('');
31 let _currentResponse = ''; 119 let _currentResponse = '';
32 120
33 try { 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 method: 'POST', 123 method: 'POST',
36 headers: { 124 headers: {
37 'Content-Type': 'application/json', 125 'Content-Type': 'application/json',
38 // 这里可以添加自定义请求头 126 // 这里可以添加自定义请求头
39 ...customHeaders 127 ...customHeaders
40 }, 128 },
41 - body: JSON.stringify({...data }) 129 + body: JSON.stringify({...data})
42 }); 130 });
43 131
44 if (!response.ok) { 132 if (!response.ok) {
@@ -50,9 +138,9 @@ const DeepSeekIndex: React.FC = () => { @@ -50,9 +138,9 @@ const DeepSeekIndex: React.FC = () => {
50 let buffer = ''; 138 let buffer = '';
51 139
52 while (true) { 140 while (true) {
53 - const { done, value } = await reader.read(); 141 + const {done, value} = await reader.read();
54 if (done) break; 142 if (done) break;
55 - buffer += decoder?.decode(value, { stream: true }); 143 + buffer += decoder?.decode(value, {stream: true});
56 const lines = buffer?.split('\n'); 144 const lines = buffer?.split('\n');
57 buffer = lines.pop(); 145 buffer = lines.pop();
58 lines.forEach(line => { 146 lines.forEach(line => {
@@ -64,10 +152,12 @@ const DeepSeekIndex: React.FC = () => { @@ -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 setChatHistory(updatedHistory); 156 setChatHistory(updatedHistory);
69 setCurrentResponse(''); 157 setCurrentResponse('');
70 158
  159 + setDisabled(false);
  160 +
71 161
72 } catch (error) { 162 } catch (error) {
73 console.error('Stream error:', error); 163 console.error('Stream error:', error);
@@ -87,45 +177,68 @@ const DeepSeekIndex: React.FC = () => { @@ -87,45 +177,68 @@ const DeepSeekIndex: React.FC = () => {
87 console.log('chatHistory', chatHistory) 177 console.log('chatHistory', chatHistory)
88 }, [chatHistory]) 178 }, [chatHistory])
89 179
  180 + const handleKeyDown = (e) => {
  181 + if (e.key === 'Enter' && !e.shiftKey) {
  182 + e.preventDefault(); // 阻止默认换行
  183 + send();
  184 + }
  185 + };
  186 +
90 return ( 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
1 .deep-seek { 1 .deep-seek {
2 - background: #f1f1f1;  
3 height: 100%; 2 height: 100%;
4 width: 100%; 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 position: fixed; 8 position: fixed;
10 top: 0; 9 top: 0;
11 left: 0; 10 left: 0;
12 right: 0; 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 img { 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 &_footer { 28 &_footer {
20 position: fixed; 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 display: flex; 34 display: flex;
25 align-items: center; 35 align-items: center;
26 justify-content: space-between; 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,6 +70,9 @@
37 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 70 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
38 transition-duration: 0.3s; 71 transition-duration: 0.3s;
39 72
  73 + position: relative;
  74 + z-index: 2;
  75 +
40 /* Webkit 浏览器(Chrome, Safari, Edge) */ 76 /* Webkit 浏览器(Chrome, Safari, Edge) */
41 ::-webkit-scrollbar { 77 ::-webkit-scrollbar {
42 width: 5px; /* 或者 height: 12px; 对于水平滚动条 */ 78 width: 5px; /* 或者 height: 12px; 对于水平滚动条 */
@@ -56,3 +92,84 @@ @@ -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,8 +3,8 @@ import react from '@vitejs/plugin-react'
3 import postCssPxToViewport from 'postcss-px-to-viewport'; 3 import postCssPxToViewport from 'postcss-px-to-viewport';
4 4
5 const _pxToViewPort = { 5 const _pxToViewPort = {
6 - viewportWidth: 750, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750  
7 - viewportHeight: 1334, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置 6 + viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750
  7 + viewportHeight: 667, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
8 unitPrecision: 5, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除) 8 unitPrecision: 5, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
9 viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw 9 viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
10 selectorBlackList: [], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名 10 selectorBlackList: [], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名