Commit 5835a5f030414fce82bf67a3cf6b11821d0d2d0d

Authored by 史婷婷
1 parent 8ee3a286

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

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
... ...
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
... ...
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: [], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
... ...