Commit 308bda69e1b39ce3640785573ea13f8d90eb2467
1 parent
3394b6c8
feat: 徽智制联-deepSeek-增加loading效果+部分样式调整
Showing
13 changed files
with
61 additions
and
655 deletions
@@ -10,20 +10,15 @@ | @@ -10,20 +10,15 @@ | ||
10 | "preview": "vite preview" | 10 | "preview": "vite preview" |
11 | }, | 11 | }, |
12 | "dependencies": { | 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 | "axios": "^1.7.3", | 15 | "axios": "^1.7.3", |
16 | - "cesium": "^1.124.0", | ||
17 | - "echarts": "^5.5.1", | ||
18 | - "echarts-for-react": "^3.0.2", | ||
19 | "js-cookie": "^3.0.5", | 16 | "js-cookie": "^3.0.5", |
20 | "lodash": "^4.17.11", | 17 | "lodash": "^4.17.11", |
21 | "lodash-es": "^4.17.21", | 18 | "lodash-es": "^4.17.21", |
22 | "normalize.css": "^8.0.1", | 19 | "normalize.css": "^8.0.1", |
23 | - "rc-pagination": "^4.2.0", | ||
24 | "react": "^18.3.1", | 20 | "react": "^18.3.1", |
25 | "react-dom": "^18.3.1", | 21 | "react-dom": "^18.3.1", |
26 | - "react-echarts": "^0.1.1", | ||
27 | "react-markdown": "^9.0.1", | 22 | "react-markdown": "^9.0.1", |
28 | "react-router-dom": "^6.26.0", | 23 | "react-router-dom": "^6.26.0", |
29 | "react-syntax-highlighter": "^15.6.1", | 24 | "react-syntax-highlighter": "^15.6.1", |
@@ -35,7 +30,6 @@ | @@ -35,7 +30,6 @@ | ||
35 | "swiper": "^8.4.4" | 30 | "swiper": "^8.4.4" |
36 | }, | 31 | }, |
37 | "devDependencies": { | 32 | "devDependencies": { |
38 | - "@types/echarts": "^4.9.22", | ||
39 | "@types/js-cookie": "^3.0.6", | 33 | "@types/js-cookie": "^3.0.6", |
40 | "@types/lodash": "^4.17.7", | 34 | "@types/lodash": "^4.17.7", |
41 | "@types/lodash-es": "^4.17.12", | 35 | "@types/lodash-es": "^4.17.12", |
@@ -15,7 +15,6 @@ import SyntaxHighlighter from 'react-syntax-highlighter' | @@ -15,7 +15,6 @@ import SyntaxHighlighter from 'react-syntax-highlighter' | ||
15 | import MarkdownButton from './markdown-button' | 15 | import MarkdownButton from './markdown-button' |
16 | import MarkdownForm from './markdown-form' | 16 | import MarkdownForm from './markdown-form' |
17 | import ThinkBlock from './markdown-block' | 17 | import ThinkBlock from './markdown-block' |
18 | -import VideoGallery from './videoGallery' | ||
19 | 18 | ||
20 | interface MarkdownProps { | 19 | interface MarkdownProps { |
21 | content: string | 20 | content: string |
@@ -164,7 +163,8 @@ const VideoBlock: any = memo(({ node }: any) => { | @@ -164,7 +163,8 @@ const VideoBlock: any = memo(({ node }: any) => { | ||
164 | const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src) | 163 | const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src) |
165 | if (srcs.length === 0) | 164 | if (srcs.length === 0) |
166 | return null | 165 | return null |
167 | - return <VideoGallery key={srcs.join()} srcs={srcs} /> | 166 | + // return <VideoGallery key={srcs.join()} srcs={srcs} /> |
167 | + return '' | ||
168 | }) | 168 | }) |
169 | VideoBlock.displayName = 'VideoBlock' | 169 | VideoBlock.displayName = 'VideoBlock' |
170 | 170 |
@@ -39,7 +39,7 @@ const useThinkTimer = (children: any) => { | @@ -39,7 +39,7 @@ const useThinkTimer = (children: any) => { | ||
39 | const [startTime] = useState(Date.now()) | 39 | const [startTime] = useState(Date.now()) |
40 | const [elapsedTime, setElapsedTime] = useState(0) | 40 | const [elapsedTime, setElapsedTime] = useState(0) |
41 | const [isComplete, setIsComplete] = useState(false) | 41 | const [isComplete, setIsComplete] = useState(false) |
42 | - const timerRef = useRef<NodeJS.Timeout>() | 42 | + const timerRef = useRef<number>() |
43 | 43 | ||
44 | useEffect(() => { | 44 | useEffect(() => { |
45 | timerRef.current = setInterval(() => { | 45 | timerRef.current = setInterval(() => { |
1 | -import { Button } from 'antd'; | 1 | +import { Button } from 'antd-mobile'; |
2 | import './base.less' | 2 | import './base.less' |
3 | import './style.less' | 3 | import './style.less' |
4 | 4 | ||
@@ -19,7 +19,7 @@ const MarkdownButton = ({ node }: any) => { | @@ -19,7 +19,7 @@ const MarkdownButton = ({ node }: any) => { | ||
19 | } | 19 | } |
20 | 20 | ||
21 | return <Button | 21 | return <Button |
22 | - variant={variant} | 22 | + color={variant} |
23 | size={size} | 23 | size={size} |
24 | onClick={() => { | 24 | onClick={() => { |
25 | if (is_valid_url(link)) { | 25 | if (is_valid_url(link)) { |
@@ -7,8 +7,9 @@ import { | @@ -7,8 +7,9 @@ import { | ||
7 | Input, | 7 | Input, |
8 | DatePicker, | 8 | DatePicker, |
9 | Checkbox, | 9 | Checkbox, |
10 | - Select | ||
11 | -} from 'antd'; | 10 | + Selector, |
11 | + TextArea | ||
12 | +} from 'antd-mobile'; | ||
12 | 13 | ||
13 | enum DATA_FORMAT { | 14 | enum DATA_FORMAT { |
14 | TEXT = 'text', | 15 | TEXT = 'text', |
@@ -95,7 +96,7 @@ const MarkdownForm = ({ node }: any) => { | @@ -95,7 +96,7 @@ const MarkdownForm = ({ node }: any) => { | ||
95 | <DatePicker | 96 | <DatePicker |
96 | key={index} | 97 | key={index} |
97 | value={formValues[child.properties.name]} | 98 | value={formValues[child.properties.name]} |
98 | - onChange={(date) => { | 99 | + onSelect={(date) => { |
99 | setFormValues(prevValues => ({ | 100 | setFormValues(prevValues => ({ |
100 | ...prevValues, | 101 | ...prevValues, |
101 | [child.properties.name]: date, | 102 | [child.properties.name]: date, |
@@ -123,30 +124,30 @@ const MarkdownForm = ({ node }: any) => { | @@ -123,30 +124,30 @@ const MarkdownForm = ({ node }: any) => { | ||
123 | } | 124 | } |
124 | if (child.properties.type === SUPPORTED_TYPES.SELECT) { | 125 | if (child.properties.type === SUPPORTED_TYPES.SELECT) { |
125 | return ( | 126 | return ( |
126 | - <Select | 127 | + <Selector |
127 | key={index} | 128 | key={index} |
128 | className="w-full" | 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 | defaultValue={formValues[child.properties.name]} | 146 | defaultValue={formValues[child.properties.name]} |
146 | - onSelect={(item) => { | 147 | + onChange={(item) => { |
147 | setFormValues(prevValues => ({ | 148 | setFormValues(prevValues => ({ |
148 | ...prevValues, | 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,10 +161,10 @@ const MarkdownForm = ({ node }: any) => { | ||
160 | name={child.properties.name} | 161 | name={child.properties.name} |
161 | placeholder={child.properties.placeholder} | 162 | placeholder={child.properties.placeholder} |
162 | value={formValues[child.properties.name]} | 163 | value={formValues[child.properties.name]} |
163 | - onChange={(e) => { | 164 | + onChange={(val: string) => { |
164 | setFormValues(prevValues => ({ | 165 | setFormValues(prevValues => ({ |
165 | ...prevValues, | 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,15 +172,15 @@ const MarkdownForm = ({ node }: any) => { | ||
171 | } | 172 | } |
172 | if (child.tagName === SUPPORTED_TAGS.TEXTAREA) { | 173 | if (child.tagName === SUPPORTED_TAGS.TEXTAREA) { |
173 | return ( | 174 | return ( |
174 | - <Input.TextArea | 175 | + <TextArea |
175 | key={index} | 176 | key={index} |
176 | name={child.properties.name} | 177 | name={child.properties.name} |
177 | placeholder={child.properties.placeholder} | 178 | placeholder={child.properties.placeholder} |
178 | value={formValues[child.properties.name]} | 179 | value={formValues[child.properties.name]} |
179 | - onChange={(e) => { | 180 | + onChange={(val: string) => { |
180 | setFormValues(prevValues => ({ | 181 | setFormValues(prevValues => ({ |
181 | ...prevValues, | 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,7 +192,7 @@ const MarkdownForm = ({ node }: any) => { | ||
191 | 192 | ||
192 | return ( | 193 | return ( |
193 | <Button | 194 | <Button |
194 | - variant={variant} | 195 | + color={variant} |
195 | size={size} | 196 | size={size} |
196 | className='mt-4' | 197 | className='mt-4' |
197 | key={index} | 198 | key={index} |
src/components/deepSeekIndex/videoGallery/VideoPlayer.module.css
deleted
100644 → 0
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 | -} |
src/components/deepSeekIndex/videoGallery/VideoPlayer.tsx
deleted
100644 → 0
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 |
src/components/deepSeekIndex/videoGallery/index.tsx
deleted
100644 → 0
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 | import ReactDOM from 'react-dom/client' | 1 | import ReactDOM from 'react-dom/client' |
2 | import "normalize.css" | 2 | import "normalize.css" |
3 | import './index.less' | 3 | import './index.less' |
4 | -import '@chatui/core/es/styles/index.less'; | ||
5 | -// 引入样式 | ||
6 | -import '@chatui/core/dist/index.css'; | ||
7 | import router from './router' | 4 | import router from './router' |
8 | 5 | ||
9 | import { | 6 | import { |
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 { Input, Button } from '@chatui/core'; | ||
4 | import Markdown from '@/components/deepSeekIndex/Markdown' | 3 | import Markdown from '@/components/deepSeekIndex/Markdown' |
5 | import banner from "./banner_base.png"; | 4 | import banner from "./banner_base.png"; |
6 | import bg from "./bg_base.jpg"; | 5 | import bg from "./bg_base.jpg"; |
7 | import refresh from "./refresh.png"; | 6 | import refresh from "./refresh.png"; |
8 | import _ from 'lodash'; | 7 | import _ from 'lodash'; |
8 | +import { DotLoading, Input, Button } from 'antd-mobile' | ||
9 | 9 | ||
10 | export const getTime = () => { | 10 | export const getTime = () => { |
11 | // 创建 Date 对象 | 11 | // 创建 Date 对象 |
@@ -97,10 +97,10 @@ const DeepSeekIndex: React.FC = () => { | @@ -97,10 +97,10 @@ const DeepSeekIndex: React.FC = () => { | ||
97 | 97 | ||
98 | const send = async (_searchValue?: string) => { | 98 | const send = async (_searchValue?: string) => { |
99 | if (disabled) return; | 99 | if (disabled) return; |
100 | - setLoading(true) | ||
101 | - setDisabled(true); | ||
102 | - console.log('searchValue', searchValue) | ||
103 | const _value = _searchValue ? _searchValue : searchValue; | 100 | const _value = _searchValue ? _searchValue : searchValue; |
101 | + if (!_value) return; | ||
102 | + setDisabled(true); | ||
103 | + setLoading(true) | ||
104 | const data = { | 104 | const data = { |
105 | "query": _value, | 105 | "query": _value, |
106 | "inputs": {}, | 106 | "inputs": {}, |
@@ -120,7 +120,6 @@ const DeepSeekIndex: React.FC = () => { | @@ -120,7 +120,6 @@ const DeepSeekIndex: React.FC = () => { | ||
120 | setChatHistory(newHistory); | 120 | setChatHistory(newHistory); |
121 | setSearchValue(''); | 121 | setSearchValue(''); |
122 | let _currentResponse = ''; | 122 | let _currentResponse = ''; |
123 | - | ||
124 | try { | 123 | try { |
125 | const response = await fetch('http://36.34.99.80:8088/v1/chat-messages', { | 124 | const response = await fetch('http://36.34.99.80:8088/v1/chat-messages', { |
126 | method: 'POST', | 125 | method: 'POST', |
@@ -183,13 +182,6 @@ const DeepSeekIndex: React.FC = () => { | @@ -183,13 +182,6 @@ const DeepSeekIndex: React.FC = () => { | ||
183 | console.log('chatHistory', chatHistory) | 182 | console.log('chatHistory', chatHistory) |
184 | }, [chatHistory]) | 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 | return ( | 185 | return ( |
194 | <div className={'deep-seek'}> | 186 | <div className={'deep-seek'}> |
195 | <img className={'deep-seek_bg'} src={bg} alt=""/> | 187 | <img className={'deep-seek_bg'} src={bg} alt=""/> |
@@ -229,7 +221,9 @@ const DeepSeekIndex: React.FC = () => { | @@ -229,7 +221,9 @@ const DeepSeekIndex: React.FC = () => { | ||
229 | { | 221 | { |
230 | loading ? <div className="content-left"> | 222 | loading ? <div className="content-left"> |
231 | <div className={'current-think'}> | 223 | <div className={'current-think'}> |
232 | - 思考中... | 224 | + 思考中<span style={{ fontSize: 14, position: 'relative', top: '8px', left: '-4px' }}> |
225 | + <DotLoading /> | ||
226 | + </span> | ||
233 | </div> | 227 | </div> |
234 | </div> : '' | 228 | </div> : '' |
235 | } | 229 | } |
@@ -246,9 +240,12 @@ const DeepSeekIndex: React.FC = () => { | @@ -246,9 +240,12 @@ const DeepSeekIndex: React.FC = () => { | ||
246 | className="deep-seek_footer-search" | 240 | className="deep-seek_footer-search" |
247 | placeholder="输入..." | 241 | placeholder="输入..." |
248 | onChange={(val: string) => setSearchValue(val)} | 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 | </div> | 249 | </div> |
253 | 250 | ||
254 | </div> | 251 | </div> |
@@ -47,19 +47,17 @@ | @@ -47,19 +47,17 @@ | ||
47 | font-size: 16px; | 47 | font-size: 16px; |
48 | } | 48 | } |
49 | &-send { | 49 | &-send { |
50 | - width: 54px; | ||
51 | - min-width: 54px; | 50 | + width: 54px !important; |
51 | + min-width: 54px !important; | ||
52 | background: #F53F3F !important; | 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,7 +165,6 @@ | ||
167 | display: flex; | 165 | display: flex; |
168 | padding-left: 10px; | 166 | padding-left: 10px; |
169 | .current-think { | 167 | .current-think { |
170 | - display: inline-block; | ||
171 | background: #fff; | 168 | background: #fff; |
172 | padding: 12px 16px; | 169 | padding: 12px 16px; |
173 | line-height: 22px; | 170 | line-height: 22px; |
@@ -176,6 +173,8 @@ | @@ -176,6 +173,8 @@ | ||
176 | border-radius: 15px; | 173 | border-radius: 15px; |
177 | font-size: 16px; | 174 | font-size: 16px; |
178 | color: #aaa; | 175 | color: #aaa; |
176 | + display: flex; | ||
177 | + align-items: center; | ||
179 | 178 | ||
180 | } | 179 | } |
181 | .mark-down { | 180 | .mark-down { |
src/pages/test.tsx
deleted
100644 → 0
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 | import {createHashRouter} from 'react-router-dom'; | 1 | import {createHashRouter} from 'react-router-dom'; |
2 | import ErrorPage from "./pages/errorPage"; | 2 | import ErrorPage from "./pages/errorPage"; |
3 | import DeepSeekIndex from "./pages/deep-seek-index"; | 3 | import DeepSeekIndex from "./pages/deep-seek-index"; |
4 | -import Test from "./pages/test"; | ||
5 | 4 | ||
6 | const routers = [ | 5 | const routers = [ |
7 | { | 6 | { |
@@ -13,12 +12,7 @@ const routers = [ | @@ -13,12 +12,7 @@ const routers = [ | ||
13 | path: '/deepSeekIndex', | 12 | path: '/deepSeekIndex', |
14 | element: <DeepSeekIndex />, | 13 | element: <DeepSeekIndex />, |
15 | errorElement: <ErrorPage />, | 14 | errorElement: <ErrorPage />, |
16 | - }, | ||
17 | - { | ||
18 | - path: '/test', | ||
19 | - element: <Test />, | ||
20 | - errorElement: <ErrorPage />, | ||
21 | - }, | 15 | + } |
22 | ]; | 16 | ]; |
23 | 17 | ||
24 | export default createHashRouter(routers) | 18 | export default createHashRouter(routers) |