Commit 308bda69e1b39ce3640785573ea13f8d90eb2467

Authored by 史婷婷
1 parent 3394b6c8

feat: 徽智制联-deepSeek-增加loading效果+部分样式调整

... ... @@ -10,20 +10,15 @@
10 10 "preview": "vite preview"
11 11 },
12 12 "dependencies": {
13   - "@chatui/core": "^2.4.2",
14   - "antd": "^5.22.3",
  13 + "antd-mobile": "^5.38.1",
  14 + "antd-mobile-icons": "^0.3.0",
15 15 "axios": "^1.7.3",
16   - "cesium": "^1.124.0",
17   - "echarts": "^5.5.1",
18   - "echarts-for-react": "^3.0.2",
19 16 "js-cookie": "^3.0.5",
20 17 "lodash": "^4.17.11",
21 18 "lodash-es": "^4.17.21",
22 19 "normalize.css": "^8.0.1",
23   - "rc-pagination": "^4.2.0",
24 20 "react": "^18.3.1",
25 21 "react-dom": "^18.3.1",
26   - "react-echarts": "^0.1.1",
27 22 "react-markdown": "^9.0.1",
28 23 "react-router-dom": "^6.26.0",
29 24 "react-syntax-highlighter": "^15.6.1",
... ... @@ -35,7 +30,6 @@
35 30 "swiper": "^8.4.4"
36 31 },
37 32 "devDependencies": {
38   - "@types/echarts": "^4.9.22",
39 33 "@types/js-cookie": "^3.0.6",
40 34 "@types/lodash": "^4.17.7",
41 35 "@types/lodash-es": "^4.17.12",
... ...
... ... @@ -15,7 +15,6 @@ import SyntaxHighlighter from 'react-syntax-highlighter'
15 15 import MarkdownButton from './markdown-button'
16 16 import MarkdownForm from './markdown-form'
17 17 import ThinkBlock from './markdown-block'
18   -import VideoGallery from './videoGallery'
19 18
20 19 interface MarkdownProps {
21 20 content: string
... ... @@ -164,7 +163,8 @@ const VideoBlock: any = memo(({ node }: any) => {
164 163 const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
165 164 if (srcs.length === 0)
166 165 return null
167   - return <VideoGallery key={srcs.join()} srcs={srcs} />
  166 + // return <VideoGallery key={srcs.join()} srcs={srcs} />
  167 + return ''
168 168 })
169 169 VideoBlock.displayName = 'VideoBlock'
170 170
... ...
... ... @@ -39,7 +39,7 @@ const useThinkTimer = (children: any) => {
39 39 const [startTime] = useState(Date.now())
40 40 const [elapsedTime, setElapsedTime] = useState(0)
41 41 const [isComplete, setIsComplete] = useState(false)
42   - const timerRef = useRef<NodeJS.Timeout>()
  42 + const timerRef = useRef<number>()
43 43
44 44 useEffect(() => {
45 45 timerRef.current = setInterval(() => {
... ...
1   -import { Button } from 'antd';
  1 +import { Button } from 'antd-mobile';
2 2 import './base.less'
3 3 import './style.less'
4 4
... ... @@ -19,7 +19,7 @@ const MarkdownButton = ({ node }: any) => {
19 19 }
20 20
21 21 return <Button
22   - variant={variant}
  22 + color={variant}
23 23 size={size}
24 24 onClick={() => {
25 25 if (is_valid_url(link)) {
... ...
... ... @@ -7,8 +7,9 @@ import {
7 7 Input,
8 8 DatePicker,
9 9 Checkbox,
10   - Select
11   -} from 'antd';
  10 + Selector,
  11 + TextArea
  12 +} from 'antd-mobile';
12 13
13 14 enum DATA_FORMAT {
14 15 TEXT = 'text',
... ... @@ -95,7 +96,7 @@ const MarkdownForm = ({ node }: any) => {
95 96 <DatePicker
96 97 key={index}
97 98 value={formValues[child.properties.name]}
98   - onChange={(date) => {
  99 + onSelect={(date) => {
99 100 setFormValues(prevValues => ({
100 101 ...prevValues,
101 102 [child.properties.name]: date,
... ... @@ -123,30 +124,30 @@ const MarkdownForm = ({ node }: any) => {
123 124 }
124 125 if (child.properties.type === SUPPORTED_TYPES.SELECT) {
125 126 return (
126   - <Select
  127 + <Selector
127 128 key={index}
128 129 className="w-full"
129   - // items={(() => {
130   - // let options = child.properties.dataOptions || child.properties['data-options'] || []
131   - // if (typeof options === 'string') {
132   - // try {
133   - // options = JSON.parse(options)
134   - // }
135   - // catch (e) {
136   - // console.error('Failed to parse options:', e)
137   - // options = []
138   - // }
139   - // }
140   - // return options.map((option: string) => ({
141   - // name: option,
142   - // value: option,
143   - // }))
144   - // })()}
  130 + options={(() => {
  131 + let options = child.properties.dataOptions || child.properties['data-options'] || []
  132 + if (typeof options === 'string') {
  133 + try {
  134 + options = JSON.parse(options)
  135 + }
  136 + catch (e) {
  137 + console.error('Failed to parse options:', e)
  138 + options = []
  139 + }
  140 + }
  141 + return options.map((option: string) => ({
  142 + name: option,
  143 + value: option,
  144 + }))
  145 + })()}
145 146 defaultValue={formValues[child.properties.name]}
146   - onSelect={(item) => {
  147 + onChange={(item) => {
147 148 setFormValues(prevValues => ({
148 149 ...prevValues,
149   - [child.properties.name]: item.value,
  150 + [child.properties.name]: item,
150 151 }))
151 152 }}
152 153 />
... ... @@ -160,10 +161,10 @@ const MarkdownForm = ({ node }: any) => {
160 161 name={child.properties.name}
161 162 placeholder={child.properties.placeholder}
162 163 value={formValues[child.properties.name]}
163   - onChange={(e) => {
  164 + onChange={(val: string) => {
164 165 setFormValues(prevValues => ({
165 166 ...prevValues,
166   - [child.properties.name]: e.target.value,
  167 + [child.properties.name]: val,
167 168 }))
168 169 }}
169 170 />
... ... @@ -171,15 +172,15 @@ const MarkdownForm = ({ node }: any) => {
171 172 }
172 173 if (child.tagName === SUPPORTED_TAGS.TEXTAREA) {
173 174 return (
174   - <Input.TextArea
  175 + <TextArea
175 176 key={index}
176 177 name={child.properties.name}
177 178 placeholder={child.properties.placeholder}
178 179 value={formValues[child.properties.name]}
179   - onChange={(e) => {
  180 + onChange={(val: string) => {
180 181 setFormValues(prevValues => ({
181 182 ...prevValues,
182   - [child.properties.name]: e.target.value,
  183 + [child.properties.name]: val,
183 184 }))
184 185 }}
185 186 />
... ... @@ -191,7 +192,7 @@ const MarkdownForm = ({ node }: any) => {
191 192
192 193 return (
193 194 <Button
194   - variant={variant}
  195 + color={variant}
195 196 size={size}
196 197 className='mt-4'
197 198 key={index}
... ...
1   -.videoPlayer {
2   - position: relative;
3   - width: 100%;
4   - max-width: 800px;
5   - margin: 0 auto;
6   - border-radius: 8px;
7   - overflow: hidden;
8   -}
9   -
10   -.video {
11   - width: 100%;
12   - display: block;
13   -}
14   -
15   -.controls {
16   - position: absolute;
17   - bottom: 0;
18   - left: 0;
19   - right: 0;
20   - width: 100%;
21   - height: 100%;
22   - display: flex;
23   - flex-direction: column;
24   - justify-content: flex-end;
25   - transition: opacity 0.3s ease;
26   -}
27   -
28   -.controls.hidden {
29   - opacity: 0;
30   -}
31   -
32   -.controls.visible {
33   - opacity: 1;
34   -}
35   -
36   -.overlay {
37   - background: linear-gradient(to top, rgba(0, 0, 0, 0.7) 0%, transparent 100%);
38   - padding: 20px;
39   - display: flex;
40   - flex-direction: column;
41   -}
42   -
43   -.progressBarContainer {
44   - width: 100%;
45   - margin-bottom: 10px;
46   -}
47   -
48   -.controlsContent {
49   - display: flex;
50   - justify-content: space-between;
51   - align-items: center;
52   -}
53   -
54   -.leftControls, .rightControls {
55   - display: flex;
56   - align-items: center;
57   -}
58   -
59   -.playPauseButton, .muteButton, .fullscreenButton {
60   - background: none;
61   - border: none;
62   - color: white;
63   - cursor: pointer;
64   - padding: 4px;
65   - margin-right: 10px;
66   - display: flex;
67   - align-items: center;
68   - justify-content: center;
69   -}
70   -
71   -.playPauseButton:hover, .muteButton:hover, .fullscreenButton:hover {
72   - background-color: rgba(255, 255, 255, 0.1);
73   - border-radius: 50%;
74   -}
75   -
76   -.time {
77   - color: white;
78   - font-size: 14px;
79   - margin-left: 8px;
80   -}
81   -
82   -.volumeControl {
83   - display: flex;
84   - align-items: center;
85   - margin-right: 16px;
86   -}
87   -
88   -.volumeSlider {
89   - width: 60px;
90   - height: 4px;
91   - background: rgba(255, 255, 255, 0.3);
92   - border-radius: 2px;
93   - cursor: pointer;
94   - margin-left: 12px;
95   - position: relative;
96   -}
97   -
98   -.volumeLevel {
99   - position: absolute;
100   - top: 0;
101   - left: 0;
102   - height: 100%;
103   - background: #ffffff;
104   - border-radius: 2px;
105   -}
106   -
107   -.progressBar {
108   - position: relative;
109   - width: 100%;
110   - height: 4px;
111   - background: rgba(255, 255, 255, 0.3);
112   - cursor: pointer;
113   - border-radius: 2px;
114   - overflow: visible;
115   - transition: height 0.2s ease;
116   -}
117   -
118   -.progressBar:hover {
119   - height: 6px;
120   -}
121   -
122   -.progress {
123   - height: 100%;
124   - background: #ffffff;
125   - transition: width 0.1s ease-in-out;
126   -}
127   -
128   -.hoverTimeIndicator {
129   - position: absolute;
130   - bottom: 100%;
131   - transform: translateX(-50%);
132   - background-color: rgba(0, 0, 0, 0.7);
133   - color: white;
134   - padding: 4px 8px;
135   - border-radius: 4px;
136   - font-size: 12px;
137   - pointer-events: none;
138   - white-space: nowrap;
139   - margin-bottom: 8px;
140   -}
141   -
142   -.hoverTimeIndicator::after {
143   - content: '';
144   - position: absolute;
145   - top: 100%;
146   - left: 50%;
147   - margin-left: -4px;
148   - border-width: 4px;
149   - border-style: solid;
150   - border-color: rgba(0, 0, 0, 0.7) transparent transparent transparent;
151   -}
152   -
153   -.controls.smallSize .controlsContent {
154   - justify-content: space-between;
155   -}
156   -
157   -.controls.smallSize .leftControls,
158   -.controls.smallSize .rightControls {
159   - flex: 0 0 auto;
160   - display: flex;
161   - align-items: center;
162   -}
163   -
164   -.controls.smallSize .rightControls {
165   - justify-content: flex-end;
166   -}
167   -
168   -.controls.smallSize .progressBarContainer {
169   - margin-bottom: 4px;
170   -}
171   -
172   -.controls.smallSize .playPauseButton,
173   -.controls.smallSize .muteButton,
174   -.controls.smallSize .fullscreenButton {
175   - padding: 2px;
176   - margin-right: 4px;
177   -}
178   -
179   -.controls.smallSize .playPauseButton svg,
180   -.controls.smallSize .muteButton svg,
181   -.controls.smallSize .fullscreenButton svg {
182   - width: 16px;
183   - height: 16px;
184   -}
185   -
186   -.controls.smallSize .muteButton {
187   - order: -1;
188   -}
1   -import React, { useCallback, useEffect, useRef, useState } from 'react'
2   -import styles from './VideoPlayer.module.css'
3   -
4   -type VideoPlayerProps = {
5   - src: string
6   -}
7   -
8   -const PlayIcon = () => (
9   - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
10   - <path d="M8 5V19L19 12L8 5Z" fill="currentColor"/>
11   - </svg>
12   -)
13   -
14   -const PauseIcon = () => (
15   - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
16   - <path d="M6 19H10V5H6V19ZM14 5V19H18V5H14Z" fill="currentColor"/>
17   - </svg>
18   -)
19   -
20   -const MuteIcon = () => (
21   - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
22   - <path d="M3 9V15H7L12 20V4L7 9H3ZM16.5 12C16.5 10.23 15.48 8.71 14 7.97V16.02C15.48 15.29 16.5 13.77 16.5 12ZM14 3.23V5.29C16.89 6.15 19 8.83 19 12C19 15.17 16.89 17.85 14 18.71V20.77C18.01 19.86 21 16.28 21 12C21 7.72 18.01 4.14 14 3.23Z" fill="currentColor"/>
23   - </svg>
24   -)
25   -
26   -const UnmuteIcon = () => (
27   - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
28   - <path d="M4.34 2.93L2.93 4.34L7.29 8.7L7 9H3V15H7L12 20V13.41L16.18 17.59C15.69 17.96 15.16 18.27 14.58 18.5V20.58C15.94 20.22 17.15 19.56 18.13 18.67L19.66 20.2L21.07 18.79L4.34 2.93ZM10 15.17L7.83 13H5V11H7.83L10 8.83V15.17ZM19 12C19 12.82 18.85 13.61 18.59 14.34L20.12 15.87C20.68 14.7 21 13.39 21 12C21 7.72 18.01 4.14 14 3.23V5.29C16.89 6.15 19 8.83 19 12ZM12 4L10.12 5.88L12 7.76V4ZM16.5 12C16.5 10.23 15.48 8.71 14 7.97V10.18L16.45 12.63C16.48 12.43 16.5 12.22 16.5 12Z" fill="currentColor"/>
29   - </svg>
30   -)
31   -
32   -const FullscreenIcon = () => (
33   - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
34   - <path d="M7 14H5V19H10V17H7V14ZM5 10H7V7H10V5H5V10ZM17 17H14V19H19V14H17V17ZM14 5V7H17V10H19V5H14Z" fill="currentColor"/>
35   - </svg>
36   -)
37   -
38   -const VideoPlayer: React.FC<VideoPlayerProps> = ({ src }) => {
39   - const [isPlaying, setIsPlaying] = useState(false)
40   - const [currentTime, setCurrentTime] = useState(0)
41   - const [duration, setDuration] = useState(0)
42   - const [isMuted, setIsMuted] = useState(false)
43   - const [volume, setVolume] = useState(1)
44   - const [isDragging, setIsDragging] = useState(false)
45   - const [isControlsVisible, setIsControlsVisible] = useState(true)
46   - const [hoverTime, setHoverTime] = useState<number | null>(null)
47   - const videoRef = useRef<HTMLVideoElement>(null)
48   - const progressRef = useRef<HTMLDivElement>(null)
49   - const volumeRef = useRef<HTMLDivElement>(null)
50   - const controlsTimeoutRef = useRef<NodeJS.Timeout | null>(null)
51   - const [isSmallSize, setIsSmallSize] = useState(false)
52   - const containerRef = useRef<HTMLDivElement>(null)
53   -
54   - useEffect(() => {
55   - const video = videoRef.current
56   - if (!video)
57   - return
58   -
59   - const setVideoData = () => {
60   - setDuration(video.duration)
61   - setVolume(video.volume)
62   - }
63   -
64   - const setVideoTime = () => {
65   - setCurrentTime(video.currentTime)
66   - }
67   -
68   - const handleEnded = () => {
69   - setIsPlaying(false)
70   - }
71   -
72   - video.addEventListener('loadedmetadata', setVideoData)
73   - video.addEventListener('timeupdate', setVideoTime)
74   - video.addEventListener('ended', handleEnded)
75   -
76   - return () => {
77   - video.removeEventListener('loadedmetadata', setVideoData)
78   - video.removeEventListener('timeupdate', setVideoTime)
79   - video.removeEventListener('ended', handleEnded)
80   - }
81   - }, [src])
82   -
83   - useEffect(() => {
84   - return () => {
85   - if (controlsTimeoutRef.current)
86   - clearTimeout(controlsTimeoutRef.current)
87   - }
88   - }, [])
89   -
90   - const showControls = useCallback(() => {
91   - setIsControlsVisible(true)
92   - if (controlsTimeoutRef.current)
93   - clearTimeout(controlsTimeoutRef.current)
94   -
95   - controlsTimeoutRef.current = setTimeout(() => setIsControlsVisible(false), 3000)
96   - }, [])
97   -
98   - const togglePlayPause = useCallback(() => {
99   - const video = videoRef.current
100   - if (video) {
101   - if (isPlaying)
102   - video.pause()
103   - else video.play().catch(error => console.error('Error playing video:', error))
104   - setIsPlaying(!isPlaying)
105   - }
106   - }, [isPlaying])
107   -
108   - const toggleMute = useCallback(() => {
109   - const video = videoRef.current
110   - if (video) {
111   - const newMutedState = !video.muted
112   - video.muted = newMutedState
113   - setIsMuted(newMutedState)
114   - setVolume(newMutedState ? 0 : (video.volume > 0 ? video.volume : 1))
115   - video.volume = newMutedState ? 0 : (video.volume > 0 ? video.volume : 1)
116   - }
117   - }, [])
118   -
119   - const toggleFullscreen = useCallback(() => {
120   - const video = videoRef.current
121   - if (video) {
122   - if (document.fullscreenElement)
123   - document.exitFullscreen()
124   - else video.requestFullscreen()
125   - }
126   - }, [])
127   -
128   - const formatTime = (time: number) => {
129   - const minutes = Math.floor(time / 60)
130   - const seconds = Math.floor(time % 60)
131   - return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
132   - }
133   -
134   - const updateVideoProgress = useCallback((clientX: number) => {
135   - const progressBar = progressRef.current
136   - const video = videoRef.current
137   - if (progressBar && video) {
138   - const rect = progressBar.getBoundingClientRect()
139   - const pos = (clientX - rect.left) / rect.width
140   - const newTime = pos * video.duration
141   - if (newTime >= 0 && newTime <= video.duration) {
142   - setHoverTime(newTime)
143   - if (isDragging)
144   - video.currentTime = newTime
145   - }
146   - }
147   - }, [isDragging])
148   -
149   - const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
150   - updateVideoProgress(e.clientX)
151   - }, [updateVideoProgress])
152   -
153   - const handleMouseLeave = useCallback(() => {
154   - if (!isDragging)
155   - setHoverTime(null)
156   - }, [isDragging])
157   -
158   - const handleMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
159   - e.preventDefault()
160   - setIsDragging(true)
161   - updateVideoProgress(e.clientX)
162   - }, [updateVideoProgress])
163   -
164   - useEffect(() => {
165   - const handleGlobalMouseMove = (e: MouseEvent) => {
166   - if (isDragging)
167   - updateVideoProgress(e.clientX)
168   - }
169   -
170   - const handleGlobalMouseUp = () => {
171   - setIsDragging(false)
172   - setHoverTime(null)
173   - }
174   -
175   - if (isDragging) {
176   - document.addEventListener('mousemove', handleGlobalMouseMove)
177   - document.addEventListener('mouseup', handleGlobalMouseUp)
178   - }
179   -
180   - return () => {
181   - document.removeEventListener('mousemove', handleGlobalMouseMove)
182   - document.removeEventListener('mouseup', handleGlobalMouseUp)
183   - }
184   - }, [isDragging, updateVideoProgress])
185   -
186   - const checkSize = useCallback(() => {
187   - if (containerRef.current)
188   - setIsSmallSize(containerRef.current.offsetWidth < 400)
189   - }, [])
190   -
191   - useEffect(() => {
192   - checkSize()
193   - window.addEventListener('resize', checkSize)
194   - return () => window.removeEventListener('resize', checkSize)
195   - }, [checkSize])
196   -
197   - const handleVolumeChange = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
198   - const volumeBar = volumeRef.current
199   - const video = videoRef.current
200   - if (volumeBar && video) {
201   - const rect = volumeBar.getBoundingClientRect()
202   - const newVolume = (e.clientX - rect.left) / rect.width
203   - const clampedVolume = Math.max(0, Math.min(1, newVolume))
204   - video.volume = clampedVolume
205   - setVolume(clampedVolume)
206   - setIsMuted(clampedVolume === 0)
207   - }
208   - }, [])
209   -
210   - return (
211   - <div ref={containerRef} className={styles.videoPlayer} onMouseMove={showControls} onMouseEnter={showControls}>
212   - <video ref={videoRef} src={src} className={styles.video} />
213   - <div className={`${styles.controls} ${isControlsVisible ? styles.visible : styles.hidden} ${isSmallSize ? styles.smallSize : ''}`}>
214   - <div className={styles.overlay}>
215   - <div className={styles.progressBarContainer}>
216   - <div
217   - ref={progressRef}
218   - className={styles.progressBar}
219   - onClick={handleMouseDown}
220   - onMouseMove={handleMouseMove}
221   - onMouseLeave={handleMouseLeave}
222   - onMouseDown={handleMouseDown}
223   - >
224   - <div className={styles.progress} style={{ width: `${(currentTime / duration) * 100}%` }} />
225   - {hoverTime !== null && (
226   - <div
227   - className={styles.hoverTimeIndicator}
228   - style={{ left: `${(hoverTime / duration) * 100}%` }}
229   - >
230   - {formatTime(hoverTime)}
231   - </div>
232   - )}
233   - </div>
234   - </div>
235   - <div className={styles.controlsContent}>
236   - <div className={styles.leftControls}>
237   - <button className={styles.playPauseButton} onClick={togglePlayPause}>
238   - {isPlaying ? <PauseIcon /> : <PlayIcon />}
239   - </button>
240   - {!isSmallSize && (<span className={styles.time}>{formatTime(currentTime)} / {formatTime(duration)}</span>)}
241   - </div>
242   - <div className={styles.rightControls}>
243   - <button className={styles.muteButton} onClick={toggleMute}>
244   - {isMuted ? <UnmuteIcon /> : <MuteIcon />}
245   - </button>
246   - {!isSmallSize && (
247   - <div className={styles.volumeControl}>
248   - <div
249   - ref={volumeRef}
250   - className={styles.volumeSlider}
251   - onClick={handleVolumeChange}
252   - onMouseDown={(e) => {
253   - handleVolumeChange(e)
254   - const handleMouseMove = (e: MouseEvent) => handleVolumeChange(e as unknown as React.MouseEvent<HTMLDivElement>)
255   - const handleMouseUp = () => {
256   - document.removeEventListener('mousemove', handleMouseMove)
257   - document.removeEventListener('mouseup', handleMouseUp)
258   - }
259   - document.addEventListener('mousemove', handleMouseMove)
260   - document.addEventListener('mouseup', handleMouseUp)
261   - }}
262   - >
263   - <div className={styles.volumeLevel} style={{ width: `${volume * 100}%` }} />
264   - </div>
265   - </div>
266   - )}
267   - <button className={styles.fullscreenButton} onClick={toggleFullscreen}>
268   - <FullscreenIcon />
269   - </button>
270   - </div>
271   - </div>
272   - </div>
273   - </div>
274   - </div>
275   - )
276   -}
277   -
278   -export default VideoPlayer
1   -import React from 'react'
2   -import VideoPlayer from './VideoPlayer'
3   -
4   -type Props = {
5   - srcs: string[]
6   -}
7   -
8   -const VideoGallery: React.FC<Props> = ({ srcs }) => {
9   - return (<><br/>{srcs.map((src, index) => (<React.Fragment key={`video_${index}`}><br/><VideoPlayer src={src}/></React.Fragment>))}</>)
10   -}
11   -
12   -export default React.memo(VideoGallery)
1 1 import ReactDOM from 'react-dom/client'
2 2 import "normalize.css"
3 3 import './index.less'
4   -import '@chatui/core/es/styles/index.less';
5   -// 引入样式
6   -import '@chatui/core/dist/index.css';
7 4 import router from './router'
8 5
9 6 import {
... ...
1 1 import React, {useState, useEffect, useRef} from "react";
2 2 import './style.less'
3   -import { Input, Button } from '@chatui/core';
4 3 import Markdown from '@/components/deepSeekIndex/Markdown'
5 4 import banner from "./banner_base.png";
6 5 import bg from "./bg_base.jpg";
7 6 import refresh from "./refresh.png";
8 7 import _ from 'lodash';
  8 +import { DotLoading, Input, Button } from 'antd-mobile'
9 9
10 10 export const getTime = () => {
11 11 // 创建 Date 对象
... ... @@ -97,10 +97,10 @@ const DeepSeekIndex: React.FC = () => {
97 97
98 98 const send = async (_searchValue?: string) => {
99 99 if (disabled) return;
100   - setLoading(true)
101   - setDisabled(true);
102   - console.log('searchValue', searchValue)
103 100 const _value = _searchValue ? _searchValue : searchValue;
  101 + if (!_value) return;
  102 + setDisabled(true);
  103 + setLoading(true)
104 104 const data = {
105 105 "query": _value,
106 106 "inputs": {},
... ... @@ -120,7 +120,6 @@ const DeepSeekIndex: React.FC = () => {
120 120 setChatHistory(newHistory);
121 121 setSearchValue('');
122 122 let _currentResponse = '';
123   -
124 123 try {
125 124 const response = await fetch('http://36.34.99.80:8088/v1/chat-messages', {
126 125 method: 'POST',
... ... @@ -183,13 +182,6 @@ const DeepSeekIndex: React.FC = () => {
183 182 console.log('chatHistory', chatHistory)
184 183 }, [chatHistory])
185 184
186   - const handleKeyDown = (e: any) => {
187   - if (e.key === 'Enter' && !e.shiftKey) {
188   - e.preventDefault(); // 阻止默认换行
189   - send();
190   - }
191   - };
192   -
193 185 return (
194 186 <div className={'deep-seek'}>
195 187 <img className={'deep-seek_bg'} src={bg} alt=""/>
... ... @@ -229,7 +221,9 @@ const DeepSeekIndex: React.FC = () => {
229 221 {
230 222 loading ? <div className="content-left">
231 223 <div className={'current-think'}>
232   - 思考中...
  224 + 思考中<span style={{ fontSize: 14, position: 'relative', top: '8px', left: '-4px' }}>
  225 + <DotLoading />
  226 + </span>
233 227 </div>
234 228 </div> : ''
235 229 }
... ... @@ -246,9 +240,12 @@ const DeepSeekIndex: React.FC = () => {
246 240 className="deep-seek_footer-search"
247 241 placeholder="输入..."
248 242 onChange={(val: string) => setSearchValue(val)}
249   - onKeyDown={handleKeyDown}
  243 + onEnterPress={() => send()}
250 244 />
251   - <Button className={`deep-seek_footer-send ${disabled ? 'disabled' : ''}`} color={'primary'} onClick={() =>send()}>发送</Button>
  245 + <Button className={`deep-seek_footer-send`}
  246 + disabled={disabled}
  247 + onClick={() =>send()}
  248 + >发送</Button>
252 249 </div>
253 250
254 251 </div>
... ...
... ... @@ -47,19 +47,17 @@
47 47 font-size: 16px;
48 48 }
49 49 &-send {
50   - width: 54px;
51   - min-width: 54px;
  50 + width: 54px !important;
  51 + min-width: 54px !important;
52 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   - }
  53 + border-radius: 5px !important;
  54 + height: 32px !important;
  55 + line-height: 32px !important;
  56 + font-size: 14px !important;
  57 + color: #FFFFFF !important;
  58 + text-align: center !important;
  59 + padding: 0 !important;
  60 + border: 0 !important;
63 61 }
64 62 }
65 63
... ... @@ -167,7 +165,6 @@
167 165 display: flex;
168 166 padding-left: 10px;
169 167 .current-think {
170   - display: inline-block;
171 168 background: #fff;
172 169 padding: 12px 16px;
173 170 line-height: 22px;
... ... @@ -176,6 +173,8 @@
176 173 border-radius: 15px;
177 174 font-size: 16px;
178 175 color: #aaa;
  176 + display: flex;
  177 + align-items: center;
179 178
180 179 }
181 180 .mark-down {
... ...
1   -import React from "react";
2   -// 引入组件
3   -import Chat, { Bubble, useMessages } from '@chatui/core';
4   -// 默认快捷短语,可选
5   -const defaultQuickReplies = [
6   - {
7   - icon: 'message',
8   - name: '联系人工服务',
9   - isNew: true,
10   - isHighlight: true,
11   - },
12   - {
13   - name: '短语1',
14   - isNew: true,
15   - },
16   - {
17   - name: '短语2',
18   - isHighlight: true,
19   - },
20   - {
21   - name: '短语3',
22   - },
23   -];
24   -
25   -const initialMessages = [
26   - {
27   - type: 'text',
28   - content: { text: '主人好,我是智能助理,你的贴心小助手~' },
29   - user: { avatar: '//gw.alicdn.com/tfs/TB1DYHLwMHqK1RjSZFEXXcGMXXa-56-62.svg' },
30   - },
31   - {
32   - type: 'image',
33   - content: {
34   - picUrl: '//img.alicdn.com/tfs/TB1p_nirYr1gK0jSZR0XXbP8XXa-300-300.png',
35   - },
36   - },
37   -];
38   -
39   -const Test: React.FC = () => {
40   - // 消息列表
41   - const { messages, appendMsg, setTyping } = useMessages(initialMessages);
42   -// 发送回调
43   - function handleSend(type: any, val: any) {
44   - if (type === 'text' && val.trim()) {
45   - // TODO: 发送请求
46   - appendMsg({
47   - type: 'text',
48   - content: { text: val },
49   - position: 'right',
50   - });
51   -
52   - setTyping(true);
53   -
54   - // 模拟回复消息
55   - setTimeout(() => {
56   - appendMsg({
57   - type: 'text',
58   - content: { text: '亲,您遇到什么问题啦?请简要描述您的问题~' },
59   - });
60   - }, 1000);
61   - }
62   - }
63   -
64   - // 快捷短语回调,可根据 item 数据做出不同的操作,这里以发送文本消息为例
65   - function handleQuickReplyClick(item: any) {
66   - handleSend('text', item.name);
67   - }
68   -
69   - function renderMessageContent(msg: any) {
70   - const { type, content } = msg;
71   -
72   - // 根据消息类型来渲染
73   - switch (type) {
74   - case 'text':
75   - return <Bubble content={content.text} />;
76   - case 'image':
77   - return (
78   - <Bubble type="image">
79   - <img src={content.picUrl} alt="" />
80   - </Bubble>
81   - );
82   - default:
83   - return null;
84   - }
85   - }
86   - return(
87   - <Chat
88   - navbar={{ title: '智能助理' }}
89   - messages={messages}
90   - renderMessageContent={renderMessageContent}
91   - quickReplies={defaultQuickReplies}
92   - onQuickReplyClick={handleQuickReplyClick}
93   - onSend={handleSend}
94   - />
95   - );
96   -};
97   -
98   -export default Test;
1 1 import {createHashRouter} from 'react-router-dom';
2 2 import ErrorPage from "./pages/errorPage";
3 3 import DeepSeekIndex from "./pages/deep-seek-index";
4   -import Test from "./pages/test";
5 4
6 5 const routers = [
7 6 {
... ... @@ -13,12 +12,7 @@ const routers = [
13 12 path: '/deepSeekIndex',
14 13 element: <DeepSeekIndex />,
15 14 errorElement: <ErrorPage />,
16   - },
17   - {
18   - path: '/test',
19   - element: <Test />,
20   - errorElement: <ErrorPage />,
21   - },
  15 + }
22 16 ];
23 17
24 18 export default createHashRouter(routers)
\ No newline at end of file
... ...