This commit is contained in:
Zylan
2025-04-03 01:53:01 +08:00
parent 52948488d5
commit 5783e20504
2 changed files with 233 additions and 454 deletions

View File

@@ -573,13 +573,13 @@ const translations: Record<Language, Record<string, string>> = {
zh: {
// 标题和导航
'title': '这b班上得值不值·测算版',
'app_description': '计算薪资之外的工作真实价值',
'github': 'GitHub',
'email': 'Email',
'xiaohongshu': '小红书',
'redirect_notice': '已自动跳转,新网址无需科学上网',
'visits': '访问量',
'visitors': '访客数',
'star_request': '到GitHub给个⭐',
'star_request': '如果觉得好用请给项目点个⭐Star吧',
'history': '历史记录',
'no_history': '暂无历史记录',
'history_notice': '查看报告后将自动保存',
@@ -807,44 +807,17 @@ const translations: Record<Language, Record<string, string>> = {
'rating_excellent': '爽到爆炸',
'rating_perfect': '人生巅峰',
'share_country': '工作国家/地区',
// 添加步骤式表单相关翻译
'step_basic_info': '基本信息',
'step_education_work': '学历与工作',
'step_environment': '工作环境',
'step_extras': '额外福利',
'basic_info': '基本薪资与工作时间',
'work_time_schedule': '工作排班',
'daily_schedule': '日常时间分配',
'education_and_experience': '学历与工作经验',
'work_environment_factors': '工作环境因素',
'environment_location': '环境与位置',
'relationships': '人际关系',
'additional_benefits': '额外福利项目',
'job_type': '工作类型',
'previous': '上一步',
'next': '下一步',
// 添加表单完成度相关翻译
'completed': '已完成',
'steps': '步骤',
'continue_filling': '继续完成以下步骤可获得更准确的评估结果',
'preliminary_result': '初步结果 - 完成所有步骤获得更准确的评估',
// 添加新的提示信息翻译
'complete_steps_to_view': '完成所有步骤后可查看工作性价比报告',
'please_enter_salary': '请输入年薪以生成报告',
},
en: {
// Title and navigation
'title': 'Is My Job Worth the Grind?',
'app_description': 'Calculating the actual value of your job beyond just salary',
'github': 'GitHub',
'email': 'Email',
'xiaohongshu': 'Rednote',
'redirect_notice': 'Automatically redirected, no VPN needed',
'visits': 'Visits',
'visitors': 'Visitors',
'star_request': 'Star on GitHub',
'star_request': 'If you find this tool helpful, please give it a ⭐Star!',
'history': 'History',
'no_history': 'No history records',
'history_notice': 'Records will be saved after viewing reports',
@@ -1072,44 +1045,17 @@ const translations: Record<Language, Record<string, string>> = {
'rating_excellent': 'Excellent',
'rating_perfect': 'Outstanding',
'share_country': 'Work Country/Region',
// 添加步骤式表单相关翻译
'step_basic_info': 'Basic Info',
'step_education_work': 'Education',
'step_environment': 'Environment',
'step_extras': 'Benefits',
'basic_info': 'Basic Salary & Schedule',
'work_time_schedule': 'Work Schedule',
'daily_schedule': 'Daily Time Allocation',
'education_and_experience': 'Education & Experience',
'work_environment_factors': 'Work Environment',
'environment_location': 'Environment & Location',
'relationships': 'Relationships',
'additional_benefits': 'Additional Benefits',
'job_type': 'Job Type',
'previous': 'Previous',
'next': 'Next',
// 添加表单完成度相关翻译
'completed': 'completed',
'steps': 'steps',
'continue_filling': 'Continue with the next steps for a more accurate assessment',
'preliminary_result': 'Preliminary result - Complete all steps for better accuracy',
// 添加新的提示信息翻译
'complete_steps_to_view': 'Complete all steps to view your job value report',
'please_enter_salary': 'Please enter your salary to generate the report',
},
ja: {
// タイトルとナビゲーション
'title': 'この仕事、割に合ってる?',
'app_description': '給料だけでなく、仕事の本当の価値を計算する',
'github': 'GitHub',
'email': 'Email',
'xiaohongshu': '小紅書',
'redirect_notice': '自動的にリダイレクトされました',
'visits': 'アクセス数',
'visitors': '訪問者数',
'star_request': 'GitHubで⭐する',
'star_request': '役に立ったら、⭐スターを付けてください!',
'history': '履歴',
'no_history': '履歴がありません',
'history_notice': 'レポートを見た後、自動的に保存されます',
@@ -1337,33 +1283,6 @@ const translations: Record<Language, Record<string, string>> = {
'rating_excellent': '非常に優れている',
'rating_perfect': '理想的',
'share_country': '勤務国・地域',
// 添加步骤式表单相关翻译
'step_basic_info': '基本情報',
'step_education_work': '学歴・経験',
'step_environment': '職場環境',
'step_extras': '福利厚生',
'basic_info': '給与・勤務時間',
'work_time_schedule': '勤務スケジュール',
'daily_schedule': '日常の時間配分',
'education_and_experience': '学歴と職歴',
'work_environment_factors': '職場環境要素',
'environment_location': '環境と勤務地',
'relationships': '人間関係',
'additional_benefits': '追加の福利厚生',
'job_type': '雇用形態',
'previous': '前へ',
'next': '次へ',
// 添加表单完成度相关翻译
'completed': '完了',
'steps': 'ステップ',
'continue_filling': 'より正確な評価のために次のステップを続けてください',
'preliminary_result': '暫定結果 - すべてのステップを完了してより正確な評価を得る',
// 添加新的提示信息翻译
'complete_steps_to_view': 'すべてのステップを完了して仕事の価値レポートを確認してください',
'please_enter_salary': 'レポートを生成するには給与を入力してください',
}
};

View File

@@ -467,30 +467,6 @@ const SalaryCalculator = () => {
const [assessmentColor, setAssessmentColor] = useState("text-gray-500");
const [visitorVisible, setVisitorVisible] = useState(false);
// 添加步骤控制状态
const [activeStep, setActiveStep] = useState(0);
// 定义表单步骤
const formSteps = [
{ title: t('step_basic_info'), icon: "💰" },
{ title: t('step_education_work'), icon: "🎓" },
{ title: t('step_environment'), icon: "🏢" },
{ title: t('step_extras'), icon: "✨" }
];
// 步骤切换函数
const nextStep = () => {
if (activeStep < formSteps.length - 1) {
setActiveStep(activeStep + 1);
}
};
const prevStep = () => {
if (activeStep > 0) {
setActiveStep(activeStep - 1);
}
};
// 添加检查document对象存在的逻辑
useEffect(() => {
// 确保在客户端环境中执行
@@ -979,22 +955,28 @@ const SalaryCalculator = () => {
<div className="mb-4 text-center">
<h1 className="text-3xl md:text-4xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-purple-600 py-2">{t('title')}</h1>
{/* 添加应用描述文字 - 调整样式使其更加醒目 */}
<p className="text-sm md:text-base text-gray-700 dark:text-gray-200 mb-5 font-medium">
{t('app_description')}
</p>
<div className="mb-3">
<a
href="https://github.com/zippland/worth-calculator"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-gray-600 dark:text-gray-300 hover:text-blue-500 dark:hover:text-blue-400 transition-colors inline-flex items-center gap-1.5"
>
<Star className="h-3.5 w-3.5" />
{t('star_request')}
</a>
</div>
{/* 删除单独的star_request元素直接将star_request作为GitHub链接文本 */}
<div className="flex items-center justify-center gap-3 mb-2">
<p className="text-sm text-gray-500 dark:text-gray-400">v6.2.1</p>
<a
href="https://github.com/zippland/worth-calculator"
target="_blank"
rel="noopener noreferrer"
className="text-xs text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 transition-colors inline-flex items-center gap-1"
className="text-sm text-gray-500 hover:text-blue-500 dark:text-gray-400 dark:hover:text-blue-400 transition-colors flex items-center gap-1"
>
<Github className="h-3 w-3" />
{t('star_request')}
<Github className="h-3.5 w-3.5" />
{t('github')}
</a>
{/* <a
href="https://www.xiaohongshu.com/user/profile/623e8b080000000010007721?xsec_token=YBzoLUB4HsSITTBOgPAXY-0Gvqvn3HqHpcDeA3sHhDh-M%3D&xsec_source=app_share&xhsshare=CopyLink&appuid=5c5d5259000000001d00ef04&apptime=1743400694&share_id=b9bfcd5090f9473daf5c1d1dc3eb0921&share_channel=copy_link"
@@ -1009,9 +991,9 @@ const SalaryCalculator = () => {
{isBrowser && (
<button
onClick={() => setShowHistory(!showHistory)}
className="text-xs text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 transition-colors flex items-center gap-1 cursor-pointer"
className="text-sm text-gray-500 hover:text-blue-500 dark:text-gray-400 dark:hover:text-blue-400 transition-colors flex items-center gap-1 cursor-pointer"
>
<History className="h-3 w-3" />
<History className="h-3.5 w-3.5" />
{t('history')}
</button>
)}
@@ -1203,42 +1185,10 @@ const SalaryCalculator = () => {
)}
</div>
{/* 修改表单为步骤式表单 */}
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl shadow-gray-200/50 dark:shadow-black/30 mb-6">
{/* 步骤指示器 */}
<div className="border-b border-gray-200 dark:border-gray-700">
<div className="flex justify-between px-4 py-3">
{formSteps.map((step, index) => (
<button
key={index}
onClick={() => setActiveStep(index)}
className={`flex flex-col items-center justify-center px-4 py-2 rounded-lg transition-colors ${
activeStep === index
? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300'
: 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-400'
}`}
>
<span className="text-xl mb-1">{step.icon}</span>
<span className="text-sm font-medium">{step.title}</span>
{activeStep === index && (
<div className="h-1 w-1/2 bg-blue-500 rounded-full mt-2"></div>
)}
</button>
))}
</div>
{/* 删除此处的完成度指示器 */}
</div>
{/* 表单内容 */}
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl shadow-gray-200/50 dark:shadow-black/30">
<div className="p-6 space-y-8">
{/* 步骤1: 基础信息 */}
{activeStep === 0 && (
<div className="space-y-6 animate-fadeIn">
<h2 className="text-xl font-bold text-gray-800 dark:text-white border-l-4 border-blue-500 pl-3">
{t('basic_info')}
</h2>
{/* 薪资与工作时间 section */}
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
{selectedCountry !== 'CN' ?
@@ -1292,9 +1242,7 @@ const SalaryCalculator = () => {
</p>
</div>
<div className="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-lg">
<h3 className="font-medium text-gray-700 dark:text-gray-300 mb-3">{t('work_time_schedule')}</h3>
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">{t('work_days_per_week')}</label>
<input
@@ -1353,12 +1301,9 @@ const SalaryCalculator = () => {
onChange={(e) => handleInputChange('paidSickLeave', e.target.value)}
className="mt-1 block w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900 dark:text-white"
/>
</div>
</div>
</div>
<div className="bg-green-50 dark:bg-green-900/20 p-4 rounded-lg">
<h3 className="font-medium text-gray-700 dark:text-gray-300 mb-3">{t('daily_schedule')}</h3>
<div className="grid grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
@@ -1405,97 +1350,90 @@ const SalaryCalculator = () => {
</div>
</div>
</div>
</div>
)}
{/* 步骤2: 学历和工作经验 */}
{activeStep === 1 && (
<div className="space-y-6 animate-fadeIn">
<h2 className="text-xl font-bold text-gray-800 dark:text-white border-l-4 border-yellow-500 pl-3">
{t('education_and_experience')}
</h2>
<div className="bg-yellow-50 dark:bg-yellow-900/20 p-4 rounded-lg">
<div className="space-y-4">
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">{t('education_level')}</label>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">{t('degree_type')}</label>
<select
value={formData.degreeType}
onChange={(e) => handleInputChange('degreeType', e.target.value)}
className="block w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900 dark:text-white"
>
<option value="belowBachelor">{t('below_bachelor')}</option>
<option value="bachelor">{t('bachelor')}</option>
<option value="masters">{t('masters')}</option>
<option value="phd">{t('phd')}</option>
</select>
</div>
<div>
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">{t('school_type')}</label>
<select
value={formData.schoolType}
onChange={(e) => handleInputChange('schoolType', e.target.value)}
className="block w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900 dark:text-white"
disabled={formData.degreeType === 'belowBachelor'}
>
<option value="secondTier">{t('school_second_tier')}</option>
{formData.degreeType === 'bachelor' ? (
<>
<option value="firstTier">{t('school_first_tier_bachelor')}</option>
<option value="elite">{t('school_elite_bachelor')}</option>
</>
) : (
<>
<option value="firstTier">{t('school_first_tier_higher')}</option>
<option value="elite">{t('school_elite_higher')}</option>
</>
)}
</select>
</div>
</div>
{/* 硕士显示本科背景选项 */}
{formData.degreeType === 'masters' && (
<div className="mt-4">
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">{t('bachelor_background')}</label>
<select
value={formData.bachelorType}
onChange={(e) => handleInputChange('bachelorType', e.target.value)}
className="block w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900 dark:text-white"
>
<option value="secondTier">{t('school_second_tier')}</option>
<option value="firstTier">{t('school_first_tier_bachelor')}</option>
<option value="elite">{t('school_elite_bachelor')}</option>
</select>
</div>
)}
</div>
{/* 工作年限选择 */}
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">{t('work_years')}</label>
<div className="border-t border-gray-200 dark:border-gray-700 my-6"></div>
{/* 环境系数 */}
<div className="space-y-4">
{/* 学历和工作年限 */}
<div className="space-y-4">
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">{t('education_level')}</label>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">{t('degree_type')}</label>
<select
value={formData.workYears}
onChange={(e) => handleInputChange('workYears', e.target.value)}
value={formData.degreeType}
onChange={(e) => handleInputChange('degreeType', e.target.value)}
className="block w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900 dark:text-white"
>
<option value="0">{t('fresh_graduate')}</option>
<option value="1">{t('years_1_3')}</option>
<option value="2">{t('years_3_5')}</option>
<option value="4">{t('years_5_8')}</option>
<option value="6">{t('years_8_10')}</option>
<option value="10">{t('years_10_12')}</option>
<option value="15">{t('years_above_12')}</option>
<option value="belowBachelor">{t('below_bachelor')}</option>
<option value="bachelor">{t('bachelor')}</option>
<option value="masters">{t('masters')}</option>
<option value="phd">{t('phd')}</option>
</select>
</div>
<div>
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">{t('school_type')}</label>
<select
value={formData.schoolType}
onChange={(e) => handleInputChange('schoolType', e.target.value)}
className="block w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900 dark:text-white"
disabled={formData.degreeType === 'belowBachelor'}
>
<option value="secondTier">{t('school_second_tier')}</option>
{formData.degreeType === 'bachelor' ? (
<>
<option value="firstTier">{t('school_first_tier_bachelor')}</option>
<option value="elite">{t('school_elite_bachelor')}</option>
</>
) : (
<>
<option value="firstTier">{t('school_first_tier_higher')}</option>
<option value="elite">{t('school_elite_higher')}</option>
</>
)}
</select>
</div>
</div>
{/* 硕士显示本科背景选项 */}
{formData.degreeType === 'masters' && (
<div className="mt-4">
<label className="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">{t('bachelor_background')}</label>
<select
value={formData.bachelorType}
onChange={(e) => handleInputChange('bachelorType', e.target.value)}
className="block w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900 dark:text-white"
>
<option value="secondTier">{t('school_second_tier')}</option>
<option value="firstTier">{t('school_first_tier_bachelor')}</option>
<option value="elite">{t('school_elite_bachelor')}</option>
</select>
</div>
)}
</div>
<div className="bg-purple-50 dark:bg-purple-900/20 p-4 rounded-lg">
<h3 className="font-medium text-gray-700 dark:text-gray-300 mb-3">{t('job_type')}</h3>
{/* 工作年限选择 */}
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">{t('work_years')}</label>
<select
value={formData.workYears}
onChange={(e) => handleInputChange('workYears', e.target.value)}
className="block w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-gray-900 dark:text-white"
>
<option value="0">{t('fresh_graduate')}</option>
<option value="1">{t('years_1_3')}</option>
<option value="2">{t('years_3_5')}</option>
<option value="4">{t('years_5_8')}</option>
<option value="6">{t('years_8_10')}</option>
<option value="10">{t('years_10_12')}</option>
<option value="15">{t('years_above_12')}</option>
</select>
</div>
</div>
{/* 添加工作类型RadioGroup */}
<RadioGroup
label={t('job_stability')}
name="jobStability"
@@ -1503,27 +1441,14 @@ const SalaryCalculator = () => {
onChange={handleInputChange}
options={[
{ label: t('job_government'), value: 'government' },
{ label: t('job_state'), value: 'state' },
{ label: t('job_foreign'), value: 'foreign' },
{ label: t('job_private'), value: 'private' },
{ label: t('job_dispatch'), value: 'dispatch' },
{ label: t('job_freelance'), value: 'freelance' },
]}
/>
</div>
</div>
)}
{/* 步骤3: 工作环境 */}
{activeStep === 2 && (
<div className="space-y-6 animate-fadeIn">
<h2 className="text-xl font-bold text-gray-800 dark:text-white border-l-4 border-green-500 pl-3">
{t('work_environment_factors')}
</h2>
<div className="bg-green-50 dark:bg-green-900/20 p-4 rounded-lg">
<h3 className="font-medium text-gray-700 dark:text-gray-300 mb-3">{t('environment_location')}</h3>
<div className="space-y-5">
{ label: t('job_state'), value: 'state' },
{ label: t('job_foreign'), value: 'foreign' },
{ label: t('job_private'), value: 'private' },
{ label: t('job_dispatch'), value: 'dispatch' },
{ label: t('job_freelance'), value: 'freelance' },
]}
/>
<RadioGroup
label={t('work_environment')}
name="workEnvironment"
@@ -1563,12 +1488,7 @@ const SalaryCalculator = () => {
{ label: t('is_hometown'), value: 'yes' },
]}
/>
</div>
</div>
<div className="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-lg">
<h3 className="font-medium text-gray-700 dark:text-gray-300 mb-3">{t('relationships')}</h3>
<div className="space-y-5">
<RadioGroup
label={t('leadership')}
name="leadership"
@@ -1595,201 +1515,141 @@ const SalaryCalculator = () => {
{ label: t('team_excellent'), value: '1.2' },
]}
/>
</div>
{/* 班车和食堂选项作为加分项,加上勾选框控制 */}
<div className="space-y-2">
<div className="flex items-center mb-2">
<input
id="hasShuttle"
type="checkbox"
checked={formData.hasShuttle === true}
onChange={(e) => handleInputChange('hasShuttle', e.target.checked)}
className="h-4 w-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500"
/>
<label htmlFor="hasShuttle" className="ml-2 text-sm font-medium text-gray-700 dark:text-gray-300">
{t('shuttle')}
</label>
</div>
</div>
)}
{/* 步骤4: 额外福利 */}
{activeStep === 3 && (
<div className="space-y-6 animate-fadeIn">
<h2 className="text-xl font-bold text-gray-800 dark:text-white border-l-4 border-purple-500 pl-3">
{t('additional_benefits')}
</h2>
<div className="bg-purple-50 dark:bg-purple-900/20 p-4 rounded-lg space-y-6">
<div className="space-y-2">
<div className="flex items-center mb-2">
<input
id="hasShuttle"
type="checkbox"
checked={formData.hasShuttle === true}
onChange={(e) => handleInputChange('hasShuttle', e.target.checked)}
className="h-4 w-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500"
/>
<label htmlFor="hasShuttle" className="ml-2 text-sm font-medium text-gray-700 dark:text-gray-300">
{t('shuttle')}
</label>
</div>
{formData.hasShuttle && (
<div className="ml-6 p-3 bg-white dark:bg-gray-800 rounded-md shadow-sm">
<RadioGroup
label=""
name="shuttle"
value={formData.shuttle}
onChange={handleInputChange}
options={[
{ label: t('shuttle_none'), value: '1.0' },
{ label: t('shuttle_inconvenient'), value: '0.9' },
{ label: t('shuttle_convenient'), value: '0.7' },
{ label: t('shuttle_direct'), value: '0.5' },
]}
/>
</div>
)}
</div>
<div className="space-y-2">
<div className="flex items-center mb-2">
<input
id="hasCanteen"
type="checkbox"
checked={formData.hasCanteen === true}
onChange={(e) => handleInputChange('hasCanteen', e.target.checked)}
className="h-4 w-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500"
/>
<label htmlFor="hasCanteen" className="ml-2 text-sm font-medium text-gray-700 dark:text-gray-300">
{t('canteen')}
</label>
</div>
{formData.hasCanteen && (
<div className="ml-6 p-3 bg-white dark:bg-gray-800 rounded-md shadow-sm">
<RadioGroup
label=""
name="canteen"
value={formData.canteen}
onChange={handleInputChange}
options={[
{ label: t('canteen_none'), value: '1.0' },
{ label: t('canteen_average'), value: '1.05' },
{ label: t('canteen_good'), value: '1.1' },
{ label: t('canteen_excellent'), value: '1.15' },
]}
/>
</div>
)}
</div>
</div>
{formData.hasShuttle && (
<RadioGroup
label=""
name="shuttle"
value={formData.shuttle}
onChange={handleInputChange}
options={[
{ label: t('shuttle_none'), value: '1.0' },
{ label: t('shuttle_inconvenient'), value: '0.9' },
{ label: t('shuttle_convenient'), value: '0.7' },
{ label: t('shuttle_direct'), value: '0.5' },
]}
/>
)}
</div>
)}
{/* 步骤导航按钮 */}
<div className="flex justify-between mt-8">
<button
onClick={prevStep}
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors
${activeStep > 0
? 'bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600'
: 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-gray-800 dark:text-gray-600'}`}
disabled={activeStep === 0}
>
{t('previous')}
</button>
{activeStep < formSteps.length - 1 ? (
<button
onClick={nextStep}
className="px-5 py-2.5 rounded-md text-sm font-medium bg-blue-500 text-white hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 transition-colors flex items-center shadow-sm"
>
{t('next')}
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
) : (
<div></div> /* 占位 */
)}
</div>
{/* 将完成度指示器移动到这里 */}
<div className="mt-4 px-1">
<div className="w-full bg-gray-200 dark:bg-gray-700 h-2 rounded-full overflow-hidden">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-300 ease-in-out"
style={{ width: `${(activeStep + 1) * 25}%` }}
></div>
<div className="space-y-2">
<div className="flex items-center mb-2">
<input
id="hasCanteen"
type="checkbox"
checked={formData.hasCanteen === true}
onChange={(e) => handleInputChange('hasCanteen', e.target.checked)}
className="h-4 w-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500"
/>
<label htmlFor="hasCanteen" className="ml-2 text-sm font-medium text-gray-700 dark:text-gray-300">
{t('canteen')}
</label>
</div>
<div className="flex justify-between mt-1 text-xs text-gray-500 dark:text-gray-400">
<span>{Math.min((activeStep + 1) * 25, 100)}% {t('completed')}</span>
<span>{activeStep + 1}/{formSteps.length} {t('steps')}</span>
{formData.hasCanteen && (
<RadioGroup
label=""
name="canteen"
value={formData.canteen}
onChange={handleInputChange}
options={[
{ label: t('canteen_none'), value: '1.0' },
{ label: t('canteen_average'), value: '1.05' },
{ label: t('canteen_good'), value: '1.1' },
{ label: t('canteen_excellent'), value: '1.15' },
]}
/>
)}
</div>
</div>
{/* 删除提示信息 */}
</div>
</div>
{/* 结果卡片 - 完全重新设计 */}
<div className="rounded-xl overflow-hidden">
<div ref={shareResultsRef} className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-xl p-6 shadow-inner">
{/* 根据完成状态显示不同内容 */}
{activeStep < formSteps.length - 1 ? (
/* 未完成全部步骤 - 显示鼓励提示 */
<div className="text-center py-4">
<div className="text-gray-500 dark:text-gray-400 text-sm mb-1">
{t('complete_steps_to_view')}
</div>
<div className="font-medium text-gray-700 dark:text-gray-300">
{Math.min((activeStep + 1) * 25, 100)}% {t('completed')}
</div>
{/* 结果卡片优化 */}
<div ref={shareResultsRef} className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-xl p-6 shadow-inner">
<div className="grid grid-cols-3 gap-8">
<div>
<div className="text-sm font-medium text-gray-500 dark:text-gray-400">{t('working_days_per_year')}</div>
<div className="text-2xl font-semibold mt-1 text-gray-900 dark:text-white">{calculateWorkingDays()}{t('days_unit')}</div>
</div>
<div>
<div className="text-sm font-medium text-gray-500 dark:text-gray-400">{t('average_daily_salary')}</div>
<div className="text-2xl font-semibold mt-1 text-gray-900 dark:text-white">
{getCurrencySymbol(selectedCountry)}{getDisplaySalary()}
</div>
) : (
/* 已完成全部步骤 - 显示查看报告按钮 */
<div className="text-center">
{formData.salary ? (
<Link
href={{
pathname: '/share',
query: {
value: value.toFixed(2),
assessment: getValueAssessmentKey(),
assessmentColor: getValueAssessment().color,
cityFactor: formData.cityFactor,
workHours: formData.workHours,
commuteHours: formData.commuteHours,
restTime: formData.restTime,
dailySalary: getDisplaySalary(),
isYuan: selectedCountry !== 'CN' ? 'false' : 'true',
workDaysPerYear: calculateWorkingDays().toString(),
workDaysPerWeek: formData.workDaysPerWeek,
wfhDaysPerWeek: formData.wfhDaysPerWeek,
annualLeave: formData.annualLeave,
paidSickLeave: formData.paidSickLeave,
publicHolidays: formData.publicHolidays,
workEnvironment: formData.workEnvironment,
leadership: formData.leadership,
teamwork: formData.teamwork,
degreeType: formData.degreeType,
schoolType: formData.schoolType,
education: formData.education,
homeTown: formData.homeTown,
shuttle: formData.hasShuttle ? formData.shuttle : '1.0',
canteen: formData.hasCanteen ? formData.canteen : '1.0',
workYears: formData.workYears,
jobStability: formData.jobStability,
bachelorType: formData.bachelorType,
countryCode: selectedCountry,
countryName: getCountryName(selectedCountry),
currencySymbol: getCurrencySymbol(selectedCountry),
hasShuttle: formData.hasShuttle,
hasCanteen: formData.hasCanteen,
}
}}
className="inline-flex items-center gap-2 px-6 py-3 rounded-md text-sm font-medium bg-blue-500 text-white hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 transition-colors shadow-sm"
onClick={() => saveToHistory()}
>
<FileText className="w-5 h-5 mr-1" />
{t('view_report')}
</Link>
) : (
<div className="text-amber-600 dark:text-amber-400">
{t('please_enter_salary')}
</div>
)}
</div>
<div>
<div className="text-sm font-medium text-gray-500 dark:text-gray-400">{t('job_value')}</div>
<div className={`text-2xl font-semibold mt-1 ${getValueAssessment().color}`}>
{value.toFixed(2)}
<span className="text-base ml-2">({getValueAssessment().text})</span>
</div>
)}
</div>
</div>
{/* 修改分享按钮为链接到分享页面,并保存到历史 */}
<div className="mt-6 flex justify-end">
<Link
href={{
pathname: '/share',
query: {
value: value.toFixed(2),
assessment: getValueAssessmentKey(),
assessmentColor: getValueAssessment().color,
cityFactor: formData.cityFactor,
workHours: formData.workHours,
commuteHours: formData.commuteHours,
restTime: formData.restTime,
dailySalary: getDisplaySalary(),
isYuan: selectedCountry !== 'CN' ? 'false' : 'true',
workDaysPerYear: calculateWorkingDays().toString(),
workDaysPerWeek: formData.workDaysPerWeek,
wfhDaysPerWeek: formData.wfhDaysPerWeek,
annualLeave: formData.annualLeave,
paidSickLeave: formData.paidSickLeave,
publicHolidays: formData.publicHolidays,
workEnvironment: formData.workEnvironment,
leadership: formData.leadership,
teamwork: formData.teamwork,
degreeType: formData.degreeType,
schoolType: formData.schoolType,
education: formData.education,
homeTown: formData.homeTown,
shuttle: formData.hasShuttle ? formData.shuttle : '1.0',
canteen: formData.hasCanteen ? formData.canteen : '1.0',
workYears: formData.workYears,
jobStability: formData.jobStability,
bachelorType: formData.bachelorType,
countryCode: selectedCountry,
countryName: getCountryName(selectedCountry),
currencySymbol: getCurrencySymbol(selectedCountry),
hasShuttle: formData.hasShuttle,
hasCanteen: formData.hasCanteen,
}
}}
className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors
${formData.salary ? 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900 dark:text-blue-300 dark:hover:bg-blue-800' :
'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-gray-800 dark:text-gray-600'}`}
onClick={() => formData.salary ? saveToHistory() : null}
>
<FileText className="w-4 h-4" />
{t('view_report')}
</Link>
</div>
</div>
</div>