|
|
@@ -0,0 +1,444 @@
|
|
|
+import React, { useState, useEffect } from 'react';
|
|
|
+import { useParams, useNavigate } from 'react-router-dom';
|
|
|
+import {
|
|
|
+ ArrowLeftIcon,
|
|
|
+ HeartIcon,
|
|
|
+ ChatBubbleLeftIcon,
|
|
|
+ ShareIcon,
|
|
|
+ MapPinIcon,
|
|
|
+ ClockIcon,
|
|
|
+ AcademicCapIcon,
|
|
|
+ BriefcaseIcon
|
|
|
+} from '@heroicons/react/24/outline';
|
|
|
+import { HeartIcon as HeartSolidIcon } from '@heroicons/react/24/solid';
|
|
|
+import { INK_COLORS } from '@/client/mobile/styles/colors';
|
|
|
+import { JobItem } from '@/client/mobile/hooks/useJobs';
|
|
|
+
|
|
|
+// 扩展JobItem类型以包含更多详情字段
|
|
|
+interface JobDetailItem extends JobItem {
|
|
|
+ workLocation?: string;
|
|
|
+ workTime?: string;
|
|
|
+ educationRequired?: string;
|
|
|
+ experienceRequired?: string;
|
|
|
+ details?: string;
|
|
|
+ company?: {
|
|
|
+ industry: string;
|
|
|
+ size: string;
|
|
|
+ tags: string[];
|
|
|
+ };
|
|
|
+ favoriteCount?: number;
|
|
|
+}
|
|
|
+
|
|
|
+const JobDetailPage: React.FC = () => {
|
|
|
+ const { id } = useParams<{ id: string }>();
|
|
|
+ const navigate = useNavigate();
|
|
|
+ const [job, setJob] = useState<JobDetailItem | null>(null);
|
|
|
+ const [loading, setLoading] = useState(true);
|
|
|
+ const [isFavorited, setIsFavorited] = useState(false);
|
|
|
+ const [showShareModal, setShowShareModal] = useState(false);
|
|
|
+
|
|
|
+ // 模拟获取岗位详情数据
|
|
|
+ useEffect(() => {
|
|
|
+ const fetchJobDetail = async () => {
|
|
|
+ setLoading(true);
|
|
|
+ // 这里应该从API获取真实数据
|
|
|
+ setTimeout(() => {
|
|
|
+ const mockJob: JobDetailItem = {
|
|
|
+ id: parseInt(id || '1'),
|
|
|
+ title: '银龄社区服务专员',
|
|
|
+ companyName: '智慧养老服务中心',
|
|
|
+ companyLogo: '/images/company-logo1.jpg',
|
|
|
+ location: '北京市朝阳区',
|
|
|
+ salaryMin: 4000,
|
|
|
+ salaryMax: 6000,
|
|
|
+ jobType: '社区服务',
|
|
|
+ description: '为社区老年人提供日常陪伴、生活协助和情感关怀服务',
|
|
|
+ requirements: '有责任心,热爱老年事业',
|
|
|
+ benefits: ['五险一金', '带薪年假', '节日福利', '培训机会', '弹性工作'],
|
|
|
+ views: 256,
|
|
|
+ applications: 18,
|
|
|
+ publishDate: '2024-07-20',
|
|
|
+ workLocation: '北京市朝阳区各社区服务中心',
|
|
|
+ workTime: '朝九晚五,周末双休',
|
|
|
+ educationRequired: '高中及以上',
|
|
|
+ experienceRequired: '1-3年',
|
|
|
+ details: '负责为社区老年人提供日常陪伴、生活协助和情感关怀服务,需要具备良好的沟通能力和耐心,能够理解和满足老年人的需求。工作地点分布在朝阳区各社区服务中心,工作时间灵活,可根据个人情况调整。',
|
|
|
+ company: {
|
|
|
+ industry: '养老服务',
|
|
|
+ size: '50-100人',
|
|
|
+ tags: ['社区服务', '养老行业', '政府合作']
|
|
|
+ },
|
|
|
+ favoriteCount: 12
|
|
|
+ };
|
|
|
+ setJob(mockJob);
|
|
|
+ setLoading(false);
|
|
|
+ }, 1000);
|
|
|
+ };
|
|
|
+
|
|
|
+ if (id) {
|
|
|
+ fetchJobDetail();
|
|
|
+ }
|
|
|
+ }, [id]);
|
|
|
+
|
|
|
+ const handleFavorite = () => {
|
|
|
+ setIsFavorited(!isFavorited);
|
|
|
+ // 这里应该调用收藏API
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleApply = () => {
|
|
|
+ // 这里应该调用投递简历API
|
|
|
+ alert('简历投递成功!');
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleShare = () => {
|
|
|
+ setShowShareModal(true);
|
|
|
+ };
|
|
|
+
|
|
|
+ const formatSalary = (min: number, max: number) => {
|
|
|
+ return `${min.toLocaleString()}-${max.toLocaleString()}元/月`;
|
|
|
+ };
|
|
|
+
|
|
|
+ const formatPublishDate = (date: string) => {
|
|
|
+ const publishDate = new Date(date);
|
|
|
+ const now = new Date();
|
|
|
+ const diffTime = Math.abs(now.getTime() - publishDate.getTime());
|
|
|
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
|
+
|
|
|
+ if (diffDays === 1) return '今天';
|
|
|
+ if (diffDays < 7) return `${diffDays}天前`;
|
|
|
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)}周前`;
|
|
|
+ return `${Math.floor(diffDays / 30)}月前`;
|
|
|
+ };
|
|
|
+
|
|
|
+ if (loading) {
|
|
|
+ return (
|
|
|
+ <div className="min-h-screen" style={{ backgroundColor: INK_COLORS.ink.light }}>
|
|
|
+ {/* 骨架屏 */}
|
|
|
+ <div className="animate-pulse">
|
|
|
+ <div className="h-16 bg-gray-200 mb-4"></div>
|
|
|
+ <div className="mx-4 mb-4">
|
|
|
+ <div className="h-20 bg-gray-200 rounded-xl mb-4"></div>
|
|
|
+ <div className="h-32 bg-gray-200 rounded-xl mb-4"></div>
|
|
|
+ <div className="h-40 bg-gray-200 rounded-xl mb-4"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!job) {
|
|
|
+ return (
|
|
|
+ <div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: INK_COLORS.ink.light }}>
|
|
|
+ <div className="text-center">
|
|
|
+ <div className="text-6xl mb-4">🔍</div>
|
|
|
+ <p className="text-lg mb-4" style={{ color: INK_COLORS.text.secondary }}>岗位信息不存在</p>
|
|
|
+ <button
|
|
|
+ onClick={() => navigate(-1)}
|
|
|
+ className="px-6 py-2 rounded-full text-sm"
|
|
|
+ style={{ backgroundColor: INK_COLORS.accent.blue, color: 'white' }}
|
|
|
+ >
|
|
|
+ 返回
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="min-h-screen pb-20" style={{ backgroundColor: INK_COLORS.ink.light }}>
|
|
|
+ {/* 顶部导航栏 */}
|
|
|
+ <header
|
|
|
+ className="sticky top-0 z-20 shadow-sm px-4 py-3 flex items-center justify-between"
|
|
|
+ style={{ backgroundColor: INK_COLORS.ink.light, borderColor: INK_COLORS.ink.medium }}
|
|
|
+ >
|
|
|
+ <button
|
|
|
+ onClick={() => navigate(-1)}
|
|
|
+ className="p-2 rounded-full hover:bg-gray-100 transition-colors"
|
|
|
+ >
|
|
|
+ <ArrowLeftIcon className="w-5 h-5" style={{ color: INK_COLORS.ink.deep }} />
|
|
|
+ </button>
|
|
|
+ <h1 className="font-serif text-lg font-medium" style={{ color: INK_COLORS.text.primary }}>
|
|
|
+ 岗位详情
|
|
|
+ </h1>
|
|
|
+ <button
|
|
|
+ onClick={handleShare}
|
|
|
+ className="p-2 rounded-full hover:bg-gray-100 transition-colors"
|
|
|
+ >
|
|
|
+ <ShareIcon className="w-5 h-5" style={{ color: INK_COLORS.ink.deep }} />
|
|
|
+ </button>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ {/* 企业信息卡片 */}
|
|
|
+ <div className="mx-4 mt-4 p-4 rounded-xl shadow-sm backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${INK_COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <div className="flex items-center mb-3">
|
|
|
+ <div className="w-12 h-12 rounded-full mr-3 flex items-center justify-center text-lg font-bold border-2"
|
|
|
+ style={{ borderColor: INK_COLORS.ink.medium, backgroundColor: INK_COLORS.ink.light }}
|
|
|
+ >
|
|
|
+ {job.companyLogo ? (
|
|
|
+ <img
|
|
|
+ src={job.companyLogo}
|
|
|
+ alt={job.companyName}
|
|
|
+ className="w-full h-full rounded-full object-cover"
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <span style={{ color: INK_COLORS.ink.dark }}>
|
|
|
+ {job.companyName.slice(0, 2)}
|
|
|
+ </span>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <h3 className="font-serif text-lg font-medium" style={{ color: INK_COLORS.text.primary }}>
|
|
|
+ {job.companyName}
|
|
|
+ </h3>
|
|
|
+ {job.company && (
|
|
|
+ <p className="text-sm" style={{ color: INK_COLORS.text.secondary }}>
|
|
|
+ {job.company.industry} · {job.company.size}
|
|
|
+ </p>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {job.company?.tags && (
|
|
|
+ <div className="flex flex-wrap gap-1">
|
|
|
+ {job.company.tags.map((tag, index) => (
|
|
|
+ <span
|
|
|
+ key={index}
|
|
|
+ className="px-2 py-1 text-xs rounded-full"
|
|
|
+ style={{
|
|
|
+ backgroundColor: `${INK_COLORS.accent.blue}20`,
|
|
|
+ color: INK_COLORS.accent.blue,
|
|
|
+ border: `1px solid ${INK_COLORS.accent.blue}30`
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {tag}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 岗位基本信息 */}
|
|
|
+ <div className="mx-4 mt-4 p-4 rounded-xl shadow-sm backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${INK_COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h2 className="font-serif text-xl font-bold mb-2" style={{ color: INK_COLORS.text.primary }}>
|
|
|
+ {job.title}
|
|
|
+ </h2>
|
|
|
+
|
|
|
+ <div className="text-lg font-bold mb-3" style={{ color: INK_COLORS.accent.red }}>
|
|
|
+ 💰 {formatSalary(job.salaryMin, job.salaryMax)}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="space-y-2">
|
|
|
+ {job.workLocation && (
|
|
|
+ <div className="flex items-center">
|
|
|
+ <MapPinIcon className="w-4 h-4 mr-2" style={{ color: INK_COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: INK_COLORS.text.secondary }}>{job.workLocation}</span>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ {job.workTime && (
|
|
|
+ <div className="flex items-center">
|
|
|
+ <ClockIcon className="w-4 h-4 mr-2" style={{ color: INK_COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: INK_COLORS.text.secondary }}>{job.workTime}</span>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ {job.educationRequired && (
|
|
|
+ <div className="flex items-center">
|
|
|
+ <AcademicCapIcon className="w-4 h-4 mr-2" style={{ color: INK_COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: INK_COLORS.text.secondary }}>{job.educationRequired}</span>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ {job.experienceRequired && (
|
|
|
+ <div className="flex items-center">
|
|
|
+ <BriefcaseIcon className="w-4 h-4 mr-2" style={{ color: INK_COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: INK_COLORS.text.secondary }}>{job.experienceRequired}</span>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 岗位介绍 */}
|
|
|
+ <div className="mx-4 mt-4 p-4 rounded-xl shadow-sm backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${INK_COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-2" style={{ color: INK_COLORS.text.primary }}>
|
|
|
+ 岗位介绍
|
|
|
+ </h3>
|
|
|
+ <p className="text-sm leading-relaxed" style={{ color: INK_COLORS.text.secondary }}>
|
|
|
+ {job.description}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 详细职责 */}
|
|
|
+ <div className="mx-4 mt-4 p-4 rounded-xl shadow-sm backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${INK_COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-2" style={{ color: INK_COLORS.text.primary }}>
|
|
|
+ 详细职责
|
|
|
+ </h3>
|
|
|
+ <p className="text-sm leading-relaxed" style={{ color: INK_COLORS.text.secondary }}>
|
|
|
+ {job.details || job.requirements || '负责为社区老年人提供日常陪伴、生活协助和情感关怀服务,需要具备良好的沟通能力和耐心,能够理解和满足老年人的需求。'}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 福利待遇 */}
|
|
|
+ <div className="mx-4 mt-4 p-4 rounded-xl shadow-sm backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${INK_COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-2" style={{ color: INK_COLORS.text.primary }}>
|
|
|
+ 福利待遇
|
|
|
+ </h3>
|
|
|
+ <div className="flex flex-wrap gap-2">
|
|
|
+ {job.benefits.map((benefit, index) => (
|
|
|
+ <span
|
|
|
+ key={index}
|
|
|
+ className="px-3 py-1 text-sm rounded-full"
|
|
|
+ style={{
|
|
|
+ backgroundColor: `${INK_COLORS.accent.green}20`,
|
|
|
+ color: INK_COLORS.accent.green,
|
|
|
+ border: `1px solid ${INK_COLORS.accent.green}30`
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {benefit}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 岗位热度 */}
|
|
|
+ <div className="mx-4 mt-4 p-4 rounded-xl shadow-sm backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${INK_COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-2" style={{ color: INK_COLORS.text.primary }}>
|
|
|
+ 岗位热度
|
|
|
+ </h3>
|
|
|
+ <div className="grid grid-cols-3 gap-2 text-center">
|
|
|
+ <div>
|
|
|
+ <div className="text-lg font-bold" style={{ color: INK_COLORS.accent.blue }}>
|
|
|
+ {job.views}
|
|
|
+ </div>
|
|
|
+ <div className="text-xs" style={{ color: INK_COLORS.text.light }}>浏览</div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div className="text-lg font-bold" style={{ color: INK_COLORS.accent.green }}>
|
|
|
+ {job.favoriteCount || 0}
|
|
|
+ </div>
|
|
|
+ <div className="text-xs" style={{ color: INK_COLORS.text.light }}>收藏</div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div className="text-lg font-bold" style={{ color: INK_COLORS.accent.red }}>
|
|
|
+ {job.applications}
|
|
|
+ </div>
|
|
|
+ <div className="text-xs" style={{ color: INK_COLORS.text.light }}>投递</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 发布信息 */}
|
|
|
+ <div className="mx-4 mt-4 mb-4 p-4 rounded-xl shadow-sm backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${INK_COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
+ <span className="text-sm" style={{ color: INK_COLORS.text.light }}>
|
|
|
+ 发布时间: {formatPublishDate(job.publishDate)}
|
|
|
+ </span>
|
|
|
+ <span className="text-sm" style={{ color: INK_COLORS.text.light }}>
|
|
|
+ 岗位编号: #{job.id.toString().padStart(6, '0')}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 底部操作栏 */}
|
|
|
+ <div className="fixed bottom-0 left-0 right-0 p-4 shadow-lg border-t"
|
|
|
+ style={{ backgroundColor: INK_COLORS.ink.light, borderColor: INK_COLORS.ink.medium }}
|
|
|
+ >
|
|
|
+ <div className="flex space-x-3">
|
|
|
+ <button
|
|
|
+ onClick={handleApply}
|
|
|
+ className="flex-1 px-4 py-3 rounded-full text-sm font-medium transition-all duration-300 hover:scale-105 active:scale-95"
|
|
|
+ style={{
|
|
|
+ backgroundColor: INK_COLORS.accent.blue,
|
|
|
+ color: 'white',
|
|
|
+ border: `1px solid ${INK_COLORS.accent.blue}`
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 立即投递
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ onClick={handleFavorite}
|
|
|
+ className={`p-3 rounded-full transition-all duration-300 hover:scale-105 active:scale-95 ${
|
|
|
+ isFavorited ? 'bg-red-500 text-white' : 'border-2'
|
|
|
+ }`}
|
|
|
+ style={!isFavorited ? {
|
|
|
+ backgroundColor: 'transparent',
|
|
|
+ color: INK_COLORS.accent.red,
|
|
|
+ border: `2px solid ${INK_COLORS.accent.red}`
|
|
|
+ } : {}}
|
|
|
+ >
|
|
|
+ {isFavorited ? (
|
|
|
+ <HeartSolidIcon className="w-5 h-5" />
|
|
|
+ ) : (
|
|
|
+ <HeartIcon className="w-5 h-5" />
|
|
|
+ )}
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ className="p-3 rounded-full transition-all duration-300 hover:scale-105 active:scale-95 border-2"
|
|
|
+ style={{
|
|
|
+ backgroundColor: 'transparent',
|
|
|
+ color: INK_COLORS.accent.green,
|
|
|
+ border: `2px solid ${INK_COLORS.accent.green}`
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <ChatBubbleLeftIcon className="w-5 h-5" />
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 分享模态框 */}
|
|
|
+ {showShareModal && (
|
|
|
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-30">
|
|
|
+ <div className="bg-white rounded-xl p-4 m-4 max-w-sm">
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-4 text-center">分享给朋友</h3>
|
|
|
+ <div className="grid grid-cols-4 gap-4">
|
|
|
+ <button className="flex flex-col items-center">
|
|
|
+ <div className="w-12 h-12 rounded-full bg-green-500 flex items-center justify-center mb-2">
|
|
|
+ <span className="text-white text-xl">📱</span>
|
|
|
+ </div>
|
|
|
+ <span className="text-xs">微信</span>
|
|
|
+ </button>
|
|
|
+ <button className="flex flex-col items-center">
|
|
|
+ <div className="w-12 h-12 rounded-full bg-blue-500 flex items-center justify-center mb-2">
|
|
|
+ <span className="text-white text-xl">📧</span>
|
|
|
+ </div>
|
|
|
+ <span className="text-xs">邮件</span>
|
|
|
+ </button>
|
|
|
+ <button className="flex flex-col items-center">
|
|
|
+ <div className="w-12 h-12 rounded-full bg-red-500 flex items-center justify-center mb-2">
|
|
|
+ <span className="text-white text-xl">🔗</span>
|
|
|
+ </div>
|
|
|
+ <span className="text-xs">复制链接</span>
|
|
|
+ </button>
|
|
|
+ <button className="flex flex-col items-center">
|
|
|
+ <div className="w-12 h-12 rounded-full bg-purple-500 flex items-center justify-center mb-2">
|
|
|
+ <span className="text-white text-xl">📋</span>
|
|
|
+ </div>
|
|
|
+ <span className="text-xs">更多</span>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <button
|
|
|
+ onClick={() => setShowShareModal(false)}
|
|
|
+ className="w-full mt-4 py-2 rounded-full text-sm"
|
|
|
+ style={{ backgroundColor: INK_COLORS.ink.medium, color: INK_COLORS.text.primary }}
|
|
|
+ >
|
|
|
+ 取消
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default JobDetailPage;
|