Files
worth-calculator/components/calculator.tsx
Zihan ebf26e66ac m
2024-10-31 04:13:53 +11:00

295 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React, { useState, useCallback } from 'react';
import { Wallet, Github, Star} from 'lucide-react'; // 保留需要的组件
const SalaryCalculator = () => {
const [formData, setFormData] = useState({
annualSalary: '', // 年薪
workDaysPerWeek: 5, // 每周工作天数
annualLeave: 5, // 年假天数
publicHolidays: 11, // 法定节假日
workHours: 8, // 工作时长
commuteHours: 1, // 通勤时长
breakHours: 1, // 预备时长
workEnvironment: '1.0', // 工作环境系数
heterogeneity: '1.0', // 异性环境系数
teamwork: '1.0', // 同事环境系数
education: '1.0' // 学历系数
});
const calculateWorkingDays = useCallback(() => {
const weeksPerYear = 52;
const totalWorkDays = weeksPerYear * formData.workDaysPerWeek;
const totalLeaves = Number(formData.annualLeave) + Number(formData.publicHolidays);
return totalWorkDays - totalLeaves;
}, [formData.workDaysPerWeek, formData.annualLeave, formData.publicHolidays]);
const calculateDailySalary = useCallback(() => {
if (!formData.annualSalary) return 0;
const workingDays = calculateWorkingDays();
return Number(formData.annualSalary) / workingDays;
}, [formData.annualSalary, calculateWorkingDays]);
const handleInputChange = (name: string, value: string) => {
setFormData(prev => ({
...prev,
[name]: value
}));
};
const calculateValue = () => {
if (!formData.annualSalary) return 0;
const dailySalary = calculateDailySalary();
const workHours = Number(formData.workHours);
const commuteHours = Number(formData.commuteHours);
const breakHours = Number(formData.breakHours);
const environmentFactor = Number(formData.workEnvironment) *
Number(formData.heterogeneity) *
Number(formData.teamwork);
return (dailySalary * environmentFactor) /
(35 * (workHours + commuteHours - 0.5 * breakHours) * Number(formData.education));
};
const value = calculateValue();
const getValueAssessment = () => {
if (!formData.annualSalary) return { text: "请输入年薪", color: "text-gray-500" };
if (value < 0.8) return { text: "很惨", color: "text-red-500" };
if (value <= 1.5) return { text: "一般", color: "text-yellow-500" };
if (value <= 2.0) return { text: "很爽", color: "text-green-500" };
return { text: "爽到爆炸", color: "text-purple-500" };
};
const RadioGroup = ({ label, name, value, onChange, options }: {
label: string;
name: string;
value: string;
onChange: (name: string, value: string) => void;
options: Array<{ label: string; value: string; }>;
}) => (
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">{label}</label>
<div className="grid grid-cols-4 gap-2">
{options.map((option) => (
<button
key={option.value}
className={`px-3 py-2 rounded-md text-sm transition-colors
${value === option.value
? 'bg-blue-100 text-blue-700 font-medium'
: 'bg-gray-50 hover:bg-gray-100'}`}
onClick={() => onChange(name, option.value)}
type="button"
>
{option.label}
</button>
))}
</div>
</div>
);
return (
<div className="max-w-4xl mx-auto p-4 space-y-8">
<div className="text-center space-y-2">
<h1 className="text-3xl font-bold text-gray-800">
b班上得值不值·
</h1>
{/* GitHub 链接和访问量计数 */}
<div className="flex items-center justify-center gap-4 text-sm text-gray-600">
<a
href="https://github.com/zippland/worth-calculator"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 hover:text-gray-900 transition-colors"
>
<Github className="w-4 h-4" />
<span className="hidden sm:inline">Star on</span>
<span>GitHub</span>
</a>
<div className="w-px h-4 bg-gray-300"></div>
<a
href="https://hits.seeyoufarm.com"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5"
>
<img
src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2FYourUsername%2Fworth-calculator&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=visits&edge_flat=true"
alt="访问量"
className="h-5"
/>
</a>
</div>
</div>
<div className="bg-white rounded-xl shadow-xl shadow-gray-200/50">
<div className="p-6 space-y-8">
{/* 薪资与工作时间 section */}
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700"></label>
<div className="flex items-center gap-2 mt-1">
<Wallet className="w-4 h-4 text-gray-500" />
<input
type="number"
value={formData.annualSalary}
onChange={(e) => handleInputChange('annualSalary', e.target.value)}
placeholder="税前年薪"
className="block w-full rounded-md border border-gray-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700"></label>
<input
type="number"
min="1"
max="7"
value={formData.workDaysPerWeek}
onChange={(e) => handleInputChange('workDaysPerWeek', e.target.value)}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700"></label>
<input
type="number"
value={formData.annualLeave}
onChange={(e) => handleInputChange('annualLeave', e.target.value)}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700"></label>
<input
type="number"
value={formData.publicHolidays}
onChange={(e) => handleInputChange('publicHolidays', e.target.value)}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700"></label>
<input
type="number"
value={formData.workHours}
onChange={(e) => handleInputChange('workHours', e.target.value)}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700"></label>
<input
type="number"
value={formData.commuteHours}
onChange={(e) => handleInputChange('commuteHours', e.target.value)}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700"></label>
<input
type="number"
value={formData.breakHours}
onChange={(e) => handleInputChange('breakHours', e.target.value)}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
</div>
</div>
<div className="border-t border-gray-200 my-6"></div>
{/* 环境系数 */}
<div className="space-y-4">
<RadioGroup
label="工作环境"
name="workEnvironment"
value={formData.workEnvironment}
onChange={handleInputChange}
options={[
{ label: '普通环境', value: '1.0' },
{ label: '偏僻地区', value: '0.8' },
{ label: '工厂工地', value: '0.9' },
{ label: 'CBD', value: '1.1' },
]}
/>
<RadioGroup
label="异性环境"
name="heterogeneity"
value={formData.heterogeneity}
onChange={handleInputChange}
options={[
{ label: '一般', value: '1.0' },
{ label: '没好看的', value: '0.9' },
{ label: '很多好看的', value: '1.1' },
]}
/>
<RadioGroup
label="同事环境"
name="teamwork"
value={formData.teamwork}
onChange={handleInputChange}
options={[
{ label: '普通同事', value: '1.0' },
{ label: 'SB同事多', value: '0.95' },
{ label: '优秀同事多', value: '1.05' },
]}
/>
<RadioGroup
label="学历要求"
name="education"
value={formData.education}
onChange={handleInputChange}
options={[
{ label: '专科及以下', value: '0.8' },
{ label: '普通本科', value: '1.0' },
{ label: '211本科', value: '1.2' },
{ label: '普通硕士', value: '1.4' },
{ label: '985硕士', value: '1.6' },
{ label: '普通博士', value: '1.8' },
{ label: '211博士', value: '2.0' },
]}
/>
</div>
</div>
</div>
{/* 结果卡片优化 */}
<div className="bg-gradient-to-r from-gray-50 to-gray-100 rounded-xl p-6 shadow-inner">
<div className="grid grid-cols-3 gap-8">
<div>
<div className="text-sm font-medium text-gray-500"></div>
<div className="text-2xl font-semibold mt-1">{calculateWorkingDays()}</div>
</div>
<div>
<div className="text-sm font-medium text-gray-500"></div>
<div className="text-2xl font-semibold mt-1">¥{calculateDailySalary().toFixed(2)}</div>
</div>
<div>
<div className="text-sm font-medium text-gray-500"></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>
</div>
);
};
export default SalaryCalculator;