|
|
@@ -1,143 +1,145 @@
|
|
|
-import React, { useState, useEffect } from 'react';
|
|
|
+import React, { useState } from 'react';
|
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
|
import {
|
|
|
ArrowLeftIcon,
|
|
|
HeartIcon,
|
|
|
- ChatBubbleLeftIcon,
|
|
|
ShareIcon,
|
|
|
MapPinIcon,
|
|
|
ClockIcon,
|
|
|
- AcademicCapIcon,
|
|
|
- BriefcaseIcon
|
|
|
+ CurrencyYenIcon,
|
|
|
+ PhoneIcon,
|
|
|
+ EnvelopeIcon,
|
|
|
+ CheckCircleIcon,
|
|
|
+ BuildingOfficeIcon,
|
|
|
+ UsersIcon
|
|
|
} 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';
|
|
|
+import { useQuery } from '@tanstack/react-query';
|
|
|
+import { silverJobClient } from '@/client/api';
|
|
|
+import type { InferResponseType } from 'hono/client';
|
|
|
|
|
|
-// 扩展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 COLORS = {
|
|
|
+ ink: {
|
|
|
+ light: '#f5f3f0',
|
|
|
+ medium: '#d4c4a8',
|
|
|
+ dark: '#8b7355',
|
|
|
+ deep: '#3a2f26',
|
|
|
+ },
|
|
|
+ accent: {
|
|
|
+ red: '#a85c5c',
|
|
|
+ blue: '#4a6b7c',
|
|
|
+ green: '#5c7c5c',
|
|
|
+ },
|
|
|
+ text: {
|
|
|
+ primary: '#2f1f0f',
|
|
|
+ secondary: '#5d4e3b',
|
|
|
+ light: '#8b7355',
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 类型定义
|
|
|
+type JobDetailResponse = InferResponseType<typeof silverJobClient[':id']['$get'], 200>;
|
|
|
+type Job = JobDetailResponse['data'];
|
|
|
|
|
|
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);
|
|
|
- };
|
|
|
+ const { data, isLoading, error } = useQuery({
|
|
|
+ queryKey: ['job-detail', id],
|
|
|
+ queryFn: async () => {
|
|
|
+ const response = await silverJobClient[':id'].$get({
|
|
|
+ param: { id: Number(id) }
|
|
|
+ });
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error('获取岗位信息失败');
|
|
|
+ }
|
|
|
+ return response.json();
|
|
|
+ },
|
|
|
+ retry: 1,
|
|
|
+ enabled: !!id && !isNaN(Number(id)),
|
|
|
+ });
|
|
|
|
|
|
- if (id) {
|
|
|
- fetchJobDetail();
|
|
|
- }
|
|
|
- }, [id]);
|
|
|
+ const job = data?.data;
|
|
|
+
|
|
|
+ const formatJobStatus = (status: number) => {
|
|
|
+ const statusMap: Record<number, { text: string; color: string }> = {
|
|
|
+ 0: { text: '草稿', color: COLORS.text.light },
|
|
|
+ 1: { text: '招聘中', color: COLORS.accent.green },
|
|
|
+ 2: { text: '已关闭', color: COLORS.text.light },
|
|
|
+ 3: { text: '已招满', color: COLORS.accent.red }
|
|
|
+ };
|
|
|
+ return statusMap[status] || { text: '未知', color: COLORS.text.light };
|
|
|
+ };
|
|
|
|
|
|
const handleFavorite = () => {
|
|
|
setIsFavorited(!isFavorited);
|
|
|
- // 这里应该调用收藏API
|
|
|
+ // TODO: 调用收藏API
|
|
|
};
|
|
|
|
|
|
const handleApply = () => {
|
|
|
- // 这里应该调用投递简历API
|
|
|
- alert('简历投递成功!');
|
|
|
- };
|
|
|
-
|
|
|
- const handleShare = () => {
|
|
|
- setShowShareModal(true);
|
|
|
+ // TODO: 跳转到申请页面或调用申请API
|
|
|
+ console.log('申请岗位:', job?.id);
|
|
|
};
|
|
|
|
|
|
- const formatSalary = (min: number, max: number) => {
|
|
|
- return `${min.toLocaleString()}-${max.toLocaleString()}元/月`;
|
|
|
+ const handlePhoneCall = () => {
|
|
|
+ if (job?.contactPhone) {
|
|
|
+ window.open(`tel:${job.contactPhone}`);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- 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)}月前`;
|
|
|
+ const formatDate = (dateString?: string) => {
|
|
|
+ if (!dateString) return '长期有效';
|
|
|
+ return new Date(dateString).toLocaleDateString('zh-CN');
|
|
|
};
|
|
|
|
|
|
- if (loading) {
|
|
|
+ if (isLoading) {
|
|
|
return (
|
|
|
- <div className="min-h-screen" style={{ backgroundColor: INK_COLORS.ink.light }}>
|
|
|
+ <div className="min-h-screen" style={{ backgroundColor: 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>
|
|
|
+ <header className="sticky top-0 z-10 shadow-sm px-4 py-3 flex items-center justify-between"
|
|
|
+ style={{ backgroundColor: COLORS.ink.light, borderColor: COLORS.ink.medium }}>
|
|
|
+ <div className="h-6 bg-gray-200 rounded w-20 animate-pulse"></div>
|
|
|
+ <div className="h-6 bg-gray-200 rounded w-20 animate-pulse"></div>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ <div className="p-4 space-y-4">
|
|
|
+ {[1, 2, 3, 4].map(i => (
|
|
|
+ <div key={i} className="rounded-xl p-4 shadow-sm animate-pulse"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${COLORS.ink.medium}` }}>
|
|
|
+ <div className="h-6 bg-gray-200 rounded w-1/3 mb-3"></div>
|
|
|
+ <div className="space-y-2">
|
|
|
+ <div className="h-4 bg-gray-200 rounded w-full"></div>
|
|
|
+ <div className="h-4 bg-gray-200 rounded w-3/4"></div>
|
|
|
+ <div className="h-4 bg-gray-200 rounded w-1/2"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- if (!job) {
|
|
|
+ if (error || !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
|
|
|
+ <div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: COLORS.ink.light }}>
|
|
|
+ <div className="text-center p-8">
|
|
|
+ <div className="text-6xl mb-4">😔</div>
|
|
|
+ <h3 className="text-xl font-serif mb-2" style={{ color: COLORS.text.primary }}>
|
|
|
+ 岗位信息获取失败
|
|
|
+ </h3>
|
|
|
+ <p className="text-sm mb-4" style={{ color: COLORS.text.secondary }}>
|
|
|
+ {error?.message || '请检查网络连接后重试'}
|
|
|
+ </p>
|
|
|
+ <button
|
|
|
onClick={() => navigate(-1)}
|
|
|
- className="px-6 py-2 rounded-full text-sm"
|
|
|
- style={{ backgroundColor: INK_COLORS.accent.blue, color: 'white' }}
|
|
|
+ className="px-6 py-2 rounded-full text-sm transition-all duration-300 hover:shadow-md"
|
|
|
+ style={{ backgroundColor: COLORS.ink.dark, color: 'white' }}
|
|
|
>
|
|
|
- 返回
|
|
|
+ 返回上一页
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -145,292 +147,343 @@ const JobDetailPage: React.FC = () => {
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
- <div className="min-h-screen pb-20" style={{ backgroundColor: INK_COLORS.ink.light }}>
|
|
|
+ <div className="min-h-screen pb-24" style={{ backgroundColor: 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}` }}
|
|
|
+ className="sticky top-0 z-20 shadow-sm px-4 py-3 flex items-center justify-between border-b"
|
|
|
+ style={{ backgroundColor: COLORS.ink.light, borderColor: `${COLORS.ink.medium}33` }}
|
|
|
>
|
|
|
- <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 }}
|
|
|
+ <div className="flex items-center">
|
|
|
+ <button
|
|
|
+ onClick={() => navigate(-1)}
|
|
|
+ className="p-2 rounded-full hover:bg-gray-100 transition-colors"
|
|
|
>
|
|
|
- {job.companyLogo ? (
|
|
|
- <img
|
|
|
- src={job.companyLogo}
|
|
|
- alt={job.companyName}
|
|
|
- className="w-full h-full rounded-full object-cover"
|
|
|
- />
|
|
|
+ <ArrowLeftIcon className="w-5 h-5" style={{ color: COLORS.ink.deep }} />
|
|
|
+ </button>
|
|
|
+ <h1 className="ml-2 font-serif text-lg font-medium" style={{ color: COLORS.text.primary }}>
|
|
|
+ 岗位详情
|
|
|
+ </h1>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="flex items-center space-x-2">
|
|
|
+ <button
|
|
|
+ onClick={handleFavorite}
|
|
|
+ className="p-2 rounded-full hover:bg-gray-100 transition-colors"
|
|
|
+ >
|
|
|
+ {isFavorited ? (
|
|
|
+ <HeartSolidIcon className="w-5 h-5" style={{ color: COLORS.accent.red }} />
|
|
|
) : (
|
|
|
- <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>
|
|
|
+ <HeartIcon className="w-5 h-5" style={{ color: COLORS.ink.deep }} />
|
|
|
)}
|
|
|
- </div>
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ onClick={() => setShowShareModal(true)}
|
|
|
+ className="p-2 rounded-full hover:bg-gray-100 transition-colors"
|
|
|
+ >
|
|
|
+ <ShareIcon className="w-5 h-5" style={{ color: COLORS.ink.deep }} />
|
|
|
+ </button>
|
|
|
</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>
|
|
|
+ </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}` }}
|
|
|
- >
|
|
|
- <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 className="p-4 space-y-4">
|
|
|
+ {/* 岗位状态标签 */}
|
|
|
+ <div className="flex justify-between items-center">
|
|
|
+ <span
|
|
|
+ className="px-3 py-1 rounded-full text-xs font-medium"
|
|
|
+ style={{
|
|
|
+ backgroundColor: formatJobStatus(job.status).color + '20',
|
|
|
+ color: formatJobStatus(job.status).color
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {formatJobStatus(job.status).text}
|
|
|
+ </span>
|
|
|
+ <span className="text-xs" style={{ color: COLORS.text.light }}>
|
|
|
+ 浏览 {job.viewCount || 0} · 申请 {job.applicationCount || 0}
|
|
|
+ </span>
|
|
|
</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="rounded-xl p-4 shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h2 className="font-serif text-xl font-bold mb-2" style={{ color: COLORS.text.primary }}>
|
|
|
+ {job.title}
|
|
|
+ </h2>
|
|
|
+
|
|
|
+ <div className="text-lg font-bold mb-3" style={{ color: COLORS.accent.red }}>
|
|
|
+ 💰 {job.salaryRange || '面议'}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="space-y-2">
|
|
|
<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>
|
|
|
+ <MapPinIcon className="w-4 h-4 mr-2" style={{ color: COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: COLORS.text.secondary }}>{job.location}</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>
|
|
|
+ <ClockIcon className="w-4 h-4 mr-2" style={{ color: COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: COLORS.text.secondary }}>{job.workHours}</span>
|
|
|
</div>
|
|
|
- )}
|
|
|
+ {job.applicationDeadline && (
|
|
|
+ <div className="flex items-center">
|
|
|
+ <ClockIcon className="w-4 h-4 mr-2" style={{ color: COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: COLORS.text.secondary }}>
|
|
|
+ 截止日期: {formatDate(job.applicationDeadline)}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ {job.startDate && (
|
|
|
+ <div className="flex items-center">
|
|
|
+ <ClockIcon className="w-4 h-4 mr-2" style={{ color: COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: COLORS.text.secondary }}>
|
|
|
+ 开始日期: {formatDate(job.startDate)}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </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>
|
|
|
+ {/* 公司信息卡片 */}
|
|
|
+ {job.company && (
|
|
|
+ <div className="rounded-xl p-4 shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${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: COLORS.ink.medium, backgroundColor: COLORS.ink.light }}
|
|
|
+ >
|
|
|
+ {job.company.shortName?.slice(0, 2) || job.company.name?.slice(0, 2) || '公司'}
|
|
|
+ </div>
|
|
|
+ <div className="flex-1">
|
|
|
+ <h3 className="font-serif text-lg font-medium" style={{ color: COLORS.text.primary }}>
|
|
|
+ {job.company.name}
|
|
|
+ </h3>
|
|
|
+ <div className="flex items-center space-x-2">
|
|
|
+ <span className="text-sm" style={{ color: COLORS.text.secondary }}>
|
|
|
+ {job.company.industryCategory} · {job.company.employeeCount}
|
|
|
+ </span>
|
|
|
+ {job.company.isCertified && (
|
|
|
+ <CheckCircleIcon className="w-4 h-4" style={{ color: COLORS.accent.green }} />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="flex items-center space-x-4 text-sm">
|
|
|
+ <a
|
|
|
+ href={`tel:${job.contactPhone}`}
|
|
|
+ className="flex items-center space-x-1"
|
|
|
+ style={{ color: COLORS.accent.blue }}
|
|
|
+ onClick={(e) => e.stopPropagation()}
|
|
|
+ >
|
|
|
+ <PhoneIcon className="w-4 h-4" />
|
|
|
+ <span>电话</span>
|
|
|
+ </a>
|
|
|
+ {job.contactEmail && (
|
|
|
+ <a
|
|
|
+ href={`mailto:${job.contactEmail}`}
|
|
|
+ className="flex items-center space-x-1"
|
|
|
+ style={{ color: COLORS.accent.blue }}
|
|
|
+ onClick={(e) => e.stopPropagation()}
|
|
|
+ >
|
|
|
+ <EnvelopeIcon className="w-4 h-4" />
|
|
|
+ <span>邮箱</span>
|
|
|
+ </a>
|
|
|
+ )}
|
|
|
+ </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="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 className="rounded-xl p-4 shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-3" style={{ color: COLORS.text.primary }}>
|
|
|
+ 岗位描述
|
|
|
+ </h3>
|
|
|
+ <p className="text-sm leading-relaxed" style={{ color: COLORS.text.secondary }}>
|
|
|
+ {job.description}
|
|
|
+ </p>
|
|
|
</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>
|
|
|
+ {/* 岗位要求 */}
|
|
|
+ {job.requirements && (
|
|
|
+ <div className="rounded-xl p-4 shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-3" style={{ color: COLORS.text.primary }}>
|
|
|
+ 岗位要求
|
|
|
+ </h3>
|
|
|
+ <p className="text-sm leading-relaxed" style={{ color: COLORS.text.secondary }}>
|
|
|
+ {job.requirements}
|
|
|
+ </p>
|
|
|
</div>
|
|
|
- <div>
|
|
|
- <div className="text-lg font-bold" style={{ color: INK_COLORS.accent.green }}>
|
|
|
- {job.favoriteCount || 0}
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 技能要求 */}
|
|
|
+ {job.requiredSkills && (
|
|
|
+ <div className="rounded-xl p-4 shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-3" style={{ color: COLORS.text.primary }}>
|
|
|
+ 技能要求
|
|
|
+ </h3>
|
|
|
+ <div className="flex flex-wrap gap-2">
|
|
|
+ {job.requiredSkills.split(/[,,、;;]/).map((skill: string, index: number) => (
|
|
|
+ <span
|
|
|
+ key={index}
|
|
|
+ className="px-3 py-1 rounded-full text-xs transition-all duration-200 hover:shadow-md"
|
|
|
+ style={{
|
|
|
+ backgroundColor: COLORS.ink.medium,
|
|
|
+ color: COLORS.text.primary
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {skill.trim()}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
</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}
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 福利待遇 */}
|
|
|
+ {job.benefits && (
|
|
|
+ <div className="rounded-xl p-4 shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-3" style={{ color: COLORS.text.primary }}>
|
|
|
+ 福利待遇
|
|
|
+ </h3>
|
|
|
+ <div className="flex flex-wrap gap-2">
|
|
|
+ {job.benefits.split(/[,,、;;]/).map((benefit: string, index: number) => (
|
|
|
+ <span
|
|
|
+ key={index}
|
|
|
+ className="px-3 py-1 rounded-full text-xs"
|
|
|
+ style={{
|
|
|
+ backgroundColor: `${COLORS.accent.green}20`,
|
|
|
+ color: COLORS.accent.green,
|
|
|
+ border: `1px solid ${COLORS.accent.green}30`
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {benefit.trim()}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
</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 className="rounded-xl p-4 shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
|
|
|
+ style={{ backgroundColor: 'rgba(255,255,255,0.8)', border: `1px solid ${COLORS.ink.medium}` }}
|
|
|
+ >
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-3" style={{ color: COLORS.text.primary }}>
|
|
|
+ 联系信息
|
|
|
+ </h3>
|
|
|
+ <div className="space-y-3">
|
|
|
+ <div className="flex items-center">
|
|
|
+ <BuildingOfficeIcon className="w-4 h-4 mr-2" style={{ color: COLORS.text.secondary }} />
|
|
|
+ <span className="text-sm" style={{ color: COLORS.text.secondary }}>
|
|
|
+ 联系人: {job.contactPerson}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center">
|
|
|
+ <PhoneIcon className="w-4 h-4 mr-2" style={{ color: COLORS.text.secondary }} />
|
|
|
+ <a
|
|
|
+ href={`tel:${job.contactPhone}`}
|
|
|
+ className="text-sm"
|
|
|
+ style={{ color: COLORS.accent.blue }}
|
|
|
+ >
|
|
|
+ {job.contactPhone}
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+ {job.contactEmail && (
|
|
|
+ <div className="flex items-center">
|
|
|
+ <EnvelopeIcon className="w-4 h-4 mr-2" style={{ color: COLORS.text.secondary }} />
|
|
|
+ <a
|
|
|
+ href={`mailto:${job.contactEmail}`}
|
|
|
+ className="text-sm"
|
|
|
+ style={{ color: COLORS.accent.blue }}
|
|
|
+ >
|
|
|
+ {job.contactEmail}
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
</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 }}
|
|
|
+ style={{ backgroundColor: COLORS.ink.light, borderColor: 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"
|
|
|
+ className="flex-1 px-4 py-3 rounded-full text-sm font-medium transition-all duration-300 hover:shadow-lg active:scale-95"
|
|
|
style={{
|
|
|
- backgroundColor: INK_COLORS.accent.blue,
|
|
|
- color: 'white',
|
|
|
- border: `1px solid ${INK_COLORS.accent.blue}`
|
|
|
+ backgroundColor: COLORS.accent.red,
|
|
|
+ color: 'white'
|
|
|
}}
|
|
|
+ disabled={job.status !== 1}
|
|
|
>
|
|
|
- 立即投递
|
|
|
+ {job.status === 1 ? '立即申请' : '招聘已结束'}
|
|
|
</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"
|
|
|
+ onClick={handlePhoneCall}
|
|
|
+ className="px-4 py-3 rounded-full text-sm font-medium transition-all duration-300 hover:shadow-lg active:scale-95"
|
|
|
style={{
|
|
|
- backgroundColor: 'transparent',
|
|
|
- color: INK_COLORS.accent.green,
|
|
|
- border: `2px solid ${INK_COLORS.accent.green}`
|
|
|
+ backgroundColor: COLORS.accent.blue,
|
|
|
+ color: 'white'
|
|
|
}}
|
|
|
>
|
|
|
- <ChatBubbleLeftIcon className="w-5 h-5" />
|
|
|
+ <PhoneIcon 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="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-30 p-4">
|
|
|
+ <div className="bg-white rounded-xl p-6 max-w-sm w-full">
|
|
|
+ <h3 className="font-serif text-lg font-medium mb-4 text-center" style={{ color: COLORS.text.primary }}>
|
|
|
+ 分享给朋友
|
|
|
+ </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 className="w-12 h-12 rounded-full flex items-center justify-center mb-2"
|
|
|
+ style={{ backgroundColor: COLORS.accent.green }}
|
|
|
+ >
|
|
|
+ <span className="text-white text-xl">💬</span>
|
|
|
</div>
|
|
|
- <span className="text-xs">微信</span>
|
|
|
+ <span className="text-xs" style={{ color: COLORS.text.secondary }}>微信</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">
|
|
|
+ <div className="w-12 h-12 rounded-full flex items-center justify-center mb-2"
|
|
|
+ style={{ backgroundColor: COLORS.accent.blue }}
|
|
|
+ >
|
|
|
<span className="text-white text-xl">📧</span>
|
|
|
</div>
|
|
|
- <span className="text-xs">邮件</span>
|
|
|
+ <span className="text-xs" style={{ color: COLORS.text.secondary }}>邮件</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">
|
|
|
+ <div className="w-12 h-12 rounded-full flex items-center justify-center mb-2"
|
|
|
+ style={{ backgroundColor: COLORS.accent.red }}
|
|
|
+ >
|
|
|
<span className="text-white text-xl">🔗</span>
|
|
|
</div>
|
|
|
- <span className="text-xs">复制链接</span>
|
|
|
+ <span className="text-xs" style={{ color: COLORS.text.secondary }}>复制</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 className="w-12 h-12 rounded-full flex items-center justify-center mb-2"
|
|
|
+ style={{ backgroundColor: COLORS.ink.medium }}
|
|
|
+ >
|
|
|
+ <span className="text-white text-xl">📱</span>
|
|
|
</div>
|
|
|
- <span className="text-xs">更多</span>
|
|
|
+ <span className="text-xs" style={{ color: COLORS.text.secondary }}>更多</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 }}
|
|
|
+ className="w-full mt-6 py-2 rounded-full text-sm"
|
|
|
+ style={{ backgroundColor: COLORS.ink.medium, color: COLORS.text.primary }}
|
|
|
>
|
|
|
取消
|
|
|
</button>
|