mirror of
https://github.com/Zippland/worth-calculator.git
synced 2026-01-19 01:21:03 +08:00
return
This commit is contained in:
@@ -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': 'レポートを生成するには給与を入力してください',
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user