diff --git a/config/proxy.ts b/config/proxy.ts index 59c4f65..bc9e1ad 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -15,7 +15,7 @@ export default { // localhost:8000/api/** -> https://preview.pro.ant.design/api/** '/api/': { // 要代理的地址 - target: 'http://39.98.44.136:6024/api/shihezi/', + target: 'http://39.98.44.136:6024/api/shihezi/', // target: 'http://localhost:9091', // 本地代理 // target: 'http://wykj.cdwsx.com/api',// 后端 // target: 'http://ks.zhaopinzao8dian.com/api/ks', diff --git a/config/routes.ts b/config/routes.ts index 04a4fa3..84f6daf 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -76,6 +76,10 @@ export default [ path: '/job-portal/personal-center/footprints',// 访问足迹岗位列表 component: './JobPortal/PersonalCenter/Footprints', }, + { + path: '/job-portal/personal-center/showcase',// 我的风采 + component: './JobPortal/PersonalCenter/Showcase', + }, { path: '/job-portal/message',// 消息通知页面 component: './JobPortal/Message', diff --git a/src/pages/JobPortal/PersonalCenter/Showcase/index.less b/src/pages/JobPortal/PersonalCenter/Showcase/index.less new file mode 100644 index 0000000..cb4f35f --- /dev/null +++ b/src/pages/JobPortal/PersonalCenter/Showcase/index.less @@ -0,0 +1,148 @@ +@import '../../theme.less'; + +.showcase-page { + min-height: 100vh; + background: @jp-bg-page; + padding-bottom: 40px; + + .page-content { + .jp-page-container(); + margin-top: 20px; + padding: 0 20px; + box-sizing: border-box; + } + + .back-button { + margin-bottom: 16px; + } + + .page-header { + margin-bottom: 24px; + + h2 { + margin-bottom: 8px; + } + } + + .upload-card { + .jp-card-base(); + margin-bottom: 24px; + } + + .upload-section { + .upload-area { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px 20px; + border: 2px dashed @jp-border; + border-radius: @jp-radius-lg; + background: @jp-bg-page; + cursor: pointer; + transition: all 0.3s; + + &:hover { + border-color: @jp-primary; + background: @jp-primary-light; + } + + .upload-icons { + display: flex; + gap: 24px; + margin-bottom: 16px; + + .upload-icon { + font-size: 40px; + color: @jp-primary; + } + } + + .upload-text { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + margin-bottom: 20px; + } + } + } + + .showcase-gallery { + .gallery-header { + display: flex; + align-items: baseline; + gap: 12px; + margin-bottom: 16px; + + h4 { + margin-bottom: 0; + } + } + + .gallery-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 16px; + } + + .gallery-card { + .jp-card-base(); + overflow: hidden; + + .image-cover { + height: 200px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + background: #f5f5f5; + + .gallery-image { + width: 100%; + height: 100%; + object-fit: cover; + } + } + + .video-cover { + height: 200px; + overflow: hidden; + background: #000; + + .gallery-video { + width: 100%; + height: 100%; + object-fit: contain; + } + } + + .ant-card-actions { + border-radius: 0 0 @jp-radius @jp-radius; + + > li > span { + cursor: pointer; + + &:hover { + .anticon { + color: #ff7875; + } + } + } + } + } + } +} + +@media (max-width: 768px) { + .showcase-page { + .page-content { + padding: 0 12px; + margin-top: 12px; + } + + .gallery-grid { + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)) !important; + } + } +} diff --git a/src/pages/JobPortal/PersonalCenter/Showcase/index.tsx b/src/pages/JobPortal/PersonalCenter/Showcase/index.tsx new file mode 100644 index 0000000..11f4923 --- /dev/null +++ b/src/pages/JobPortal/PersonalCenter/Showcase/index.tsx @@ -0,0 +1,277 @@ +import React, { useState } from 'react'; +import { + Upload, + Button, + Card, + Image, + Spin, + Empty, + message, + Modal, + Typography +} from 'antd'; +import { + UploadOutlined, + ArrowLeftOutlined, + CameraOutlined, + VideoCameraOutlined, + DeleteOutlined +} from '@ant-design/icons'; +import type { UploadFile } from 'antd/es/upload/interface'; +import { history } from '@umijs/max'; +import JobPortalHeader from '@/components/JobPortalHeader'; +import './index.less'; + +const { Title, Text } = Typography; + +/** 从缓存获取用户ID */ +const getUserIdFromCache = (): number | null => { + try { + const cached = localStorage.getItem('userInfo'); + if (cached) { + const userInfo = JSON.parse(cached); + return userInfo?.userId || null; + } + } catch (error) { + console.error('读取缓存用户信息失败:', error); + } + return null; +}; + +const ShowcasePage: React.FC = () => { + const [uploadedFiles, setUploadedFiles] = useState([]); + const [uploading, setUploading] = useState(false); + const [previewVisible, setPreviewVisible] = useState(false); + const [previewFile, setPreviewFile] = useState(null); + + const userId = getUserIdFromCache(); + + const isVideo = (file: UploadFile): boolean => { + const type = file.type || ''; + return type.startsWith('video/'); + }; + + const isImage = (file: UploadFile): boolean => { + const type = file.type || ''; + return type.startsWith('image/'); + }; + + /** 自定义上传逻辑 */ + const customUpload = async (options: any) => { + const { file, onSuccess, onError, onProgress } = options; + + if (!userId) { + message.error('请先登录'); + onError(new Error('未登录')); + return; + } + + const formData = new FormData(); + formData.append('file', file as File); + + try { + setUploading(true); + const response = await fetch( + `/app/file/upload?bussinessid=${userId}`, + { + method: 'POST', + body: formData, + } + ); + + if (response.ok) { + const result = await response.json(); + if (result.code === 200 || result.code === 0) { + message.success(`${file.name} 上传成功`); + onSuccess(result, file); + // 将上传成功的文件添加到展示列表 + setUploadedFiles((prev) => [...prev, file]); + } else { + message.error(result.msg || `${file.name} 上传失败`); + onError(new Error(result.msg || '上传失败')); + } + } else { + message.error(`${file.name} 上传失败`); + onError(new Error('上传失败')); + } + } catch (error) { + console.error('上传失败:', error); + message.error(`${file.name} 上传失败`); + onError(error as Error); + } finally { + setUploading(false); + } + }; + + /** 上传前校验:仅允许图片和视频 */ + const beforeUpload = (file: File): boolean => { + const isImageOrVideo = file.type.startsWith('image/') || file.type.startsWith('video/'); + if (!isImageOrVideo) { + message.error('只支持上传图片和视频文件!'); + return false; + } + return true; + }; + + /** 移除已上传文件 */ + const handleRemove = (file: UploadFile) => { + setUploadedFiles((prev) => prev.filter((f) => f.uid !== file.uid)); + message.info('已移除'); + }; + + /** 预览文件 */ + const handlePreview = (file: UploadFile) => { + setPreviewFile(file); + setPreviewVisible(true); + }; + + const handleBack = () => { + history.push('/job-portal/personal-center'); + }; + + return ( +
+ + + +
+ {/* 返回按钮 */} +
+ +
+ + {/* 页面标题 */} +
+ 我的风采 + 上传你的个人照片和视频,展示你的风采 +
+ + {/* 上传区域 */} + +
+ +
+
+ + +
+
+ 点击或拖拽上传 + 支持图片和视频文件,大小不限 +
+ +
+
+
+
+ + {/* 已上传内容展示 */} + {uploadedFiles.length === 0 ? ( + + ) : ( +
+
+ 已上传内容 + 共 {uploadedFiles.length} 个文件 +
+
+ {uploadedFiles.map((file, index) => ( + +
+ ) : ( +
+ {file.name}, + }} + /> +
+ ) + } + actions={[ + handleRemove(file)} + style={{ color: '#ff4d4f' }} + />, + ]} + > + + {file.size + ? file.size > 1024 * 1024 + ? `${(file.size / 1024 / 1024).toFixed(2)} MB` + : `${(file.size / 1024).toFixed(2)} KB` + : ''} + + } + /> + + ))} +
+
+ )} + + {/* 视频预览弹窗 */} + setPreviewVisible(false)} + width={800} + title={previewFile?.name || '预览'} + > + {previewFile && isVideo(previewFile) && ( + +
+ + + ); +}; + +export default ShowcasePage; diff --git a/src/pages/JobPortal/PersonalCenter/index.tsx b/src/pages/JobPortal/PersonalCenter/index.tsx index 5d50443..d97daf8 100644 --- a/src/pages/JobPortal/PersonalCenter/index.tsx +++ b/src/pages/JobPortal/PersonalCenter/index.tsx @@ -16,7 +16,8 @@ import { RightOutlined, SafetyCertificateOutlined, BellOutlined, - FileTextOutlined + FileTextOutlined, + CameraOutlined } from '@ant-design/icons'; import { history } from '@umijs/max'; import JobPortalHeader from '@/components/JobPortalHeader'; @@ -169,6 +170,15 @@ const PersonalCenter: React.FC = () => { hasArrow: !isRealNameVerified, color: isRealNameVerified ? '#52c41a' : '#8c8c8c' }, + { + id: 5, + title: '我的风采', + icon: , + status: '', + hasArrow: true, + color: '#722ed1', + link: '/job-portal/personal-center/showcase' + }, { id: 2, title: '在线面试', @@ -190,7 +200,11 @@ const PersonalCenter: React.FC = () => { const handleServiceClick = (service: any) => { if (service.link) { - window.open(service.link, '_blank'); + if (service.link.startsWith('http')) { + window.open(service.link, '_blank'); + } else { + history.push(service.link); + } } };