Showing
8 changed files
with
236 additions
and
126 deletions
... | ... | @@ -38,4 +38,16 @@ export function getFilePreview(fileId?: string) { |
38 | 38 | { |
39 | 39 | } |
40 | 40 | ); |
41 | +} | |
42 | + | |
43 | +/**生产线信息--轮播图设置 | |
44 | + * **/ | |
45 | +export function getCarouselSettings() { | |
46 | + return post( | |
47 | + `/open/qx-apaas-lowcode/getCarouselSettings`, | |
48 | + { | |
49 | + }, | |
50 | + { | |
51 | + } | |
52 | + ); | |
41 | 53 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -6,7 +6,7 @@ import { useNavigate } from 'react-router-dom'; |
6 | 6 | interface NavBarProps { |
7 | 7 | rightInfo?: any; |
8 | 8 | showBack?: any; |
9 | - title?: any; | |
9 | + title?: string; | |
10 | 10 | isWhite?: any; // 是否是白色返回箭头 |
11 | 11 | style?: any; |
12 | 12 | backContent?: any; |
... | ... | @@ -27,6 +27,9 @@ const NavBarSec: React.FC<NavBarProps> = (props) => { |
27 | 27 | |
28 | 28 | const style = props?.style; |
29 | 29 | |
30 | + useEffect(() => { | |
31 | + window.scrollTo(0, 0); | |
32 | + }, []) | |
30 | 33 | |
31 | 34 | const back = () => { |
32 | 35 | navigate(-1); // 回退到上一个页面 | ... | ... |
src/components/videoPlay.tsx
0 → 100644
1 | +import React, {useEffect, useRef, useState} from 'react'; | |
2 | + | |
3 | +type VideoPlayProps = { | |
4 | + key: string; | |
5 | + url: string; | |
6 | + className: string; | |
7 | +}; | |
8 | +// 视频自动播放组件 | |
9 | +const VideoPlay: React.FC<VideoPlayProps> = (props) => { | |
10 | + | |
11 | + const videoRef = useRef<HTMLVideoElement>(null); | |
12 | + const [isPlaying, setIsPlaying] = useState(true); | |
13 | + | |
14 | + useEffect(() => { | |
15 | + if (videoRef.current) { | |
16 | + videoRef.current.onended = () => { | |
17 | + setIsPlaying(true); | |
18 | + }; | |
19 | + } | |
20 | + }, [props?.key]); | |
21 | + | |
22 | + useEffect(() => { | |
23 | + if (isPlaying && videoRef.current) { | |
24 | + videoRef.current.play().catch(() => { | |
25 | + setIsPlaying(false); | |
26 | + }); | |
27 | + } | |
28 | + }, [isPlaying]); | |
29 | + | |
30 | + return ( | |
31 | + <> | |
32 | + { | |
33 | + props?.url ? <video | |
34 | + id={props?.url} | |
35 | + ref={videoRef} | |
36 | + autoPlay | |
37 | + loop | |
38 | + muted | |
39 | + playsInline | |
40 | + className={`${props?.className}`} | |
41 | + > | |
42 | + <source src={props?.url} type="video/mp4" /> | |
43 | + </video> : '' | |
44 | + } | |
45 | + </> | |
46 | + ); | |
47 | +}; | |
48 | + | |
49 | +export default VideoPlay; | ... | ... |
1 | -import React from 'react' | |
1 | +import React, {useEffect, useState} from 'react' | |
2 | 2 | import './style.less' |
3 | 3 | import NavBar from '@/components/nav-bar' |
4 | -import {baseColorPrimary} from "@/utils/common"; | |
5 | -import {hexToRgba} from "@/utils/utils"; | |
4 | +import VideoPlay from '@/components/videoPlay' | |
5 | +import {useSearchParams} from "react-router-dom"; | |
6 | +import { Document, Page, pdfjs } from "react-pdf"; | |
7 | +import 'react-pdf/dist/esm/Page/AnnotationLayer.css' | |
8 | +import 'react-pdf/dist/esm/Page/TextLayer.css'; | |
9 | +import {getCarouselSettings} from "@/api/apiConfig"; | |
10 | +pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`; | |
6 | 11 | |
7 | 12 | |
8 | -const ProductionManagement: React.FC = () => { | |
13 | +const ProductionDetail: React.FC = () => { | |
14 | + const [title, setTitle] = useState<string>(''); | |
15 | + const [filePathItem, setFilePathItem] = useState(); | |
16 | + // 自动预览 定时器 | |
17 | + const [preViewTimer, setPreViewTimer] = useState<any>(null); | |
9 | 18 | |
10 | - const list = [ | |
11 | - { | |
12 | - id: '1', | |
13 | - name: '车顶生产线A' | |
14 | - }, | |
15 | - { | |
16 | - id: '2', | |
17 | - name: '车顶生产线B' | |
18 | - }, | |
19 | - { | |
20 | - id: '3', | |
21 | - name: '车顶生产线C' | |
22 | - }, | |
23 | - { | |
24 | - id: '4', | |
25 | - name: '车顶生产线D' | |
26 | - }, | |
27 | - { | |
28 | - id: '5', | |
29 | - name: '车顶生产线E' | |
30 | - }, | |
31 | - { | |
32 | - id: '6', | |
33 | - name: '车顶生产线F' | |
34 | - }, | |
35 | - { | |
36 | - id: '7', | |
37 | - name: '车顶生产线G' | |
38 | - }, | |
39 | - { | |
40 | - id: '8', | |
41 | - name: '车顶生产线H' | |
42 | - }, | |
43 | - { | |
44 | - id: '9', | |
45 | - name: '车顶生产线I' | |
46 | - }, | |
47 | - { | |
48 | - id: '10', | |
49 | - name: '车顶生产线J' | |
50 | - }, | |
51 | - { | |
52 | - id: '11', | |
53 | - name: '车顶生产线K' | |
54 | - }, | |
55 | - { | |
56 | - id: '12', | |
57 | - name: '车顶生车顶生车顶生产线L' | |
58 | - }, | |
59 | - { | |
60 | - id: '13', | |
61 | - name: '车顶生产线M' | |
62 | - }, | |
63 | - { | |
64 | - id: '14', | |
65 | - name: '车顶生产线N' | |
19 | + // // 因是hook,必须写在组件的顶部执行,useSearchParams() 返回的是数组 | |
20 | + const [params] = useSearchParams(); | |
21 | + // // 通过 get 方法获取目标参数 | |
22 | + const name = params.get("name") || ""; | |
23 | + | |
24 | + const sxPreViewList = localStorage.getItem('sxPreViewListStorage') ? JSON.parse(localStorage.getItem('sxPreViewListStorage')) : []; | |
25 | + | |
26 | + console.log('sxPreViewList', sxPreViewList) | |
27 | + | |
28 | + useEffect(() => { | |
29 | + if (sxPreViewList?.length) { | |
30 | + const _isLoop = sxPreViewList?.length > 1; | |
31 | + const loopPreView = (index: number, interval: number) => { | |
32 | + setFilePathItem(sxPreViewList?.[index]); | |
33 | + clearInterval(preViewTimer); | |
34 | + // 3秒调一次 | |
35 | + let _preViewTimer = setInterval(() => { | |
36 | + index = index + 1; | |
37 | + if (index > sxPreViewList?.length - 1) { | |
38 | + index = 0; | |
39 | + } | |
40 | + setFilePathItem(sxPreViewList?.[index]); | |
41 | + }, interval * 1000); | |
42 | + setPreViewTimer(_preViewTimer); | |
43 | + } | |
44 | + if (_isLoop) { | |
45 | + getCarouselSettings().then((res: any) => { | |
46 | + const interval = res?.carousel?.interval || 10; | |
47 | + loopPreView(0, interval); | |
48 | + }).catch(() => { | |
49 | + loopPreView(0, 10) | |
50 | + }) | |
51 | + } else { | |
52 | + setFilePathItem(sxPreViewList?.[0]); | |
53 | + } | |
54 | + } | |
55 | + }, [JSON.stringify(sxPreViewList)]) | |
56 | + | |
57 | + useEffect(() => { | |
58 | + if (name) { | |
59 | + setTitle(name) | |
66 | 60 | } |
67 | - ] | |
61 | + }, [name]) | |
62 | + | |
63 | + useEffect(() => { | |
64 | + return () => { | |
65 | + clearInterval(preViewTimer); | |
66 | + }; | |
67 | + }, []) | |
68 | + | |
69 | + useEffect(() => { | |
70 | + console.log('filePathItem', filePathItem) | |
71 | + }, [filePathItem]) | |
72 | + | |
68 | 73 | |
69 | 74 | |
70 | 75 | return ( |
71 | 76 | <div className={'sxjx-content-main sxjx-layout-main-unfoot'}> |
72 | - <div className={'production-management'}> | |
73 | - <NavBar title={'上产线管理'}/> | |
74 | - <div className={'production-management_list'}> | |
75 | - { | |
76 | - list?.map((item: any) => { | |
77 | - return <div | |
78 | - key={item?.id} | |
79 | - className={'production-management_list-item'} | |
80 | - style={{backgroundColor: hexToRgba(baseColorPrimary, 0.6)}} | |
81 | - > | |
82 | - {item?.name} | |
83 | - </div> | |
84 | - }) | |
85 | - } | |
86 | - </div> | |
77 | + <div className={'production-detail'}> | |
78 | + <NavBar showBack={true} title={title}/> | |
79 | + { | |
80 | + filePathItem?.type === 'mp4' ? <div className={'production-detail-video-box'}> | |
81 | + <VideoPlay | |
82 | + key={filePathItem?.url} | |
83 | + url={filePathItem?.url || ''} | |
84 | + className={'production-detail-video'} | |
85 | + /> | |
86 | + </div> : <> | |
87 | + </> | |
88 | + } | |
87 | 89 | </div> |
88 | 90 | </div> |
89 | 91 | ) |
90 | 92 | } |
91 | 93 | |
92 | -export default ProductionManagement; | |
94 | +export default ProductionDetail; | ... | ... |
1 | -.production-management { | |
1 | +.production-detail { | |
2 | 2 | background: #f7f7f7; |
3 | - | |
4 | - &_list { | |
5 | - padding: 60px; | |
3 | + position: relative; | |
4 | + width: 100%; | |
5 | + height: calc(100vh - 88px); | |
6 | + &-video-box { | |
7 | + position: absolute; | |
8 | + top: 0; | |
9 | + left: 0; | |
10 | + right: 0; | |
11 | + bottom: 0; | |
6 | 12 | display: flex; |
7 | - flex-wrap: wrap; | |
8 | - | |
9 | - &-item { | |
10 | - font-size: 28px; | |
11 | - width: 312px; | |
12 | - height: 312px; | |
13 | - color: #fff; | |
14 | - margin-right: 60px; | |
15 | - margin-bottom: 60px; | |
16 | - cursor: pointer; | |
17 | - border-radius: 12px; | |
18 | - display: flex; | |
19 | - align-items: center; | |
20 | - justify-content: center; | |
21 | - line-height: 40px; | |
22 | - padding: 20px; | |
23 | - box-sizing: border-box; | |
24 | - text-align: center; | |
25 | - | |
26 | - &:nth-child(5n) { | |
27 | - margin-right: 0; | |
28 | - } | |
29 | - } | |
13 | + align-items: center; | |
14 | + justify-content: center; | |
15 | + } | |
16 | + &-video { | |
17 | + width: 100%; | |
18 | + max-width: 100%; | |
19 | + max-height: 100%; | |
20 | + height: auto; | |
30 | 21 | } |
31 | 22 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -7,16 +7,18 @@ import _preview from './preview.png'; |
7 | 7 | |
8 | 8 | import {useSearchParams} from "react-router-dom"; |
9 | 9 | import {getFilePreview, getProductBook} from "@/api/apiConfig"; |
10 | -import {SpinLoading, Checkbox} from 'antd-mobile' | |
10 | +import {SpinLoading, Checkbox, Toast} from 'antd-mobile' | |
11 | 11 | import { useNavigate } from "react-router-dom"; |
12 | +import {a} from "vite/dist/node/types.d-aGj9QkWt"; | |
12 | 13 | |
13 | 14 | interface ListType { |
14 | 15 | id: string; |
16 | + fileId: string; | |
15 | 17 | name: string; |
16 | 18 | type: 'pdf' | 'mp4' | string |
17 | 19 | } |
18 | 20 | |
19 | -type CheckboxValue = string | number | |
21 | +type CheckboxValue = string; | |
20 | 22 | |
21 | 23 | const ProductionList: React.FC = () => { |
22 | 24 | const [title, setTitle] = useState<string>(''); |
... | ... | @@ -26,6 +28,7 @@ const ProductionList: React.FC = () => { |
26 | 28 | const navigate = useNavigate(); |
27 | 29 | const [checkItems, setCheckItems] = useState<CheckboxValue[]>([]) |
28 | 30 | const [checkValues, setCheckValues] = useState<CheckboxValue[]>([]) |
31 | + // const [sxPreViewList, setSxPreViewList] = useState<ListType[]>([]) | |
29 | 32 | |
30 | 33 | |
31 | 34 | // 因是hook,必须写在组件的顶部执行,useSearchParams() 返回的是数组 |
... | ... | @@ -36,6 +39,10 @@ const ProductionList: React.FC = () => { |
36 | 39 | const id = params.get("id") || ""; |
37 | 40 | |
38 | 41 | useEffect(() => { |
42 | + localStorage.setItem('sxPreViewListStorage', ''); | |
43 | + }, []) | |
44 | + | |
45 | + useEffect(() => { | |
39 | 46 | if (name) { |
40 | 47 | setTitle(name) |
41 | 48 | } |
... | ... | @@ -51,7 +58,8 @@ const ProductionList: React.FC = () => { |
51 | 58 | _checkItems.push(item?.guide_book_file_info_?.[0]?.fileId); |
52 | 59 | const _arr = item?.guide_book_file_info_?.[0]?.name?.split('.') || []; |
53 | 60 | return { |
54 | - id: item?.guide_book_file_info_?.[0]?.fileId || '', | |
61 | + id: item?.id || '', | |
62 | + fileId: item?.guide_book_file_info_?.[0]?.fileId || '', | |
55 | 63 | name: _arr?.[0] || '', |
56 | 64 | type: _arr?.[1] || '', |
57 | 65 | } |
... | ... | @@ -78,20 +86,58 @@ const ProductionList: React.FC = () => { |
78 | 86 | console.log('checkboxChange-value', value) |
79 | 87 | setCheckValues(value) |
80 | 88 | } |
81 | - const toDetail = (ids: CheckboxValue[]) => { | |
89 | + | |
90 | + // 假设这是你的API调用函数 | |
91 | + const fetchData = async (item: ListType) => { | |
92 | + // 替换为你的实际API URL和请求逻辑 | |
93 | + const res = await getFilePreview(item?.fileId); | |
94 | + return res; | |
95 | + } | |
96 | + | |
97 | + | |
98 | + const toDetail = async (ids: CheckboxValue[]) => { | |
82 | 99 | console.log('toDetail-ids', ids) |
83 | - if (ids?.[0]) { | |
84 | - const _id: string = ids?.[0].toString() || ''; | |
85 | - getFilePreview(_id).then((res: any) => { | |
86 | - console.log('res', res) | |
87 | - window.open(res, '_blank') | |
88 | - }).catch((err: any) => { | |
89 | - console.log('err', err) | |
100 | + if (ids?.length) { | |
101 | + let _arr = list?.filter((item: ListType) => ids?.includes(item?.id || '')) || []; | |
102 | + console.log('_arr', _arr) | |
103 | + // 创建一个promise数组,每个promise都是对API的一次调用 | |
104 | + const promises = _arr?.map((item: ListType) => fetchData(item)); | |
105 | + // 使用Promise.all来并行地解决这些promise | |
106 | + Promise.all(promises) | |
107 | + .then((results: any) => { | |
108 | + let sxPreViewList = _arr?.map((it: ListType, index: number) => { | |
109 | + return { | |
110 | + ...it, | |
111 | + url: results?.[index] | |
112 | + } | |
113 | + }) | |
114 | + console.log('sxPreViewList', sxPreViewList) | |
115 | + localStorage.setItem('sxPreViewListStorage', JSON.stringify(sxPreViewList)); | |
116 | + navigate(`/production/detail?name=${sxPreViewList?.[0]?.name}`); | |
117 | + }) | |
118 | + .catch(err => { | |
119 | + localStorage.setItem('sxPreViewListStorage', ''); | |
120 | + console.log('err', err) | |
121 | + }); | |
122 | + } else { | |
123 | + localStorage.setItem('sxPreViewListStorage', ''); | |
124 | + Toast.show({ | |
125 | + content: '请先选择数据!', | |
126 | + maskClassName: 'to-detail-mask', | |
90 | 127 | }) |
91 | 128 | } |
92 | - // navigate(`/production/detail?id=${item?.id}&name=${item?.name}`); | |
129 | + | |
93 | 130 | } |
94 | 131 | |
132 | + // useEffect(() => { | |
133 | + // console.log('sxPreViewList', sxPreViewList) | |
134 | + // if (sxPreViewList?.length) { | |
135 | + // const _name = sxPreViewList?.[0]?.name; | |
136 | + // console.log('_name', _name) | |
137 | + // navigate(`/production/detail?name=${_name}`); | |
138 | + // } | |
139 | + // }, [sxPreViewList]) | |
140 | + | |
95 | 141 | return ( |
96 | 142 | <div className={'sxjx-content-main sxjx-layout-main-unfoot'}> |
97 | 143 | <div className={`production-list ${showBatch ? 'production-list--batch' : ''}`}> |
... | ... | @@ -107,7 +153,7 @@ const ProductionList: React.FC = () => { |
107 | 153 | list?.map((item: ListType) => { |
108 | 154 | return <div |
109 | 155 | key={item?.id} |
110 | - className={`production-list_list-item ${item?.id}`} | |
156 | + className={`production-list_list-item`} | |
111 | 157 | style={{backgroundColor: '#fff'}} |
112 | 158 | onClick={() => { |
113 | 159 | if (!showBatch) { |
... | ... | @@ -156,13 +202,13 @@ const ProductionList: React.FC = () => { |
156 | 202 | >取消</div> |
157 | 203 | </div> |
158 | 204 | <div className={'production-list--batch_info-bottom'}> |
159 | - <div className={'production-list--batch_info-bottom_item'}> | |
205 | + <div className={'production-list--batch_info-bottom_item'} | |
206 | + onClick={() => { | |
207 | + toDetail(checkValues) | |
208 | + }} | |
209 | + > | |
160 | 210 | <img className={'production-list--batch_info-bottom_item-icon'} src={_preview} alt=""/> |
161 | - <span className={'production-list--batch_info-bottom_item-info'} | |
162 | - onClick={() => { | |
163 | - toDetail(checkValues) | |
164 | - }} | |
165 | - >预览</span> | |
211 | + <span className={'production-list--batch_info-bottom_item-info'}>预览</span> | |
166 | 212 | </div> |
167 | 213 | </div> |
168 | 214 | </div> : '' | ... | ... |
... | ... | @@ -114,8 +114,8 @@ |
114 | 114 | position: absolute; |
115 | 115 | top: 0; |
116 | 116 | left: 30px; |
117 | - width: 100%; | |
118 | - height: 100%; | |
117 | + width: calc(100% - 30px); | |
118 | + height: calc(100% - 30px); | |
119 | 119 | --icon-size: 30px; |
120 | 120 | display: flex; |
121 | 121 | align-items: start; |
... | ... | @@ -133,4 +133,10 @@ |
133 | 133 | padding: 0 10px; |
134 | 134 | } |
135 | 135 | } |
136 | +} | |
137 | + | |
138 | +.to-detail-mask { | |
139 | + .adm-auto-center-content { | |
140 | + font-size: 28px !important; | |
141 | + } | |
136 | 142 | } |
\ No newline at end of file | ... | ... |