|
|
@@ -23,6 +23,26 @@ import {
|
|
|
TrophyIcon
|
|
|
} from '@heroicons/react/24/outline';
|
|
|
|
|
|
+// 工具函数:格式化薪资显示
|
|
|
+const formatSalary = (min: number, max: number): string => {
|
|
|
+ if (min === 0 && max === 0) return '面议';
|
|
|
+ if (min === max) return `¥${min.toLocaleString()}/月`;
|
|
|
+ return `¥${min.toLocaleString()}-${max.toLocaleString()}/月`;
|
|
|
+};
|
|
|
+
|
|
|
+// 工具函数:格式化发布时间
|
|
|
+const formatPublishDate = (date: string): string => {
|
|
|
+ const publishDate = new Date(date);
|
|
|
+ const now = new Date();
|
|
|
+ const diffDays = Math.floor((now.getTime() - publishDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
|
+
|
|
|
+ if (diffDays === 0) return '今天';
|
|
|
+ if (diffDays === 1) return '昨天';
|
|
|
+ if (diffDays < 7) return `${diffDays}天前`;
|
|
|
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)}周前`;
|
|
|
+ return `${Math.floor(diffDays / 30)}月前`;
|
|
|
+};
|
|
|
+
|
|
|
// 中国水墨风格色彩方案
|
|
|
const COLORS = {
|
|
|
ink: {
|
|
|
@@ -149,6 +169,7 @@ const transformSilverTalents = (talents: any[]) =>
|
|
|
jobSeekingStatus: talent.jobSeekingStatus === 1 ? 'ACTIVELY_SEEKING' : talent.jobSeekingStatus === 2 ? 'OPEN_TO_OPPORTUNITIES' : 'NOT_SEEKING',
|
|
|
knowledgeShareCount: talent.knowledgeShareCount || 0,
|
|
|
knowledgeReadCount: talent.knowledgeReadCount || 0,
|
|
|
+ knowledgeLikeCount: talent.knowledgeLikeCount || 0,
|
|
|
knowledgeRankingScore: parseFloat(talent.knowledgeRankingScore || 0),
|
|
|
totalPoints: talent.totalPoints || 0,
|
|
|
city: talent.city || '未知地区',
|
|
|
@@ -156,16 +177,45 @@ const transformSilverTalents = (talents: any[]) =>
|
|
|
}));
|
|
|
|
|
|
const transformSilverPositions = (positions: any[]) =>
|
|
|
- positions.map(position => ({
|
|
|
- id: position.id,
|
|
|
- title: position.title,
|
|
|
- organization: position.company?.name || '未知单位',
|
|
|
- budget: position.salaryRange || '面议',
|
|
|
- deadline: position.deadline || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
|
|
|
- location: position.location || '全国',
|
|
|
- category: position.category?.name || '其他',
|
|
|
- createdAt: position.createdAt
|
|
|
- }));
|
|
|
+ positions.map(position => {
|
|
|
+ // 解析薪资范围
|
|
|
+ let salaryMin = 0;
|
|
|
+ let salaryMax = 0;
|
|
|
+ if (position.salaryRange) {
|
|
|
+ const salaryMatch = position.salaryRange.match(/(\d+)-(\d+)/);
|
|
|
+ if (salaryMatch) {
|
|
|
+ salaryMin = parseInt(salaryMatch[1]);
|
|
|
+ salaryMax = parseInt(salaryMatch[2]);
|
|
|
+ } else {
|
|
|
+ const singleSalary = parseInt(position.salaryRange.replace(/\D/g, ''));
|
|
|
+ salaryMin = singleSalary;
|
|
|
+ salaryMax = singleSalary;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换福利标签
|
|
|
+ let benefits: string[] = [];
|
|
|
+ if (position.benefits) {
|
|
|
+ benefits = position.benefits.split(',').map((benefit: string) => benefit.trim()).filter((benefit: string) => benefit);
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ id: position.id,
|
|
|
+ title: position.title,
|
|
|
+ companyName: position.company?.name || '未知企业',
|
|
|
+ location: position.location || '全国',
|
|
|
+ salaryMin,
|
|
|
+ salaryMax,
|
|
|
+ jobType: position.jobType || '全职',
|
|
|
+ description: position.description || '',
|
|
|
+ requirements: position.requirements || '',
|
|
|
+ benefits,
|
|
|
+ publishDate: position.createdAt,
|
|
|
+ views: position.viewCount || 0,
|
|
|
+ applications: position.applicationCount || 0,
|
|
|
+ companyLogo: position.company?.logo
|
|
|
+ };
|
|
|
+ });
|
|
|
|
|
|
// 主页面组件
|
|
|
const NewHomePage: React.FC = () => {
|
|
|
@@ -230,7 +280,7 @@ const NewHomePage: React.FC = () => {
|
|
|
|
|
|
// 使用真实的银龄岗位数据
|
|
|
const silverPositions = transformSilverPositions(homeData.silverPositions || [])
|
|
|
- .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
|
|
+ .sort((a, b) => new Date(b.publishDate).getTime() - new Date(a.publishDate).getTime())
|
|
|
.slice(0, 3);
|
|
|
|
|
|
// 使用真实的银龄人才数据
|
|
|
@@ -822,78 +872,116 @@ const NewHomePage: React.FC = () => {
|
|
|
</div>
|
|
|
|
|
|
<div className="space-y-3">
|
|
|
- {silverPositions.map((position) => (
|
|
|
+ {silverPositions.map((job) => (
|
|
|
<div
|
|
|
- key={position.id}
|
|
|
- className="flex items-center justify-between p-4 rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer backdrop-blur-sm"
|
|
|
+ key={job.id}
|
|
|
+ className="mb-3 mx-0 rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer backdrop-blur-sm border overflow-hidden"
|
|
|
style={{
|
|
|
backgroundColor: 'rgba(255,255,255,0.8)',
|
|
|
- border: `1px solid ${COLORS.ink.medium}`
|
|
|
+ borderColor: COLORS.ink.medium
|
|
|
}}
|
|
|
- onClick={() => navigate(`/silver-jobs/${position.id}`)}
|
|
|
+ onClick={() => navigate(`/silver-jobs/${job.id}`)}
|
|
|
>
|
|
|
- <div className="flex items-center flex-1">
|
|
|
- <img
|
|
|
- src={position.image || unsplash.getJobImage(position.id)}
|
|
|
- alt={position.title}
|
|
|
- className="w-14 h-14 rounded-full object-cover mr-4 border-2 shadow-sm"
|
|
|
- style={{ borderColor: COLORS.ink.medium }}
|
|
|
- />
|
|
|
- <div className="flex-1">
|
|
|
- <h4
|
|
|
- className={`font-medium ${FONT_STYLES.body}`}
|
|
|
- style={{ color: COLORS.text.primary }}
|
|
|
- >
|
|
|
- {position.title}
|
|
|
- </h4>
|
|
|
- <p
|
|
|
- className={`${FONT_STYLES.caption}`}
|
|
|
- style={{ color: COLORS.text.secondary }}
|
|
|
- >
|
|
|
- {position.organization}
|
|
|
- </p>
|
|
|
- <div className="flex items-center mt-1 space-x-2">
|
|
|
- <span
|
|
|
- className={`${FONT_STYLES.small} px-2 py-1 rounded-full flex items-center`}
|
|
|
- style={{
|
|
|
- backgroundColor: COLORS.accent.green,
|
|
|
- color: 'white'
|
|
|
- }}
|
|
|
+ <div className="p-4">
|
|
|
+ {/* 顶部信息 */}
|
|
|
+ <div className="flex items-start justify-between mb-3">
|
|
|
+ <div className="flex items-center">
|
|
|
+ <div
|
|
|
+ className="w-14 h-14 rounded-full mr-3 flex items-center justify-center text-lg font-bold border-2"
|
|
|
+ style={{ borderColor: COLORS.ink.medium }}
|
|
|
>
|
|
|
- {position.budget}
|
|
|
- </span>
|
|
|
+ {job.companyLogo ? (
|
|
|
+ <img
|
|
|
+ src={job.companyLogo}
|
|
|
+ alt={job.companyName}
|
|
|
+ className="w-full h-full rounded-full object-cover"
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <span style={{ color: COLORS.ink.dark }}>
|
|
|
+ {job.companyName.slice(0, 2)}
|
|
|
+ </span>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <h3
|
|
|
+ className="font-serif text-lg font-medium leading-snug"
|
|
|
+ style={{ color: COLORS.text.primary }}
|
|
|
+ >
|
|
|
+ {job.title}
|
|
|
+ </h3>
|
|
|
+ <p
|
|
|
+ className="font-sans text-sm leading-normal mt-1"
|
|
|
+ style={{ color: COLORS.text.secondary }}
|
|
|
+ >
|
|
|
+ {job.companyName}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <span
|
|
|
+ className="px-2 py-1 rounded-full text-xs font-medium"
|
|
|
+ style={{ backgroundColor: COLORS.accent.blue, color: 'white' }}
|
|
|
+ >
|
|
|
+ {job.jobType}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 基本信息 */}
|
|
|
+ <div className="flex items-center justify-between mb-2">
|
|
|
+ <div className="flex items-center">
|
|
|
+ <span className="text-sm mr-2">📍</span>
|
|
|
<span
|
|
|
- className={`${FONT_STYLES.small} px-2 py-1 rounded-full flex items-center`}
|
|
|
- style={{
|
|
|
- backgroundColor: COLORS.accent.blue,
|
|
|
- color: 'white'
|
|
|
- }}
|
|
|
+ className="font-sans text-sm"
|
|
|
+ style={{ color: COLORS.text.secondary }}
|
|
|
>
|
|
|
- <MapPinIcon className="w-3 h-3 mr-1" />
|
|
|
- {position.location}
|
|
|
+ {job.location}
|
|
|
</span>
|
|
|
</div>
|
|
|
+ <span className="text-sm">💰 {formatSalary(job.salaryMin, job.salaryMax)}</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div className="text-right flex flex-col items-end">
|
|
|
- <span
|
|
|
- className={`text-xs px-2 py-1 rounded-full mb-1`}
|
|
|
- style={{
|
|
|
- color: COLORS.text.light,
|
|
|
- backgroundColor: COLORS.ink.light
|
|
|
- }}
|
|
|
+
|
|
|
+ {/* 职位描述 */}
|
|
|
+ <p
|
|
|
+ className="font-sans text-sm leading-relaxed mb-3 line-clamp-2"
|
|
|
+ style={{ color: COLORS.text.light }}
|
|
|
>
|
|
|
- 截止: {new Date(position.deadline).toLocaleDateString()}
|
|
|
- </span>
|
|
|
- <span
|
|
|
- className={`text-xs px-2 py-1 rounded-full`}
|
|
|
- style={{
|
|
|
- backgroundColor: COLORS.accent.green,
|
|
|
- color: 'white'
|
|
|
- }}
|
|
|
+ {job.description}
|
|
|
+ </p>
|
|
|
+
|
|
|
+ {/* 福利标签 */}
|
|
|
+ {job.benefits.length > 0 && (
|
|
|
+ <div className="flex flex-wrap gap-1 mb-3">
|
|
|
+ {job.benefits.slice(0, 3).map((benefit: string, index: number) => (
|
|
|
+ <span
|
|
|
+ key={index}
|
|
|
+ className="px-2 py-1 text-xs rounded-full"
|
|
|
+ style={{
|
|
|
+ backgroundColor: COLORS.accent.green + '20',
|
|
|
+ color: COLORS.accent.green,
|
|
|
+ borderColor: COLORS.accent.green + '30'
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {benefit}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 底部信息 */}
|
|
|
+ <div className="flex items-center justify-between text-xs pt-3 border-t"
|
|
|
+ style={{ borderColor: COLORS.ink.medium }}
|
|
|
>
|
|
|
- {position.category}
|
|
|
- </span>
|
|
|
+ <div className="flex items-center space-x-3">
|
|
|
+ <span style={{ color: COLORS.text.light }}>
|
|
|
+ 👁️ {job.views}
|
|
|
+ </span>
|
|
|
+ <span style={{ color: COLORS.text.light }}>
|
|
|
+ 📋 {job.applications}人投递
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <span style={{ color: COLORS.text.light }}>
|
|
|
+ {formatPublishDate(job.publishDate)}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
))}
|