├── .eslintrc.json
├── .gitignore
├── LICENSE
├── README.md
├── app
├── favicon.ico
├── fonts
│ ├── GeistMonoVF.woff
│ └── GeistVF.woff
├── globals.css
├── layout.tsx
├── page.tsx
└── share
│ └── page.tsx
├── components
├── LanguageContext.tsx
├── LanguageSwitcher.tsx
├── ShareCard.tsx
└── calculator.tsx
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
├── favicon.ico
├── file.svg
├── formula.png
├── globe.svg
├── next.svg
├── title.png
├── vercel.svg
├── website.png
└── window.svg
├── tailwind.config.ts
├── test.txt
├── test
└── test.txt
├── title.png
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"],
3 | "rules": {
4 | "@typescript-eslint/no-unused-vars": "off"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for commiting if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Zylan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | **[⚡ Try it now ⚡](https://worthjob.zippland.com)**
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | ---
32 |
33 |
34 |
35 |
📊 Job Worth Calculator
36 |
37 |
Calculating the actual value of your job beyond just salary
38 |
39 | ### ✨ Features
40 |
41 | - **💰 Comprehensive Evaluation**: Calculate job worth based on salary, work hours, commute time, environment, and more
42 | - **🌏 PPP Conversion**: International salary comparison with Purchasing Power Parity conversion across 190+ countries
43 | - **👩🎓 Personal Factors**: Customize calculations with personal education level, work experience, and more
44 | - **📱 Detailed Report**: Generate a shareable, downloadable job analysis report
45 | - **🌐 Internationalization**: Available in both English and Chinese
46 | - **📱 Mobile Friendly**: Responsive design works on all devices
47 |
48 | ### 🖥️ How to Use
49 |
50 | 1. Enter your annual salary
51 | 2. Select your country/region
52 | 3. Fill in work details (days per week, hours, commute time, etc.)
53 | 4. Specify environmental factors (city, work environment, team, etc.)
54 | 5. Input your education and experience
55 | 6. View your job worth score and detailed evaluation
56 | 7. Generate a shareable report
57 |
58 | ### 📊 The Calculation
59 |
60 | The job worth score is calculated using a comprehensive formula that accounts for:
61 | - Standardized daily salary (adjusted for PPP)
62 | - Work-life balance factors (hours, commute, WFH options)
63 | - Environmental aspects (office location, team dynamics)
64 | - Educational qualification premiums
65 | - Experience-based expectations
66 |
67 | ### 👨💻 Contributing
68 |
69 | Contributions are welcome! Here's how you can help:
70 |
71 | - [Open an issue](https://github.com/zippland/worth-calculator/issues/new) if you have suggestions or find a bug
72 | - Fork the repository and submit a PR for new features or bug fixes
73 | - Improve documentation or translations
74 |
75 | Please make sure to test your changes before submitting a PR.
76 |
77 | ### 📝 License
78 |
79 | [MIT License](LICENSE)
80 |
81 |
82 |
83 | ---
84 |
85 |
86 |
87 |
📊 工作性价比计算器
88 |
89 |
全面考量,计算薪资之外的工作真实价值
90 |
91 | ### ✨ 特点
92 |
93 | - **💰 全面评估**: 基于薪资、工作时间、通勤时间、工作环境等多方面因素计算工作价值
94 | - **🌏 PPP转换**: 通过购买力平价(PPP)转换支持190多个国家的薪资比较
95 | - **👩🎓 个人因素**: 根据个人学历、工作经验等定制计算
96 | - **📱 详细报告**: 生成可分享、可下载的工作分析报告
97 | - **🌐 国际化**: 支持中英文双语
98 | - **📱 移动友好**: 响应式设计,适用于所有设备
99 |
100 | ### 🖥️ 使用方法
101 |
102 | 1. 输入年薪
103 | 2. 选择工作国家/地区
104 | 3. 填写工作细节(每周工作天数、工作时长、通勤时间等)
105 | 4. 指定环境因素(城市、工作环境、团队等)
106 | 5. 输入学历和工作经验
107 | 6. 查看工作性价比分数和详细评估
108 | 7. 生成可分享的报告
109 |
110 | ### 📊 计算方法
111 |
112 | 工作性价比分数使用全面的公式计算,考虑了:
113 | - 标准化日薪(经PPP调整)
114 | - 工作生活平衡因素(工作时长、通勤、远程工作选项)
115 | - 环境因素(办公地点、团队关系)
116 | - 学历加成
117 | - 基于经验的期望值调整
118 |
119 | ### 👨💻 贡献指南
120 |
121 | 欢迎参与贡献!以下是您可以提供帮助的方式:
122 |
123 | - 如有建议或发现错误,请[提交问题](https://github.com/zippland/worth-calculator/issues/new)
124 | - 分叉仓库并提交PR,增加新功能或修复bug
125 | - 改进文档或翻译
126 |
127 | 请确保在提交PR前测试您的更改。
128 |
129 | ### 📝 许可证
130 |
131 | [MIT 许可证](LICENSE)
132 |
133 |
134 |
135 | ---
136 |
137 |
138 |
139 |
📊 仕事の価値計算機
140 |
141 |
給料だけでなく、仕事の本当の価値を計算する
142 |
143 | ### ✨ 特徴
144 |
145 | - **💰 総合的な評価**: 給与、労働時間、通勤時間、職場環境など複数の要素に基づいて仕事の価値を計算
146 | - **🌏 PPP変換**: 購買力平価(PPP)による190カ国以上の国際的な給与比較
147 | - **👩🎓 個人要素**: 学歴、職務経験などに基づいてカスタマイズされた計算
148 | - **📱 詳細レポート**: 共有可能でダウンロード可能な仕事分析レポートの生成
149 | - **🌐 多言語対応**: 英語、中国語、日本語で利用可能
150 | - **📱 モバイル対応**: すべてのデバイスで動作するレスポンシブデザイン
151 |
152 | ### 🖥️ 使用方法
153 |
154 | 1. 年収を入力
155 | 2. 国/地域を選択
156 | 3. 勤務詳細(週あたりの勤務日数、勤務時間、通勤時間など)を入力
157 | 4. 環境要素(都市、職場環境、チームなど)を指定
158 | 5. 学歴と経験を入力
159 | 6. 仕事の価値スコアと詳細評価を確認
160 | 7. 共有可能なレポートを生成
161 |
162 | ### 📊 計算方法
163 |
164 | 仕事の価値スコアは以下を考慮した総合的な計算式を使用しています:
165 | - 標準化された日給(PPPで調整済み)
166 | - ワークライフバランス要素(労働時間、通勤、リモートワークオプション)
167 | - 環境的側面(オフィスの場所、チームダイナミクス)
168 | - 教育資格による優遇
169 | - 経験に基づく期待値
170 |
171 | ### 👨💻 貢献方法
172 |
173 | 貢献は大歓迎です!以下の方法でご協力いただけます:
174 |
175 | - 提案やバグを発見した場合は[問題を報告](https://github.com/zippland/worth-calculator/issues/new)してください
176 | - リポジトリをフォークし、新機能やバグ修正のためのPRを提出
177 | - ドキュメントや翻訳の改善
178 |
179 | PRを提出する前に変更をテストしてください。
180 |
181 | ### 📝 ライセンス
182 |
183 | [MITライセンス](LICENSE)
184 |
185 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/app/favicon.ico
--------------------------------------------------------------------------------
/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
23 | /* 页面和卡片的过渡动画 */
24 | @keyframes fadeIn {
25 | from {
26 | opacity: 0;
27 | transform: translateY(20px);
28 | }
29 | to {
30 | opacity: 1;
31 | transform: translateY(0);
32 | }
33 | }
34 |
35 | .animate-fadeIn {
36 | animation: fadeIn 0.5s ease-out forwards;
37 | }
38 |
39 | /* 卡片翻页效果 */
40 | @keyframes flipIn {
41 | from {
42 | opacity: 0;
43 | transform: rotateY(-10deg) translateZ(-100px);
44 | }
45 | to {
46 | opacity: 1;
47 | transform: rotateY(0) translateZ(0);
48 | }
49 | }
50 |
51 | .animate-flipIn {
52 | animation: flipIn 0.6s ease-out forwards;
53 | }
54 |
55 | /* 按钮点击效果 */
56 | .btn-pulse {
57 | position: relative;
58 | overflow: hidden;
59 | }
60 |
61 | .btn-pulse::after {
62 | content: '';
63 | position: absolute;
64 | top: 50%;
65 | left: 50%;
66 | width: 5px;
67 | height: 5px;
68 | background: rgba(255, 255, 255, 0.5);
69 | opacity: 0;
70 | border-radius: 100%;
71 | transform: scale(1, 1) translate(-50%, -50%);
72 | transform-origin: 50% 50%;
73 | }
74 |
75 | .btn-pulse:focus::after {
76 | animation: ripple 1s ease-out;
77 | }
78 |
79 | @keyframes ripple {
80 | 0% {
81 | transform: scale(0, 0);
82 | opacity: 0.5;
83 | }
84 | 100% {
85 | transform: scale(50, 50);
86 | opacity: 0;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import localFont from "next/font/local";
3 | import "./globals.css";
4 | import { LanguageProvider } from "@/components/LanguageContext";
5 | import { Analytics } from "@vercel/analytics/next";
6 |
7 | const geistSans = localFont({
8 | src: "./fonts/GeistVF.woff",
9 | variable: "--font-geist-sans",
10 | weight: "100 900",
11 | });
12 | const geistMono = localFont({
13 | src: "./fonts/GeistMonoVF.woff",
14 | variable: "--font-geist-mono",
15 | weight: "100 900",
16 | });
17 |
18 | export const metadata: Metadata = {
19 | title: {
20 | default: "Job Worth Calculator",
21 | template: "%s | Job Worth Calculator"
22 | },
23 | alternates: {
24 | languages: {
25 | "en-US": "/en",
26 | "zh-CN": "/",
27 | },
28 | },
29 | description: "这b班上得值不值 - 计算你的工作性价比 | Job Worth Calculator - Calculate your job's value",
30 | verification: {
31 | google: "_OQGiIpYz87USAsgJV2C07-JJhQ8myV_4GoM1kDjFic",
32 | },
33 | };
34 |
35 | export default function RootLayout({
36 | children,
37 | }: Readonly<{
38 | children: React.ReactNode;
39 | }>) {
40 | return (
41 |
42 |
43 |
44 |
45 |
46 |
47 |
50 |
51 | {children}
52 |
53 |
54 |
55 |
119 |
120 |
121 | );
122 | }
123 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Calculator from '@/components/calculator';
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
--------------------------------------------------------------------------------
/app/share/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useSearchParams } from 'next/navigation';
4 | import dynamic from 'next/dynamic';
5 | import React, { Suspense } from 'react';
6 | import { LanguageProvider } from '@/components/LanguageContext';
7 | import { LanguageSwitcher } from '@/components/LanguageSwitcher';
8 |
9 | // 动态导入ShareCard组件,禁用SSR
10 | const ShareCard = dynamic(() => import('@/components/ShareCard'), { ssr: false });
11 |
12 | // 一个包装组件,负责从URL参数获取数据
13 | function ShareCardWrapper() {
14 | const searchParams = useSearchParams();
15 |
16 | // 从URL参数中获取数据 - 基础数据
17 | const value = searchParams.get('value') || '0';
18 | const assessment = searchParams.get('assessment') || '请输入年薪';
19 | const assessmentColor = searchParams.get('assessmentColor') || 'text-gray-500';
20 | const cityFactor = searchParams.get('cityFactor') || '1.0';
21 | const workHours = searchParams.get('workHours') || '10';
22 | const commuteHours = searchParams.get('commuteHours') || '2';
23 | const restTime = searchParams.get('restTime') || '2';
24 | const dailySalary = searchParams.get('dailySalary') || '0';
25 | const isYuan = searchParams.get('isYuan') || 'true';
26 | const workDaysPerYear = searchParams.get('workDaysPerYear') || '250';
27 | const countryCode = searchParams.get('countryCode') || 'CN';
28 | const countryName = searchParams.get('countryName') || '中国';
29 | const currencySymbol = searchParams.get('currencySymbol') || '¥';
30 |
31 | // 额外参数 - 详细工作信息
32 | const workDaysPerWeek = searchParams.get('workDaysPerWeek') || '5';
33 | const wfhDaysPerWeek = searchParams.get('wfhDaysPerWeek') || '0';
34 | const annualLeave = searchParams.get('annualLeave') || '5';
35 | const paidSickLeave = searchParams.get('paidSickLeave') || '12';
36 | const publicHolidays = searchParams.get('publicHolidays') || '13';
37 |
38 | // 额外参数 - 工作环境
39 | const workEnvironment = searchParams.get('workEnvironment') || '1.0';
40 | const leadership = searchParams.get('leadership') || '1.0';
41 | const teamwork = searchParams.get('teamwork') || '1.0';
42 | const homeTown = searchParams.get('homeTown') || 'no';
43 | const shuttle = searchParams.get('shuttle') || '1.0';
44 | const canteen = searchParams.get('canteen') || '1.0';
45 | const hasShuttle = searchParams.get('hasShuttle') === 'true';
46 | const hasCanteen = searchParams.get('hasCanteen') === 'true';
47 |
48 | // 额外参数 - 学历和工作经验
49 | const degreeType = searchParams.get('degreeType') || 'bachelor';
50 | const schoolType = searchParams.get('schoolType') || 'elite';
51 | const bachelorType = searchParams.get('bachelorType') || 'elite';
52 | const education = searchParams.get('education') || '1.2';
53 | const workYears = searchParams.get('workYears') || '0';
54 | const jobStability = searchParams.get('jobStability') || 'private';
55 |
56 | return (
57 |
58 |
99 |
100 | );
101 | }
102 |
103 | // 加载过程中显示的组件
104 | function ShareLoading() {
105 | return (
106 |
107 |
正在加载报告...
108 |
Loading report...
109 |
110 | );
111 | }
112 |
113 | // 主页面组件
114 | export default function SharePage() {
115 | return (
116 |
117 |
118 |
119 |
120 |
121 |
122 |
}>
123 |
124 |
125 |
126 |
127 |
128 | );
129 | }
--------------------------------------------------------------------------------
/components/LanguageContext.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
4 |
5 | // 定义语言类型
6 | export type Language = 'zh' | 'en' | 'ja';
7 |
8 | // 创建上下文接口
9 | interface LanguageContextType {
10 | language: Language;
11 | setLanguage: (language: Language) => void;
12 | t: (key: string) => string;
13 | }
14 |
15 | // 创建默认上下文
16 | const defaultContext: LanguageContextType = {
17 | language: 'zh',
18 | setLanguage: () => {},
19 | t: (key: string) => key,
20 | };
21 |
22 | // 创建上下文
23 | const LanguageContext = createContext(defaultContext);
24 |
25 | // 国家名称映射
26 | export const countryNames: Record> = {
27 | zh: {
28 | 'AF': '阿富汗',
29 | 'AO': '安哥拉',
30 | 'AL': '阿尔巴尼亚',
31 | 'AR': '阿根廷',
32 | 'AM': '亚美尼亚',
33 | 'AG': '安提瓜和巴布达',
34 | 'AU': '澳大利亚',
35 | 'AT': '奥地利',
36 | 'AZ': '阿塞拜疆',
37 | 'BI': '布隆迪',
38 | 'BE': '比利时',
39 | 'BJ': '贝宁',
40 | 'BF': '布基纳法索',
41 | 'BD': '孟加拉国',
42 | 'BG': '保加利亚',
43 | 'BH': '巴林',
44 | 'BS': '巴哈马',
45 | 'BA': '波斯尼亚和黑塞哥维那',
46 | 'BY': '白俄罗斯',
47 | 'BZ': '伯利兹',
48 | 'BO': '玻利维亚',
49 | 'BR': '巴西',
50 | 'BB': '巴巴多斯',
51 | 'BN': '文莱达鲁萨兰国',
52 | 'BT': '不丹',
53 | 'BW': '博茨瓦纳',
54 | 'CF': '中非共和国',
55 | 'CA': '加拿大',
56 | 'CH': '瑞士',
57 | 'CL': '智利',
58 | 'CN': '中国',
59 | 'CI': '科特迪瓦',
60 | 'CM': '喀麦隆',
61 | 'CD': '刚果(金)',
62 | 'CG': '刚果(布)',
63 | 'CO': '哥伦比亚',
64 | 'KM': '科摩罗',
65 | 'CV': '佛得角',
66 | 'CR': '哥斯达黎加',
67 | 'CY': '塞浦路斯',
68 | 'CZ': '捷克共和国',
69 | 'DE': '德国',
70 | 'DJ': '吉布提',
71 | 'DM': '多米尼克',
72 | 'DK': '丹麦',
73 | 'DO': '多米尼加共和国',
74 | 'DZ': '阿尔及利亚',
75 | 'EC': '厄瓜多尔',
76 | 'EG': '阿拉伯埃及共和国',
77 | 'ES': '西班牙',
78 | 'EE': '爱沙尼亚',
79 | 'ET': '埃塞俄比亚',
80 | 'FI': '芬兰',
81 | 'FJ': '斐济',
82 | 'FR': '法国',
83 | 'GA': '加蓬',
84 | 'GB': '英国',
85 | 'GE': '格鲁吉亚',
86 | 'GH': '加纳',
87 | 'GN': '几内亚',
88 | 'GM': '冈比亚',
89 | 'GW': '几内亚比绍共和国',
90 | 'GQ': '赤道几内亚',
91 | 'GR': '希腊',
92 | 'GD': '格林纳达',
93 | 'GT': '危地马拉',
94 | 'GY': '圭亚那',
95 | 'HK': '香港特别行政区',
96 | 'HN': '洪都拉斯',
97 | 'HR': '克罗地亚',
98 | 'HT': '海地',
99 | 'HU': '匈牙利',
100 | 'ID': '印度尼西亚',
101 | 'IN': '印度',
102 | 'IE': '爱尔兰',
103 | 'IR': '伊朗伊斯兰共和国',
104 | 'IQ': '伊拉克',
105 | 'IS': '冰岛',
106 | 'IL': '以色列',
107 | 'IT': '意大利',
108 | 'JM': '牙买加',
109 | 'JO': '约旦',
110 | 'JP': '日本',
111 | 'KZ': '哈萨克斯坦',
112 | 'KE': '肯尼亚',
113 | 'KG': '吉尔吉斯斯坦',
114 | 'KH': '柬埔寨',
115 | 'KI': '基里巴斯',
116 | 'KN': '圣基茨和尼维斯',
117 | 'KR': '大韩民国',
118 | 'LA': '老挝',
119 | 'LB': '黎巴嫩',
120 | 'LR': '利比里亚',
121 | 'LY': '利比亚',
122 | 'LC': '圣卢西亚',
123 | 'LK': '斯里兰卡',
124 | 'LS': '莱索托',
125 | 'LT': '立陶宛',
126 | 'LU': '卢森堡',
127 | 'LV': '拉脱维亚',
128 | 'MO': '中国澳门特别行政区',
129 | 'MA': '摩洛哥',
130 | 'MD': '摩尔多瓦',
131 | 'MG': '马达加斯加',
132 | 'MV': '马尔代夫',
133 | 'MX': '墨西哥',
134 | 'MK': '北马其顿',
135 | 'ML': '马里',
136 | 'MT': '马耳他',
137 | 'MM': '缅甸',
138 | 'ME': '黑山',
139 | 'MN': '蒙古',
140 | 'MZ': '莫桑比克',
141 | 'MR': '毛里塔尼亚',
142 | 'MU': '毛里求斯',
143 | 'MW': '马拉维',
144 | 'MY': '马来西亚',
145 | 'NA': '纳米比亚',
146 | 'NE': '尼日尔',
147 | 'NG': '尼日利亚',
148 | 'NI': '尼加拉瓜',
149 | 'NL': '荷兰',
150 | 'NO': '挪威',
151 | 'NP': '尼泊尔',
152 | 'NZ': '新西兰',
153 | 'PK': '巴基斯坦',
154 | 'PA': '巴拿马',
155 | 'PE': '秘鲁',
156 | 'PH': '菲律宾',
157 | 'PG': '巴布亚新几内亚',
158 | 'PL': '波兰',
159 | 'PR': '波多黎各',
160 | 'PT': '葡萄牙',
161 | 'PY': '巴拉圭',
162 | 'PS': '约旦河西岸和加沙',
163 | 'QA': '卡塔尔',
164 | 'RO': '罗马尼亚',
165 | 'RU': '俄罗斯联邦',
166 | 'RW': '卢旺达',
167 | 'SA': '沙特阿拉伯',
168 | 'SD': '苏丹',
169 | 'SN': '塞内加尔',
170 | 'SG': '新加坡',
171 | 'SB': '所罗门群岛',
172 | 'SL': '塞拉利昂',
173 | 'SV': '萨尔瓦多',
174 | 'SO': '索马里',
175 | 'RS': '塞尔维亚',
176 | 'ST': '圣多美和普林西比',
177 | 'SR': '苏里南',
178 | 'SK': '斯洛伐克共和国',
179 | 'SI': '斯洛文尼亚',
180 | 'SE': '瑞典',
181 | 'SZ': '斯威士兰',
182 | 'SC': '塞舌尔',
183 | 'TC': '特克斯科斯群岛',
184 | 'TD': '乍得',
185 | 'TG': '多哥',
186 | 'TH': '泰国',
187 | 'TJ': '塔吉克斯坦',
188 | 'TL': '东帝汶',
189 | 'TT': '特立尼达和多巴哥',
190 | 'TN': '突尼斯',
191 | 'TR': '土耳其',
192 | 'TV': '图瓦卢',
193 | 'TW': '台湾',
194 | 'TZ': '坦桑尼亚',
195 | 'UG': '乌干达',
196 | 'UA': '乌克兰',
197 | 'UY': '乌拉圭',
198 | 'US': '美国',
199 | 'UZ': '乌兹别克斯坦',
200 | 'VC': '圣文森特和格林纳丁斯',
201 | 'VN': '越南',
202 | 'VU': '瓦努阿图',
203 | 'XK': '科索沃',
204 | 'ZA': '南非',
205 | 'ZM': '赞比亚',
206 | 'ZW': '津巴布韦'
207 | },
208 | en: {
209 | 'AF': 'Afghanistan',
210 | 'AO': 'Angola',
211 | 'AL': 'Albania',
212 | 'AR': 'Argentina',
213 | 'AM': 'Armenia',
214 | 'AG': 'Antigua and Barbuda',
215 | 'AU': 'Australia',
216 | 'AT': 'Austria',
217 | 'AZ': 'Azerbaijan',
218 | 'BI': 'Burundi',
219 | 'BE': 'Belgium',
220 | 'BJ': 'Benin',
221 | 'BF': 'Burkina Faso',
222 | 'BD': 'Bangladesh',
223 | 'BG': 'Bulgaria',
224 | 'BH': 'Bahrain',
225 | 'BS': 'Bahamas',
226 | 'BA': 'Bosnia and Herzegovina',
227 | 'BY': 'Belarus',
228 | 'BZ': 'Belize',
229 | 'BO': 'Bolivia',
230 | 'BR': 'Brazil',
231 | 'BB': 'Barbados',
232 | 'BN': 'Brunei Darussalam',
233 | 'BT': 'Bhutan',
234 | 'BW': 'Botswana',
235 | 'CF': 'Central African Republic',
236 | 'CA': 'Canada',
237 | 'CH': 'Switzerland',
238 | 'CL': 'Chile',
239 | 'CN': 'China',
240 | 'CI': 'Côte d\'Ivoire',
241 | 'CM': 'Cameroon',
242 | 'CD': 'Congo (DRC)',
243 | 'CG': 'Congo (Republic)',
244 | 'CO': 'Colombia',
245 | 'KM': 'Comoros',
246 | 'CV': 'Cape Verde',
247 | 'CR': 'Costa Rica',
248 | 'CY': 'Cyprus',
249 | 'CZ': 'Czech Republic',
250 | 'DE': 'Germany',
251 | 'DJ': 'Djibouti',
252 | 'DM': 'Dominica',
253 | 'DK': 'Denmark',
254 | 'DO': 'Dominican Republic',
255 | 'DZ': 'Algeria',
256 | 'EC': 'Ecuador',
257 | 'EG': 'Egypt',
258 | 'ES': 'Spain',
259 | 'EE': 'Estonia',
260 | 'ET': 'Ethiopia',
261 | 'FI': 'Finland',
262 | 'FJ': 'Fiji',
263 | 'FR': 'France',
264 | 'GA': 'Gabon',
265 | 'GB': 'United Kingdom',
266 | 'GE': 'Georgia',
267 | 'GH': 'Ghana',
268 | 'GN': 'Guinea',
269 | 'GM': 'Gambia',
270 | 'GW': 'Guinea-Bissau',
271 | 'GQ': 'Equatorial Guinea',
272 | 'GR': 'Greece',
273 | 'GD': 'Grenada',
274 | 'GT': 'Guatemala',
275 | 'GY': 'Guyana',
276 | 'HK': 'Hong Kong SAR',
277 | 'HN': 'Honduras',
278 | 'HR': 'Croatia',
279 | 'HT': 'Haiti',
280 | 'HU': 'Hungary',
281 | 'ID': 'Indonesia',
282 | 'IN': 'India',
283 | 'IE': 'Ireland',
284 | 'IR': 'Iran',
285 | 'IQ': 'Iraq',
286 | 'IS': 'Iceland',
287 | 'IL': 'Israel',
288 | 'IT': 'Italy',
289 | 'JM': 'Jamaica',
290 | 'JO': 'Jordan',
291 | 'JP': 'Japan',
292 | 'KZ': 'Kazakhstan',
293 | 'KE': 'Kenya',
294 | 'KG': 'Kyrgyzstan',
295 | 'KH': 'Cambodia',
296 | 'KI': 'Kiribati',
297 | 'KN': 'St. Kitts and Nevis',
298 | 'KR': 'South Korea',
299 | 'LA': 'Laos',
300 | 'LB': 'Lebanon',
301 | 'LR': 'Liberia',
302 | 'LY': 'Libya',
303 | 'LC': 'St. Lucia',
304 | 'LK': 'Sri Lanka',
305 | 'LS': 'Lesotho',
306 | 'LT': 'Lithuania',
307 | 'LU': 'Luxembourg',
308 | 'LV': 'Latvia',
309 | 'MO': 'Macao SAR',
310 | 'MA': 'Morocco',
311 | 'MD': 'Moldova',
312 | 'MG': 'Madagascar',
313 | 'MV': 'Maldives',
314 | 'MX': 'Mexico',
315 | 'MK': 'North Macedonia',
316 | 'ML': 'Mali',
317 | 'MT': 'Malta',
318 | 'MM': 'Myanmar',
319 | 'ME': 'Montenegro',
320 | 'MN': 'Mongolia',
321 | 'MZ': 'Mozambique',
322 | 'MR': 'Mauritania',
323 | 'MU': 'Mauritius',
324 | 'MW': 'Malawi',
325 | 'MY': 'Malaysia',
326 | 'NA': 'Namibia',
327 | 'NE': 'Niger',
328 | 'NG': 'Nigeria',
329 | 'NI': 'Nicaragua',
330 | 'NL': 'Netherlands',
331 | 'NO': 'Norway',
332 | 'NP': 'Nepal',
333 | 'NZ': 'New Zealand',
334 | 'PK': 'Pakistan',
335 | 'PA': 'Panama',
336 | 'PE': 'Peru',
337 | 'PH': 'Philippines',
338 | 'PG': 'Papua New Guinea',
339 | 'PL': 'Poland',
340 | 'PR': 'Puerto Rico',
341 | 'PT': 'Portugal',
342 | 'PY': 'Paraguay',
343 | 'PS': 'West Bank and Gaza',
344 | 'QA': 'Qatar',
345 | 'RO': 'Romania',
346 | 'RU': 'Russia',
347 | 'RW': 'Rwanda',
348 | 'SA': 'Saudi Arabia',
349 | 'SD': 'Sudan',
350 | 'SN': 'Senegal',
351 | 'SG': 'Singapore',
352 | 'SB': 'Solomon Islands',
353 | 'SL': 'Sierra Leone',
354 | 'SV': 'El Salvador',
355 | 'SO': 'Somalia',
356 | 'RS': 'Serbia',
357 | 'ST': 'São Tomé and Principe',
358 | 'SR': 'Suriname',
359 | 'SK': 'Slovak Republic',
360 | 'SI': 'Slovenia',
361 | 'SE': 'Sweden',
362 | 'SZ': 'Eswatini',
363 | 'SC': 'Seychelles',
364 | 'TC': 'Turks and Caicos Islands',
365 | 'TD': 'Chad',
366 | 'TG': 'Togo',
367 | 'TH': 'Thailand',
368 | 'TJ': 'Tajikistan',
369 | 'TL': 'Timor-Leste',
370 | 'TT': 'Trinidad and Tobago',
371 | 'TN': 'Tunisia',
372 | 'TR': 'Turkey',
373 | 'TV': 'Tuvalu',
374 | 'TW': 'Taiwan',
375 | 'TZ': 'Tanzania',
376 | 'UG': 'Uganda',
377 | 'UA': 'Ukraine',
378 | 'UY': 'Uruguay',
379 | 'US': 'United States',
380 | 'UZ': 'Uzbekistan',
381 | 'VC': 'St. Vincent and the Grenadines',
382 | 'VN': 'Vietnam',
383 | 'VU': 'Vanuatu',
384 | 'XK': 'Kosovo',
385 | 'ZA': 'South Africa',
386 | 'ZM': 'Zambia',
387 | 'ZW': 'Zimbabwe'
388 | },
389 | ja: {
390 | 'AF': 'アフガニスタン',
391 | 'AL': 'アルバニア',
392 | 'DZ': 'アルジェリア',
393 | 'AO': 'アンゴラ',
394 | 'AR': 'アルゼンチン',
395 | 'AM': 'アルメニア',
396 | 'AU': 'オーストラリア',
397 | 'AT': 'オーストリア',
398 | 'AZ': 'アゼルバイジャン',
399 | 'BI': 'ブルンジ',
400 | 'BE': 'ベルギー',
401 | 'BJ': 'ベナン',
402 | 'BF': 'ブルキナファソ',
403 | 'BD': 'バングラデシュ',
404 | 'BG': 'ブルガリア',
405 | 'BH': 'バーレーン',
406 | 'BS': 'バハマ',
407 | 'BA': 'ボスニア・ヘルツェゴビナ',
408 | 'BY': 'ベラルーシ',
409 | 'BZ': 'ベリーズ',
410 | 'BO': 'ボリビア',
411 | 'BR': 'ブラジル',
412 | 'BB': 'バルバドス',
413 | 'BN': 'ブルネイ',
414 | 'BT': 'ブータン',
415 | 'BW': 'ボツワナ',
416 | 'CF': '中央アフリカ共和国',
417 | 'CA': 'カナダ',
418 | 'CH': 'スイス',
419 | 'CL': 'チリ',
420 | 'CN': '中国',
421 | 'CI': 'コートジボワール',
422 | 'CM': 'カメルーン',
423 | 'CD': 'コンゴ民主共和国',
424 | 'CG': 'コンゴ共和国',
425 | 'CO': 'コロンビア',
426 | 'KM': 'コモロ',
427 | 'CV': 'カーボベルデ',
428 | 'CR': 'コスタリカ',
429 | 'CY': 'キプロス',
430 | 'CZ': 'チェコ共和国',
431 | 'DE': 'ドイツ',
432 | 'DJ': 'ジブチ',
433 | 'DM': 'ドミニカ国',
434 | 'DK': 'デンマーク',
435 | 'DO': 'ドミニカ共和国',
436 | 'EC': 'エクアドル',
437 | 'EG': 'エジプト',
438 | 'ES': 'スペイン',
439 | 'EE': 'エストニア',
440 | 'ET': 'エチオピア',
441 | 'FI': 'フィンランド',
442 | 'FJ': 'フィジー',
443 | 'FR': 'フランス',
444 | 'GA': 'ガボン',
445 | 'GB': 'イギリス',
446 | 'GE': 'ジョージア',
447 | 'GH': 'ガーナ',
448 | 'GN': 'ギニア',
449 | 'GM': 'ガンビア',
450 | 'GW': 'ギニアビサウ',
451 | 'GQ': '赤道ギニア',
452 | 'GR': 'ギリシャ',
453 | 'GD': 'グレナダ',
454 | 'GT': 'グアテマラ',
455 | 'GY': 'ガイアナ',
456 | 'HK': '香港特別行政区',
457 | 'HN': 'ホンジュラス',
458 | 'HR': 'クロアチア',
459 | 'HT': 'ハイチ',
460 | 'HU': 'ハンガリー',
461 | 'ID': 'インドネシア',
462 | 'IN': 'インド',
463 | 'IE': 'アイルランド',
464 | 'IR': 'イラン',
465 | 'IQ': 'イラク',
466 | 'IS': 'アイスランド',
467 | 'IL': 'イスラエル',
468 | 'IT': 'イタリア',
469 | 'JM': 'ジャマイカ',
470 | 'JO': 'ヨルダン',
471 | 'JP': '日本',
472 | 'KZ': 'カザフスタン',
473 | 'KE': 'ケニア',
474 | 'KG': 'キルギス',
475 | 'KH': 'カンボジア',
476 | 'KI': 'キリバス',
477 | 'KN': 'セントクリストファー・ネイビス',
478 | 'KR': '韓国',
479 | 'LA': 'ラオス',
480 | 'LB': 'レバノン',
481 | 'LR': 'リベリア',
482 | 'LY': 'リビア',
483 | 'LC': 'セントルシア',
484 | 'LK': 'スリランカ',
485 | 'LS': 'レソト',
486 | 'LT': 'リトアニア',
487 | 'LU': 'ルクセンブルク',
488 | 'LV': 'ラトビア',
489 | 'MO': 'マカオ特別行政区',
490 | 'MA': 'モロッコ',
491 | 'MD': 'モルドバ',
492 | 'MG': 'マダガスカル',
493 | 'MV': 'モルディブ',
494 | 'MX': 'メキシコ',
495 | 'MK': '北マケドニア',
496 | 'ML': 'マリ',
497 | 'MT': 'マルタ',
498 | 'MM': 'ミャンマー',
499 | 'ME': 'モンテネグロ',
500 | 'MN': 'モンゴル',
501 | 'MZ': 'モザンビーク',
502 | 'MR': 'モーリタニア',
503 | 'MU': 'モーリシャス',
504 | 'MW': 'マラウイ',
505 | 'MY': 'マレーシア',
506 | 'NA': 'ナミビア',
507 | 'NE': 'ニジェール',
508 | 'NG': 'ナイジェリア',
509 | 'NI': 'ニカラグア',
510 | 'NL': 'オランダ',
511 | 'NO': 'ノルウェー',
512 | 'NP': 'ネパール',
513 | 'NZ': 'ニュージーランド',
514 | 'PK': 'パキスタン',
515 | 'PA': 'パナマ',
516 | 'PE': 'ペルー',
517 | 'PH': 'フィリピン',
518 | 'PG': 'パプアニューギニア',
519 | 'PL': 'ポーランド',
520 | 'PR': 'プエルトリコ',
521 | 'PT': 'ポルトガル',
522 | 'PY': 'パラグアイ',
523 | 'PS': 'パレスチナ',
524 | 'QA': 'カタール',
525 | 'RO': 'ルーマニア',
526 | 'RU': 'ロシア',
527 | 'RW': 'ルワンダ',
528 | 'SA': 'サウジアラビア',
529 | 'SD': 'スーダン',
530 | 'SN': 'セネガル',
531 | 'SG': 'シンガポール',
532 | 'SB': 'ソロモン諸島',
533 | 'SL': 'シエラレオネ',
534 | 'SV': 'エルサルバドル',
535 | 'SO': 'ソマリア',
536 | 'RS': 'セルビア',
537 | 'ST': 'サントメ・プリンシペ',
538 | 'SR': 'スリナム',
539 | 'SK': 'スロバキア',
540 | 'SI': 'スロベジア',
541 | 'SE': 'スウェーデン',
542 | 'SZ': 'エスワティニ',
543 | 'SC': 'セーシェル',
544 | 'TC': 'タークス・カイコス諸島',
545 | 'TD': 'チャド',
546 | 'TG': 'トーゴ',
547 | 'TH': 'タイ',
548 | 'TJ': 'タジキスタン',
549 | 'TL': '東ティモール',
550 | 'TT': 'トリニダード・トバゴ',
551 | 'TN': 'チュニジア',
552 | 'TR': 'トルコ',
553 | 'TV': 'ツバル',
554 | 'TW': '台湾',
555 | 'TZ': 'タンザニア',
556 | 'UG': 'ウガンダ',
557 | 'UA': 'ウクライナ',
558 | 'UY': 'ウルグアイ',
559 | 'US': 'アメリカ合衆国',
560 | 'UZ': 'ウズベキスタン',
561 | 'VC': 'セントビンセント・グレナディーン',
562 | 'VN': 'ベトナム',
563 | 'VU': 'バヌアツ',
564 | 'XK': 'コソボ',
565 | 'ZA': '南アフリカ',
566 | 'ZM': 'ザンビア',
567 | 'ZW': 'ジンバブエ'
568 | }
569 | };
570 |
571 | // 翻译数据
572 | const translations: Record> = {
573 | zh: {
574 | // 标题和导航
575 | 'title': '这b班上得值不值·测算版',
576 | 'github': 'GitHub',
577 | 'email': 'Email',
578 | 'xiaohongshu': '小红书',
579 | 'redirect_notice': '已自动跳转,新网址无需科学上网',
580 | 'visits': '访问量',
581 | 'visitors': '访客数',
582 | 'star_request': '如果觉得好用,请给项目点个⭐Star吧!',
583 | 'history': '历史记录',
584 | 'no_history': '暂无历史记录',
585 | 'history_notice': '查看报告后将自动保存',
586 | 'delete_history': '删除',
587 | 'clear_all': '清空全部',
588 | 'restore_history': '恢复此记录',
589 |
590 | // 表单标签
591 | 'annual_salary_cny': '年薪总包(元)',
592 | 'annual_salary_foreign': '年薪总包(当地货币)',
593 | 'annual_salary': '年薪总包',
594 | 'salary_placeholder_cny': '税前年薪',
595 | 'salary_placeholder_foreign': '使用当地货币',
596 | 'salary_placeholder': '税前年薪',
597 | 'non_china_salary': '非中国地区薪资',
598 | 'ppp_factor': '购买力平价(PPP)转换因子',
599 | 'ppp_tooltip': 'PPP转换因子是将各国货币购买力标准化的指标。例如中国为4.19,表示1美元在美国的购买力等同于4.19元人民币在中国的购买力。',
600 | 'ppp_placeholder': '请输入购买力平价转换因子',
601 | 'ppp_common_regions': '常见地区:中国大陆:4.19, 日本:102.59, 美国:1.00, 新加坡:0.84',
602 | 'view_more': '查看更多',
603 | 'country_selection': '工作国家/地区',
604 | 'selected_ppp': '当前PPP值',
605 | 'work_days_per_week': '每周工作天数/d',
606 | 'wfh_days_per_week': '周WFH天数/d',
607 | 'wfh_tooltip': 'WFH指居家办公(Work From Home),这里填写的是前面工作天数中有多少天是在家办公的。',
608 | 'annual_leave': '年假天数/d',
609 | 'public_holidays': '法定假日/d',
610 | 'paid_sick_leave': '带薪病假/d',
611 | 'work_hours': '总工时/h',
612 | 'work_hours_tooltip': '工时:是指"下班时间-上班时间"的总时间,包括吃饭、午休、加班等(不含通勤)。',
613 | 'commute_hours': '通勤/h',
614 | 'commute_tooltip': '通勤时长是指上下班往返的总时间,即家到公司和公司回家的时间总和。',
615 | 'rest_time': '休息&摸鱼/h',
616 |
617 | // 环境系数
618 | 'job_stability': '职业稳定度',
619 | 'job_government': '政府/事业单位',
620 | 'job_state': '国企/大型企业',
621 | 'job_private': '私企/狼性文化',
622 | 'job_foreign': '外企/守法企业',
623 | 'job_dispatch': '劳务派遣/OD',
624 | 'job_freelance': '自由职业',
625 | 'work_environment': '工作环境',
626 | 'env_remote': '偏僻的工厂/工地/户外',
627 | 'env_factory': '工厂/工地/户外',
628 | 'env_normal': '普通环境',
629 | 'env_cbd': 'CBD',
630 | 'city_factor': '所在城市(按生活成本选择)',
631 | 'city_tier1': '一线城市',
632 | 'city_newtier1': '新一线',
633 | 'city_tier2': '二线城市',
634 | 'city_tier3': '三线城市',
635 | 'city_tier4': '四线城市',
636 | 'city_county': '县城',
637 | 'city_town': '乡镇',
638 | 'hometown': '是否在家乡工作',
639 | 'not_hometown': '不在家乡',
640 | 'is_hometown': '在家乡',
641 | 'leadership': '领导/老板',
642 | 'leader_bad': '对我不爽',
643 | 'leader_strict': '管理严格',
644 | 'leader_normal': '中规中矩',
645 | 'leader_good': '善解人意',
646 | 'leader_favorite': '我是嫡系',
647 | 'teamwork': '同事环境',
648 | 'team_bad': '都是傻逼',
649 | 'team_normal': '萍水相逢',
650 | 'team_good': '和和睦睦',
651 | 'team_excellent': '私交甚好',
652 | 'shuttle': '班车服务(加分项)',
653 | 'shuttle_none': '无法抵达',
654 | 'shuttle_inconvenient': '班车不便',
655 | 'shuttle_convenient': '便利班车',
656 | 'shuttle_direct': '班车直达',
657 | 'canteen': '食堂情况(加分项)',
658 | 'canteen_none': '很难吃',
659 | 'canteen_average': '食堂一般',
660 | 'canteen_good': '食堂不错',
661 | 'canteen_excellent': '食堂超赞',
662 |
663 | // 教育和工作经验
664 | 'education_level': '个人学历水平',
665 | 'degree_type': '学位类型',
666 | 'below_bachelor': '专科及以下',
667 | 'bachelor': '本科',
668 | 'masters': '硕士',
669 | 'phd': '博士',
670 | 'school_type': '学校类型',
671 | 'school_second_tier': '二本三本',
672 | 'school_first_tier_bachelor': '双非/ QS200/ USnews80',
673 | 'school_elite_bachelor': '985211/ QS50/ USnews30',
674 | 'school_first_tier_higher': '双非/ QS100/ USnews50',
675 | 'school_elite_higher': '985211/ QS30/ USnews20',
676 | 'bachelor_background': '本科背景',
677 | 'work_years': '工作年限',
678 | 'fresh_graduate': '应届生',
679 | 'years_1_3': '1-3年',
680 | 'years_3_5': '3-5年',
681 | 'years_5_8': '5-8年',
682 | 'years_8_10': '8-10年',
683 | 'years_10_12': '10-12年',
684 | 'years_above_12': '12年以上',
685 |
686 | // 结果
687 | 'working_days_per_year': '年工作天数',
688 | 'days_unit': '天',
689 | 'average_daily_salary': '平均日薪',
690 | 'job_value': '工作性价比',
691 | 'view_report': '查看我的工作性价比报告',
692 |
693 | // ShareCard组件
694 | 'share_back_to_calculator': '返回计算器',
695 | 'share_your_job_worth_report': '你的工作性价比报告',
696 | 'share_job_worth_report': '工作性价比报告',
697 | 'share_custom_made': '由"这b班上得值不值·测算版"精心定制',
698 | 'share_generating': '生成中...',
699 | 'share_download_report': '下载报告',
700 | 'share_basic_info': '基础信息',
701 | 'share_work_city': '工作城市',
702 | 'share_is_hometown': '是否家乡',
703 | 'share_yes': '是',
704 | 'share_no': '否',
705 | 'share_daily_salary': '日薪',
706 | 'share_day': '天',
707 | 'share_days': '天',
708 | 'share_work_hours_title': '工作时间',
709 | 'share_hours': '小时',
710 | 'share_daily_work_hours': '每天工作',
711 | 'share_daily_commute_hours': '每天通勤',
712 | 'share_rest_time': '午休与休息',
713 | 'share_weekly_work_days': '每周工作天数',
714 | 'share_remote_work': '远程办公',
715 | 'share_days_per_week': '天/周',
716 | 'share_shuttle_service': '班车服务',
717 | 'share_annual_leave': '年假',
718 | 'share_paid_sick_leave': '带薪病假',
719 | 'share_days_per_year': '天/年',
720 | 'share_work_environment_title': '工作环境',
721 | 'share_office_environment': '办公环境',
722 | 'share_leadership_relation': '领导关系',
723 | 'share_colleague_relationship': '同事关系',
724 | 'share_canteen_quality': '食堂情况',
725 | 'share_education_and_experience': '教育与工作经验',
726 | 'share_highest_degree': '最高学历',
727 | 'share_school_type_label': '学校类型',
728 | 'share_work_years_label': '工作年限',
729 | 'share_contract_type_label': '(稳定度)',
730 | 'share_final_assessment': '最终评估',
731 | 'share_low_value_assessment_1': '这份工作对你来说简直是一场噩梦,每一天都是艰难的挑战。',
732 | 'share_low_value_assessment_2': '这份工作让你疲惫不堪,但或许是通往更好未来的必经之路。',
733 | 'share_medium_value_assessment_1': '这份工作平平淡淡,既没有太多惊喜,也没有太多失望。',
734 | 'share_medium_value_assessment_2': '这份工作给你带来了不少成就感,是一份令人满意的选择。',
735 | 'share_high_value_assessment_1': '这份工作几乎满足了你的所有期望,每天都充满干劲。',
736 | 'share_high_value_assessment_2': '这份工作简直是为你量身定做的,既有挑战又有回报,令你心满意足。',
737 | 'share_high_value_assessment_3': '恭喜你找到了人生中的理想工作,这样的机会可遇而不可求!',
738 | 'share_working_days_per_year': '年工作天数',
739 |
740 | 'share_hometown_comment': '在家乡工作,让你既能追求事业,又能照顾家人,平衡感满满。家的温暖和熟悉的环境给你带来额外的安全感和幸福感。',
741 | 'share_not_hometown_comment': '要照顾好自己,按时吃饭休息,你一个人去得那么远。',
742 | 'share_tier1andnewtier1_city_comment': '虽然生活成本较高,但丰富的机会和广阔的平台能够助你更快成长。',
743 | 'share_tier2and3_city_comment': '生活节奏虽然没有一线城市那么快,但依然提供了不错的发展空间。这里的生活压力适中,让你能找到工作与生活之间的平衡。',
744 | 'share_tier4andbelow_city_comment': '你享受着低成本高质量的生活。虽然机会相对较少,但悠闲的生活节奏和较低的压力让你能更从容地面对人生。',
745 |
746 | 'share_commute_short': '你的通勤时间很短,让你每天都能多出宝贵的时间用于自我提升或休息。',
747 | 'share_commute_medium': '你的通勤时间适中,不会让你感到太大压力,也可以利用这段时间听书或补觉。',
748 | 'share_commute_long': '你长时间的通勤占用了大量宝贵时间,会对身心健康造成一定影响,建议考虑搬家或换工作以改善。',
749 | 'share_wfh_high': '而且你有大量居家办公的机会,进一步减轻了通勤负担,提高了工作生活质量。',
750 | 'share_wfh_medium': '你的部分居家办公安排也为你节省了不少通勤时间。',
751 | 'share_shuttle_service_good': '公司提供的便利班车服务是一个不小的福利,让你的通勤更轻松愉快。',
752 |
753 | 'share_cbd_environment': '在CBD的办公环境既专业又现代化,提供了良好的职业形象和便利的工作条件。',
754 | 'share_factory_environment': '在工厂/户外环境工作确实有些挑战,但也培养了你的坚韧品质和适应能力。',
755 | 'share_normal_environment': '你的工作环境舒适适中,能满足基本需求,为高效工作提供了足够的保障。',
756 | 'share_leadership_excellent': '你享受着作为嫡系的优越待遇和发展机会,但也面临着更高的期望和责任。',
757 | 'share_leadership_good': '你的领导能够理解你的工作状态并提供必要的支持,这在职场中非常难得。',
758 | 'share_leadership_normal': '你和领导各司其职,这种关系虽然普通但稳定可靠。',
759 | 'share_leadership_strict': '你领导的管理风格较为严格,这种严格虽然有时让人压力大,但也能促使你更加专业和自律。',
760 | 'share_leadership_bad': '你与领导之间的关系有些紧张,这种情况下要学会保持情绪稳定,专注于工作本身,同时提升自己的沟通技巧。',
761 | 'share_teamwork_excellent': '你与同事们建立了深厚的私人友谊,工作之余还能互相支持和陪伴,这种关系让职场生活更加充实和有意义。',
762 | 'share_teamwork_good': '团队氛围和谐友善,同事之间相互尊重和支持,这种积极的人际环境让工作过程更加愉快和高效。',
763 | 'share_teamwork_normal': '与同事们相处和平但不过分亲近,这种关系模式适合专注于工作的职场人士。',
764 | 'share_teamwork_bad': '同事关系略显紧张,这种环境虽然不太舒适,但也锻炼了你的独立工作能力和心理承受力。',
765 |
766 | 'share_workhours_balanced': '你的工作强度适中,有足够的时间照顾个人生活,保持着良好的工作生活平衡。',
767 | 'share_workhours_long': '你的工作时间略长,但仍在可接受范围内。注意合理安排休息时间,避免长期疲劳。',
768 | 'share_workhours_excessive': '你的工作时间过长,长期如此可能影响健康和生活质量。建议寻找方法提高效率或与上级商量调整工作安排。',
769 | 'share_rest_adequate': '你有充足的休息和午休时间,这有助于恢复精力,提高下午的工作效率。',
770 | 'share_rest_insufficient': '你的休息时间较少,记得定期起身活动,防止久坐带来的健康问题。',
771 | 'share_leave_abundant': '丰富的年假让你有充分的时间休整和旅行,这对维持长期工作动力非常重要。',
772 | 'share_leave_limited': '你的年假较少,可以考虑更有效地规划和利用这些宝贵的休假时间。',
773 |
774 | 'share_phd_comment': '博士学历是你职场的一张重要名片,为你打开了许多高端研究和专业岗位的大门。',
775 | 'share_masters_comment': '硕士学历在当今就业市场仍有一定优势,证明了你的学习能力和专业素养。',
776 | 'share_bachelor_comment': '本科学历为你的职业生涯奠定了坚实基础,结合实际经验,你能在各个领域找到发展机会。',
777 | 'share_below_bachelor_comment': '专科及以下学历虽然在某些领域可能面临挑战,但实践经验和专业技能同样能帮你赢得认可。',
778 | 'share_fresh_graduate_comment': '作为应届生,你充满朝气和学习热情,有无限的可能性去探索和成长。',
779 | 'share_experienced_comment': '多年的工作经验是你最宝贵的财富,让你在职场中更加从容和自信。',
780 | 'share_mid_career_comment': '几年的工作经验让你更加了解行业和自己的优势,职业发展正处于上升期。',
781 | 'share_government_job_comment': '体制内的工作稳定性高,让你无需过多担忧失业风险,可以更从容地规划未来。',
782 | 'share_private_job_comment': '私企的工作虽然有一定风险,但也提供了更多成长和收入提升的机会。',
783 | 'share_dispatch_job_comment': '劳务派遣的工作灵活度高,但稳定性较低。在享受短期便利的同时,应积极规划长远职业发展路径。',
784 | 'share_freelance_job_comment': '自由职业提供了极高的灵活性和自主性,但也伴随着收入不稳定和福利缺失的风险。需要具备较强的自我管理和市场开拓能力。',
785 |
786 | 'share_salary_high_cny': '你的日薪处于较高水平,财务状况良好,能够满足日常生活和一定的休闲娱乐需求。',
787 | 'share_salary_medium_cny': '你的日薪处于中等水平,足以应对基本生活需求,但可能需要更细致的预算规划。',
788 | 'share_salary_low_cny': '你的日薪较低,可能需要精打细算来管理财务,同时寻找提升收入的机会。',
789 | 'share_salary_high_foreign': '你的日薪处于较高水平,财务状况良好,能够满足日常生活和一定的休闲娱乐需求。',
790 | 'share_salary_medium_foreign': '你的日薪处于中等水平,足以应对基本生活需求,但可能需要更细致的预算规划。',
791 | 'share_salary_low_foreign': '你的日薪较低,可能需要精打细算来管理财务,同时寻找提升收入的机会。',
792 | 'share_high_cost_city': '在高生活成本的城市,你的薪资需要更精明地管理才能达到理想的生活质量。',
793 | 'share_low_cost_city': '在低生活成本的地区,你的薪资能够带来更高的生活质量和更多的储蓄机会。',
794 |
795 | 'share_value_low': '虽然目前的工作性价比较低,但这可能是积累经验的必经阶段。记住每份工作都有其价值,努力汲取经验,为下一步发展打好基础。',
796 | 'share_value_medium': '你的工作性价比处于中等水平,有优点也有不足。可以专注于现有优势,同时寻找提升不足方面的方法,让工作体验更加全面。',
797 | 'share_value_high': '恭喜你拥有高性价比的工作!这样的机会难得,要珍惜现在的环境,继续发挥自己的优势,享受工作带来的成就感和满足感。',
798 | 'share_summary_advice': '综合建议',
799 |
800 | // 评价
801 | 'rating_enter_salary': '请输入年薪',
802 | 'rating_terrible': '惨绝人寰',
803 | 'rating_poor': '略惨',
804 | 'rating_average': '一般',
805 | 'rating_good': '还不错',
806 | 'rating_great': '很爽',
807 | 'rating_excellent': '爽到爆炸',
808 | 'rating_perfect': '人生巅峰',
809 | 'share_country': '工作国家/地区',
810 | },
811 | en: {
812 | // Title and navigation
813 | 'title': 'Is My Job Worth the Grind?',
814 | 'github': 'GitHub',
815 | 'email': 'Email',
816 | 'xiaohongshu': 'Rednote',
817 | 'redirect_notice': 'Automatically redirected, no VPN needed',
818 | 'visits': 'Visits',
819 | 'visitors': 'Visitors',
820 | 'star_request': 'If you find this tool helpful, please give it a ⭐Star!',
821 | 'history': 'History',
822 | 'no_history': 'No history records',
823 | 'history_notice': 'Records will be saved after viewing reports',
824 | 'delete_history': 'Delete',
825 | 'clear_all': 'Clear All',
826 | 'restore_history': 'Restore this record',
827 |
828 | // Form labels
829 | 'annual_salary_cny': 'Annual Salary (CNY)',
830 | 'annual_salary_foreign': 'Annual Salary (Local Currency)',
831 | 'annual_salary': 'Annual Salary',
832 | 'salary_placeholder_cny': 'Pre-tax annual salary',
833 | 'salary_placeholder_foreign': 'Use local currency',
834 | 'salary_placeholder': 'Pre-tax annual salary',
835 | 'non_china_salary': 'Non-China Salary',
836 | 'ppp_factor': 'Purchasing Power Parity Factor',
837 | 'ppp_tooltip': 'PPP factor standardizes purchasing power across countries. For example, China\'s 4.19 means that 1 USD in US has the same purchasing power as 4.19 CNY in China.',
838 | 'ppp_placeholder': 'Enter PPP conversion factor',
839 | 'ppp_common_regions': 'Common regions: China:4.19, Japan:102.59, US:1.00, Singapore:0.84',
840 | 'view_more': 'View more',
841 | 'country_selection': 'Work Country/Region',
842 | 'selected_ppp': 'Current PPP value',
843 | 'work_days_per_week': 'Workdays/week',
844 | 'wfh_days_per_week': 'WFH days/week',
845 | 'wfh_tooltip': 'WFH means Work From Home. Enter how many of your weekly workdays are spent working from home.',
846 | 'annual_leave': 'Annual leave /d',
847 | 'public_holidays': 'Public holidays',
848 | 'paid_sick_leave': 'Paid sick days',
849 | 'work_hours': 'Work hours /h',
850 | 'work_hours_tooltip': 'Work hours: total time from start to end of workday, including meals, breaks, and overtime (excluding commute).',
851 | 'commute_hours': 'Commute hours',
852 | 'commute_tooltip': 'Commute time refers to the total round-trip time between home and office.',
853 | 'rest_time': 'Breaks & slacking/h',
854 |
855 | // Environment factors
856 | 'job_stability': 'Job Stability',
857 | 'job_government': 'Government Position',
858 | 'job_state': 'State-owned/Large Corp',
859 | 'job_foreign': 'Foreign Company',
860 | 'job_private': 'Private Company',
861 | 'job_dispatch': 'Temp Agency Worker',
862 | 'job_freelance': 'Freelancer',
863 | 'work_environment': 'Work Environment',
864 | 'env_remote': 'Remote Factory/ Site/ Outdoor',
865 | 'env_factory': 'Factory/ Site/ Outdoor',
866 | 'env_normal': 'Standard Office',
867 | 'env_cbd': 'CBD Office',
868 | 'city_factor': 'City (by Cost of Living)',
869 | 'city_tier1': 'Major Metropolis',
870 | 'city_newtier1': 'Emerging Major City',
871 | 'city_tier2': 'Regional Center',
872 | 'city_tier3': 'Medium-sized City',
873 | 'city_tier4': 'Small City',
874 | 'city_county': 'County',
875 | 'city_town': 'Township',
876 | 'hometown': 'Working in Hometown?',
877 | 'not_hometown': 'Not Hometown',
878 | 'is_hometown': 'In Hometown',
879 | 'leadership': 'Manager/Boss',
880 | 'leader_bad': 'Difficult Relationship',
881 | 'leader_strict': 'Strict Management',
882 | 'leader_normal': 'Professional & Neutral',
883 | 'leader_good': 'Supportive Leadership',
884 | 'leader_favorite': 'Inner Circle Member',
885 | 'teamwork': 'Team Environment',
886 | 'team_bad': 'Toxic Environment',
887 | 'team_normal': 'Professional Colleagues',
888 | 'team_good': 'Collaborative Team',
889 | 'team_excellent': 'Close-knit Team',
890 | 'shuttle': 'Shuttle Service (Bonus)',
891 | 'shuttle_none': 'Inaccessible',
892 | 'shuttle_inconvenient': 'Inconvenient Shuttle',
893 | 'shuttle_convenient': 'Convenient Shuttle',
894 | 'shuttle_direct': 'Direct Route Shuttle',
895 | 'canteen': 'Canteen Quality (Bonus)',
896 | 'canteen_none': 'Terrible Food',
897 | 'canteen_average': 'Average Quality',
898 | 'canteen_good': 'Good Quality',
899 | 'canteen_excellent': 'Excellent Quality',
900 |
901 | // Education and work experience
902 | 'education_level': 'Education Level',
903 | 'degree_type': 'Degree Type',
904 | 'below_bachelor': 'Below Bachelor\'s',
905 | 'bachelor': 'Bachelor\'s',
906 | 'masters': 'Master\'s',
907 | 'phd': 'PhD',
908 | 'school_type': 'University Type',
909 | 'school_second_tier': 'Standard University',
910 | 'school_first_tier_bachelor': 'Top University/QS200/USnews80',
911 | 'school_elite_bachelor': 'Elite University/QS50/USnews30',
912 | 'school_first_tier_higher': 'Top Grad School/QS100/USnews50',
913 | 'school_elite_higher': 'Elite Grad School/QS30/USnews20',
914 | 'bachelor_background': 'Bachelor Background',
915 | 'work_years': 'Work Experience',
916 | 'fresh_graduate': 'Fresh Graduate',
917 | 'years_1_3': '1-3 years',
918 | 'years_3_5': '3-5 years',
919 | 'years_5_8': '5-8 years',
920 | 'years_8_10': '8-10 years',
921 | 'years_10_12': '10-12 years',
922 | 'years_above_12': '12+ years',
923 |
924 | // Results
925 | 'working_days_per_year': 'Working days/year',
926 | 'days_unit': 'days',
927 | 'average_daily_salary': 'Average daily salary',
928 | 'job_value': 'Job Value Rating',
929 | 'view_report': 'View my job worth report',
930 |
931 | // ShareCard Component
932 | 'share_back_to_calculator': 'Back to Calculator',
933 | 'share_your_job_worth_report': 'Your Job Worth Report',
934 | 'share_job_worth_report': 'Job Worth Report',
935 | 'share_custom_made': 'Custom made by "Is My Job Worth It?"',
936 | 'share_generating': 'Generating...',
937 | 'share_download_report': 'Download Report',
938 | 'share_basic_info': 'Basic Information',
939 | 'share_work_city': 'Work City',
940 | 'share_is_hometown': 'Hometown',
941 | 'share_yes': 'Yes',
942 | 'share_no': 'No',
943 | 'share_daily_salary': 'Daily Salary',
944 | 'share_day': 'day',
945 | 'share_days': 'days',
946 | 'share_work_hours_title': 'Work Hours',
947 | 'share_hours': 'hours',
948 | 'share_daily_work_hours': 'Daily Work',
949 | 'share_daily_commute_hours': 'Daily Commute',
950 | 'share_rest_time': 'Breaks & Rest',
951 | 'share_weekly_work_days': 'Weekly Workdays',
952 | 'share_remote_work': 'Remote Work',
953 | 'share_days_per_week': 'days/week',
954 | 'share_shuttle_service': 'Shuttle Service',
955 | 'share_annual_leave': 'Annual Leave',
956 | 'share_paid_sick_leave': 'Paid Sick Leave',
957 | 'share_days_per_year': 'days/year',
958 | 'share_work_environment_title': 'Work Environment',
959 | 'share_office_environment': 'Office Environment',
960 | 'share_leadership_relation': 'Leadership',
961 | 'share_colleague_relationship': 'Colleague Relations',
962 | 'share_canteen_quality': 'Canteen',
963 | 'share_education_and_experience': 'Education & Experience',
964 | 'share_highest_degree': 'Highest Degree',
965 | 'share_school_type_label': 'School Type',
966 | 'share_work_years_label': 'Work Years',
967 | 'share_contract_type_label': 'Contract Type',
968 | 'share_final_assessment': 'Final Assessment',
969 | 'share_low_value_assessment_1': 'This job feels like a daily struggle, with challenges that outweigh the benefits.',
970 | 'share_low_value_assessment_2': 'This job is quite demanding, but might be a stepping stone to better opportunities.',
971 | 'share_medium_value_assessment_1': 'This job is balanced - not particularly exciting but stable and manageable.',
972 | 'share_medium_value_assessment_2': 'This job offers good satisfaction and rewards that match your efforts.',
973 | 'share_high_value_assessment_1': 'This job meets most of your expectations, providing meaningful work and fair compensation.',
974 | 'share_high_value_assessment_2': 'This job seems tailor-made for you, offering both challenge and reward in perfect measure.',
975 | 'share_high_value_assessment_3': "You've found an exceptional job opportunity that offers extraordinary value and satisfaction!",
976 | 'share_working_days_per_year': 'Working days/year',
977 |
978 | 'share_hometown_comment': 'Working in your hometown allows you to build your career while maintaining family connections - a valuable balance that contributes to overall life satisfaction.',
979 | 'share_not_hometown_comment': 'To take care of yourself, eat and rest on time, you\'re so far away by yourself.',
980 | 'share_tier1andnewtier1_city_comment': 'While living costs are high in major metropolitan areas, the abundant opportunities and professional networks can accelerate your career growth.',
981 | 'share_tier2and3_city_comment': 'These regional centers offer a good balance - decent career opportunities with more manageable living costs and work pressure compared to major cities.',
982 | 'share_tier4andbelow_city_comment': 'You enjoy a good quality of life with lower living costs. While career opportunities may be more limited, the relaxed pace and lower pressure are significant advantages.',
983 |
984 | 'share_commute_short': 'Your short commute time gives you more precious time each day for personal development or relaxation.',
985 | 'share_commute_medium': 'Your moderate commute is manageable and can be used productively for reading or podcasts.',
986 | 'share_commute_long': 'Your lengthy commute consumes significant time that affects work-life balance. Consider relocation or negotiating flexible arrangements if possible.',
987 | 'share_wfh_high': 'Your substantial work-from-home arrangement significantly reduces commuting burden and improves quality of life.',
988 | 'share_wfh_medium': 'Your partial work-from-home arrangement helps save valuable commuting time.',
989 | 'share_shuttle_service_good': 'The company shuttle service is a valuable benefit that makes your commute more comfortable and stress-free.',
990 |
991 | 'share_cbd_environment': 'The CBD office environment offers professional surroundings and convenient access to business services and networking opportunities.',
992 | 'share_factory_environment': 'Working in an industrial or outdoor setting presents unique challenges but also develops resilience and practical problem-solving skills.',
993 | 'share_normal_environment': 'Your work environment provides the standard amenities needed for productive work without unnecessary distractions.',
994 | 'share_leadership_excellent': "Being part of leadership's inner circle offers advantages in terms of opportunities and influence, though it comes with higher expectations.",
995 | 'share_leadership_good': 'Your supportive leadership recognizes your contributions and provides the guidance needed for success - a valuable workplace asset.',
996 | 'share_leadership_normal': 'Your professional relationship with leadership is straightforward and functional - clear expectations without unnecessary complications.',
997 | 'share_leadership_strict': 'Working under strict management can be challenging but often develops discipline and attention to detail that serve you well professionally.',
998 | 'share_leadership_bad': 'The tension with leadership creates challenges, requiring careful communication and focusing on deliverables rather than relationships.',
999 | 'share_teamwork_excellent': 'The strong personal connections with colleagues creates a supportive network that enhances both work satisfaction and effectiveness.',
1000 | 'share_teamwork_good': 'Your collaborative team environment fosters mutual support and effective communication, making daily work more pleasant and productive.',
1001 | 'share_teamwork_normal': 'Maintaining professional but not overly personal relationships with colleagues allows you to focus on your work while having adequate support.',
1002 | 'share_teamwork_bad': 'The challenging team dynamics require adaptability and self-reliance, which can develop valuable independence and resilience.',
1003 |
1004 | 'share_workhours_balanced': 'Your balanced work schedule allows sufficient time for personal life, contributing to sustainable long-term performance.',
1005 | 'share_workhours_long': 'Your extended work hours are manageable but require attention to maintaining energy and preventing burnout.',
1006 | 'share_workhours_excessive': 'Your work hours are unsustainably long and may impact wellbeing and performance over time. Consider discussing workload adjustments.',
1007 | 'share_rest_adequate': 'Your generous break time helps maintain energy levels and productivity throughout the workday.',
1008 | 'share_rest_insufficient': 'Your limited break time suggests a need for incorporating short movement breaks to maintain health and focus.',
1009 | 'share_leave_abundant': 'Your generous leave allowance provides ample time for rejuvenation and personal pursuits - essential for sustained motivation.',
1010 | 'share_leave_limited': 'With limited leave time, strategic planning of your days off becomes important for maximizing their restorative benefit.',
1011 |
1012 | 'share_phd_comment': 'Your doctoral qualification opens doors to specialized positions and demonstrates advanced research and analytical capabilities.',
1013 | 'share_masters_comment': "Your master's degree demonstrates advanced knowledge and commitment that remains valuable in today's competitive job market.",
1014 | 'share_bachelor_comment': "Your bachelor's degree provides a solid foundation that, combined with practical experience, enables diverse career opportunities.",
1015 | 'share_below_bachelor_comment': "While formal education below bachelor's level may present challenges in some fields, practical skills and experience can be equally valuable assets.",
1016 | 'share_fresh_graduate_comment': 'As a recent graduate, your fresh perspective and current knowledge are assets, balanced by the unlimited potential for growth and learning.',
1017 | 'share_experienced_comment': 'Your substantial work experience provides valuable context and judgment that enhance your effectiveness and confidence.',
1018 | 'share_mid_career_comment': 'With several years of experience, you understand both your industry and personal strengths, positioning you for strategic career development.',
1019 | 'share_government_job_comment': 'The stability of public sector employment reduces career uncertainty, allowing more confident long-term planning.',
1020 | 'share_private_job_comment': 'While private sector employment carries some uncertainty, it often provides accelerated growth and compensation opportunities.',
1021 | 'share_dispatch_job_comment': 'Temp agency work offers flexibility but less stability. While enjoying short-term convenience, actively planning your long-term career path is essential.',
1022 | 'share_freelance_job_comment': 'Freelancing offers exceptional flexibility and autonomy, but comes with income instability and lack of benefits. Strong self-management and marketing skills are essential.',
1023 |
1024 | 'share_salary_high_cny': 'Your daily compensation is competitive, providing financial security and flexibility for both necessities and discretionary spending.',
1025 | 'share_salary_medium_cny': 'Your compensation meets basic needs comfortably but requires thoughtful budgeting for optimal financial health.',
1026 | 'share_salary_low_cny': 'Your current compensation level necessitates careful financial management while you explore opportunities for income growth.',
1027 | 'share_salary_high_foreign': 'Your daily compensation is competitive, providing financial security and flexibility for both necessities and discretionary spending.',
1028 | 'share_salary_medium_foreign': 'Your compensation meets basic needs comfortably but requires thoughtful budgeting for optimal financial health.',
1029 | 'share_salary_low_foreign': 'Your current compensation level necessitates careful financial management while you explore opportunities for income growth.',
1030 | 'share_high_cost_city': 'In a high-cost location, careful financial planning helps maximize the value of your compensation.',
1031 | 'share_low_cost_city': 'In a lower-cost area, your compensation provides enhanced purchasing power and potential for savings.',
1032 |
1033 | 'share_value_low': 'While the current position shows limited value, it may provide necessary experience for future growth. Extract learning from every aspect while preparing for your next career move.',
1034 | 'share_value_medium': 'Your job offers balanced value with both strengths and areas for improvement. Focus on leveraging the positive aspects while developing strategies to address the challenges.',
1035 | 'share_value_high': "You've found a high-value position worth maintaining and developing. Continue building on your strengths and appreciate the satisfaction this role provides.",
1036 | 'share_summary_advice': 'Overall Recommendation',
1037 |
1038 | // Ratings
1039 | 'rating_enter_salary': 'Please enter salary',
1040 | 'rating_terrible': 'Dismal',
1041 | 'rating_poor': 'Poor',
1042 | 'rating_average': 'Average',
1043 | 'rating_good': 'Good',
1044 | 'rating_great': 'Great',
1045 | 'rating_excellent': 'Excellent',
1046 | 'rating_perfect': 'Outstanding',
1047 | 'share_country': 'Work Country/Region',
1048 | },
1049 | ja: {
1050 | // タイトルとナビゲーション
1051 | 'title': 'この仕事、正気でやれる?ブラック度診断',
1052 | 'github': 'GitHub',
1053 | 'email': 'Email',
1054 | 'xiaohongshu': '小紅書(RED)',
1055 | 'redirect_notice': '自動的にリダイレクトされました',
1056 | 'visits': 'アクセス数',
1057 | 'visitors': '訪問者数',
1058 | 'star_request': '役に立ったら⭐スターを付けてください!',
1059 | 'history': '履歴',
1060 | 'no_history': '履歴がありません',
1061 | 'history_notice': 'レポートを見た後、自動的に保存されます',
1062 | 'delete_history': '削除',
1063 | 'clear_all': 'すべて削除',
1064 | 'restore_history': 'この記録を復元',
1065 |
1066 | // フォームラベル
1067 | 'annual_salary_cny': '年収(元)',
1068 | 'annual_salary_foreign': '年収(現地通貨)',
1069 | 'annual_salary': '年収',
1070 | 'salary_placeholder_cny': '税引前の年収',
1071 | 'salary_placeholder_foreign': '現地通貨で入力',
1072 | 'salary_placeholder': '額面年収',
1073 | 'non_china_salary': '中国以外の給与',
1074 | 'ppp_factor': '購買力平価(PPP)換算係数',
1075 | 'ppp_tooltip': 'PPP換算係数は各国の通貨の購買力を標準化する指標です。例えば、中国の4.19は、米国の1ドルが中国の4.19元と同等の購買力を持つことを意味します。',
1076 | 'ppp_placeholder': 'PPP換算係数を入力',
1077 | 'ppp_common_regions': '主な地域:中国:4.19、日本:102.59、米国:1.00、シンガポール:0.84',
1078 | 'view_more': 'もっと見る',
1079 | 'country_selection': '勤務国・地域',
1080 | 'selected_ppp': '現在のPPP値',
1081 | 'work_days_per_week': '週間勤務日数',
1082 | 'wfh_days_per_week': '週間リモートワーク日数',
1083 | 'wfh_tooltip': 'リモートワーク・在宅勤務日数とは、週の勤務日のうち、何日間を自宅で勤務するかを指します。',
1084 | 'annual_leave': '年次有給休暇',
1085 | 'public_holidays': '祝日数',
1086 | 'paid_sick_leave': '有給病休',
1087 | 'work_hours': '一日あたりの勤務時間',
1088 | 'work_hours_tooltip': '勤務時間:始業から終業までの時間(昼食、休憩、残業を含む、通勤時間は除く)',
1089 | 'commute_hours': '通勤時間',
1090 | 'commute_tooltip': '通勤時間とは、自宅と職場との往復に要する時間の合計を指します。',
1091 | 'rest_time': '休憩&サボり時間',
1092 |
1093 | // 環境係数
1094 | 'job_stability': '雇用形態(安定性)',
1095 | 'job_government': '公務員・準公務員',
1096 | 'job_state': '大企業・終身雇用',
1097 | 'job_foreign': '外資系企業',
1098 | 'job_private': '一般企業',
1099 | 'job_dispatch': '派遣社員',
1100 | 'job_freelance': 'フリーランス',
1101 | 'work_environment': '職場環境',
1102 | 'env_remote': '僻地の工場・現場・屋外',
1103 | 'env_factory': '工場・現場・屋外',
1104 | 'env_normal': '一般的なオフィス',
1105 | 'env_cbd': '都心の高級オフィス',
1106 | 'city_factor': '勤務地(生活コストによる)',
1107 | 'city_tier1': '都心3区',
1108 | 'city_newtier1': '都内',
1109 | 'city_tier2': '大阪・横浜・名古屋・福岡市中心部',
1110 | 'city_tier3': '地方都市の県庁所在地',
1111 | 'city_tier4': '地方都市',
1112 | 'city_county': '小都市',
1113 | 'city_town': '田舎',
1114 | 'hometown': '地元で働いていますか',
1115 | 'not_hometown': '地元ではない',
1116 | 'is_hometown': '地元である',
1117 | 'leadership': '上司・経営者との関係',
1118 | 'leader_bad': '嫌われている',
1119 | 'leader_strict': '厳しい管理される',
1120 | 'leader_normal': '普通の関係',
1121 | 'leader_good': '理解し合えている',
1122 | 'leader_favorite': '気に入られている',
1123 | 'teamwork': '職場の人間関係',
1124 | 'team_bad': '最悪な環境',
1125 | 'team_normal': '事務的な関係',
1126 | 'team_good': '協力的な環境',
1127 | 'team_excellent': '仲の良い職場',
1128 | 'shuttle': '送迎バス(加点項目)',
1129 | 'shuttle_none': 'アクセス不便',
1130 | 'shuttle_inconvenient': '不便なバス',
1131 | 'shuttle_convenient': '便利なバス',
1132 | 'shuttle_direct': '直行便あり',
1133 | 'canteen': '社員食堂(加点項目)',
1134 | 'canteen_none': '非常にまずい',
1135 | 'canteen_average': '普通',
1136 | 'canteen_good': '美味しい',
1137 | 'canteen_excellent': '非常に美味しい',
1138 |
1139 | // 教育と経験
1140 | 'education_level': '学歴',
1141 | 'degree_type': '学位タイプ',
1142 | 'below_bachelor': '短大・専門学校以下',
1143 | 'bachelor': '学士(大学卒)',
1144 | 'masters': '修士',
1145 | 'phd': '博士',
1146 | 'school_type': '大学タイプ',
1147 | 'school_second_tier': '一般大学',
1148 | 'school_first_tier_bachelor': 'MARCH・関関同立・QS200位',
1149 | 'school_elite_bachelor': '東大・京大・早慶・QS50位',
1150 | 'school_first_tier_higher': '国公立大学院・QS100位',
1151 | 'school_elite_higher': '東大・京大大学院・QS30位',
1152 | 'bachelor_background': '学部背景',
1153 | 'work_years': '勤務年数',
1154 | 'fresh_graduate': '新卒',
1155 | 'years_1_3': '1-3年',
1156 | 'years_3_5': '3-5年',
1157 | 'years_5_8': '5-8年',
1158 | 'years_8_10': '8-10年',
1159 | 'years_10_12': '10-12年',
1160 | 'years_above_12': '12年以上',
1161 |
1162 | // 結果
1163 | 'working_days_per_year': '年間勤務日数',
1164 | 'days_unit': '日',
1165 | 'average_daily_salary': '1日あたりの給与',
1166 | 'job_value': '仕事の価値',
1167 | 'view_report': '診断レポートを見る',
1168 |
1169 | // ShareCardコンポーネント
1170 | 'share_back_to_calculator': '計算機に戻る',
1171 | 'share_your_job_worth_report': 'あなたの仕事価値レポート',
1172 | 'share_job_worth_report': '仕事価値レポート',
1173 | 'share_custom_made': '「この仕事、正気でやれる?ブラック度診断」による分析',
1174 | 'share_generating': '生成中...',
1175 | 'share_download_report': 'レポートをダウンロード',
1176 | 'share_basic_info': '基本情報',
1177 | 'share_work_city': '勤務地',
1178 | 'share_is_hometown': '地元',
1179 | 'share_yes': 'はい',
1180 | 'share_no': 'いいえ',
1181 | 'share_daily_salary': '日給',
1182 | 'share_day': '日',
1183 | 'share_days': '日',
1184 | 'share_work_hours_title': '勤務時間',
1185 | 'share_hours': '時間',
1186 | 'share_daily_work_hours': '1日の勤務時間',
1187 | 'share_daily_commute_hours': '1日の通勤時間',
1188 | 'share_rest_time': '休憩時間',
1189 | 'share_weekly_work_days': '週の勤務日数',
1190 | 'share_remote_work': 'リモートワーク',
1191 | 'share_days_per_week': '日/週',
1192 | 'share_shuttle_service': '送迎バス',
1193 | 'share_annual_leave': '年次有給休暇',
1194 | 'share_paid_sick_leave': '有給病気休暇',
1195 | 'share_days_per_year': '日/年',
1196 | 'share_work_environment_title': '職場環境',
1197 | 'share_office_environment': 'オフィス環境',
1198 | 'share_leadership_relation': '上司との関係',
1199 | 'share_colleague_relationship': '同僚との関係',
1200 | 'share_canteen_quality': '社員食堂',
1201 | 'share_education_and_experience': '学歴と経験',
1202 | 'share_highest_degree': '最終学歴',
1203 | 'share_school_type_label': '大学タイプ',
1204 | 'share_work_years_label': '勤務年数',
1205 | 'share_contract_type_label': '雇用形態',
1206 | 'share_final_assessment': '総合評価',
1207 | 'share_low_value_assessment_1': 'この仕事はあなたにとって日々が苦痛で、まさにブラック企業の特徴を持っています。',
1208 | 'share_low_value_assessment_2': 'この仕事は非常に厳しいですが、より良い将来へのステップになるかもしれません。',
1209 | 'share_medium_value_assessment_1': 'この仕事は普通で、特に素晴らしいわけでも悪いわけでもありません。',
1210 | 'share_medium_value_assessment_2': 'この仕事はそこそこ満足感を与えてくれる、悪くない選択です。',
1211 | 'share_high_value_assessment_1': 'この仕事はあなたの期待のほとんどを満たし、やりがいを感じられます。',
1212 | 'share_high_value_assessment_2': 'この仕事はあなたにぴったりで、チャレンジと報酬のバランスが取れています。',
1213 | 'share_high_value_assessment_3': '理想的な仕事を見つけましたね!このような機会は滅多にありません!',
1214 | 'share_working_days_per_year': '年間勤務日数',
1215 |
1216 | 'share_hometown_comment': 'あなたの故郷で働くことで、キャリアを構築しながら家族とのつながりを維持することができます - これは総合的な生活満足度に貢献する貴重なバランスです。',
1217 | 'share_not_hometown_comment': '自分の面倒をしっかり見て、定時に食事と休息を取るように。あなたは一人でとても遠くまで来ました。',
1218 | 'share_tier1andnewtier1_city_comment': '大都市圏の中心部は家賃が非常に高いエリアですが、キャリア機会も多く、プロフェッショナルなネットワーク形成には最適な環境です。',
1219 | 'share_tier2and3_city_comment': '地方の中核都市は、適度な生活コストと仕事の機会のバランスが取れたエリアです。',
1220 | 'share_tier4andbelow_city_comment': '地方都市では家賃など生活コストが低く、ゆとりある生活が送れます。仕事の機会は限られますが、ストレスの少ない環境は魅力的です。',
1221 |
1222 | 'share_commute_short': '通勤時間が短いため、自己啓発やリラックスのための貴重な時間が確保できています。',
1223 | 'share_commute_medium': '適度な通勤時間は負担にならず、オーディオブックや音楽を楽しむ時間として活用できます。',
1224 | 'share_commute_long': '長時間の通勤は貴重な時間を消費し、心身の健康に影響を与える可能性があります。可能であれば引越しや在宅勤務の検討をおすすめします。',
1225 | 'share_wfh_high': 'リモートワークの機会が多いため、通勤の負担が大幅に軽減され、生活の質が向上しています。',
1226 | 'share_wfh_medium': '部分的なリモートワークにより、通勤時間を節約できています。',
1227 | 'share_shuttle_service_good': '会社の送迎バスは価値ある福利厚生で、通勤をより快適にしています。',
1228 |
1229 | 'share_cbd_environment': '都心のオフィス環境は専門的かつ近代的で、ビジネスサービスやネットワーキングの機会へのアクセスが容易です。',
1230 | 'share_factory_environment': '工場や屋外での勤務は独自の課題がありますが、耐久力と実践的な問題解決能力も育てています。',
1231 | 'share_normal_environment': '職場環境は基本的な設備が整っており、生産的に仕事ができる条件が揃っています。',
1232 | 'share_leadership_excellent': "上司の信頼を得ていることで、多くのチャンスと影響力を持つことができますが、同時に高い期待に応える責任も伴います。",
1233 | 'share_leadership_good': '理解のある上司はあなたの貢献を認め、成功に必要な指導を提供してくれます - これは職場での貴重な財産です。',
1234 | 'share_leadership_normal': '上司との関係は明快で機能的であり、余計な複雑さなく明確な期待が示されています。',
1235 | 'share_leadership_strict': '厳格な管理下で働くことは挑戦的ですが、職業人としての規律と細部への注意力を養うことができます。',
1236 | 'share_leadership_bad': '上司との緊張関係は課題をもたらし、慎重なコミュニケーションと人間関係よりも成果物に焦点を当てる必要があります。',
1237 | 'share_teamwork_excellent': '同僚との強い個人的なつながりは、仕事の満足度と効率性を高める支援ネットワークを作り出しています。',
1238 | 'share_teamwork_good': '協力的なチーム環境は相互サポートと効果的なコミュニケーションを促進し、日々の仕事をより快適で生産的にします。',
1239 | 'share_teamwork_normal': '同僚と専門的でありながら過度に個人的ではない関係を維持することで、十分なサポートを受けながら仕事に集中できます。',
1240 | 'share_teamwork_bad': '困難なチームダイナミクスには適応力と自立性が求められ、これが貴重な独立性と回復力を育てることがあります。',
1241 |
1242 | 'share_workhours_balanced': 'バランスの取れた勤務スケジュールは、個人生活のための十分な時間を確保し、長期的な持続可能なパフォーマンスに貢献します。',
1243 | 'share_workhours_long': '長時間の勤務は管理可能ですが、エネルギーを維持し燃え尽き症候群を防ぐための注意が必要です。',
1244 | 'share_workhours_excessive': '持続不可能なほど長い勤務時間は、長期的に健康とパフォーマンスに影響を与える可能性があります。業務量の調整について話し合うことを検討してください。',
1245 | 'share_rest_adequate': '十分な休憩時間は、一日を通してエネルギーレベルと生産性を維持するのに役立ちます。',
1246 | 'share_rest_insufficient': '限られた休憩時間は、健康と集中力を維持するために短い運動休憩を取り入れる必要性を示唆しています。',
1247 | 'share_leave_abundant': '充実した休暇制度は、リフレッシュと個人的な追求のための十分な時間を提供し、持続的なモチベーションに不可欠です。',
1248 | 'share_leave_limited': '限られた休暇時間では、その回復効果を最大化するために休日の戦略的な計画が重要になります。',
1249 |
1250 | 'share_phd_comment': '博士号の資格は専門的なポジションへの道を開き、高度な研究と分析能力を証明します。',
1251 | 'share_masters_comment': "修士号は高度な知識と献身を示し、今日の競争の激しい就職市場で価値ある資格です。",
1252 | 'share_bachelor_comment': "学士号は堅実な基盤を提供し、実践的な経験と組み合わせることで、多様なキャリア機会を可能にします。",
1253 | 'share_below_bachelor_comment': "学士未満の正式な教育は一部の分野で課題をもたらす可能性がありますが、実践的なスキルと経験は同様に価値ある資産となります。",
1254 | 'share_fresh_graduate_comment': '新卒者として、あなたの新鮮な視点と最新の知識は資産であり、成長と学習のための無限の可能性とバランスを取ることができます。',
1255 | 'share_experienced_comment': '豊富な職務経験は、あなたの効果と自信を高める貴重な文脈と判断力を提供します。',
1256 | 'share_mid_career_comment': '数年の経験を持つあなたは、業界と個人の強みの両方を理解し、戦略的なキャリア開発の準備ができています。',
1257 | 'share_government_job_comment': '公共部門の雇用の安定性はキャリアの不確実性を軽減し、より自信を持って長期的な計画を立てることができます。',
1258 | 'share_private_job_comment': '民間部門の雇用にはある程度の不確実性がありますが、多くの場合、加速された成長と報酬の機会を提供します。',
1259 | 'share_dispatch_job_comment': '派遣社員としての働き方は柔軟性がある一方で、雇用の安定性は低めです。短期的な利便性を享受しながらも、長期的なキャリアパスを積極的に計画することが重要です。',
1260 | 'share_freelance_job_comment': 'フリーランスとして働くことで高い自由度と自律性を得られますが、収入の不安定さや福利厚生の欠如というリスクも伴います。自己管理能力とマーケティングスキルが重要になります。',
1261 |
1262 | 'share_salary_high_cny': '日給が競争力があり、必需品と自由裁量の支出の両方に対して財政的安定性と柔軟性を提供します。',
1263 | 'share_salary_medium_cny': '給与は基本的なニーズを快適に満たしていますが、最適な財政状態のために思慮深い予算計画が必要です。',
1264 | 'share_salary_low_cny': '現在の給与水準では、収入増加の機会を探りながら、慎重な財務管理が必要です。',
1265 | 'share_salary_high_foreign': '日給が競争力があり、必需品と自由裁量の支出の両方に対して財政的安定性と柔軟性を提供します。',
1266 | 'share_salary_medium_foreign': '給与は基本的なニーズを快適に満たしていますが、最適な財政状態のために思慮深い予算計画が必要です。',
1267 | 'share_salary_low_foreign': '現在の給与水準では、収入増加の機会を探りながら、慎重な財務管理が必要です。',
1268 | 'share_high_cost_city': '生活費の高い地域では、慎重な財務計画が報酬の価値を最大化するのに役立ちます。',
1269 | 'share_low_cost_city': '生活費の低い地域では、給与がより高い購買力と貯蓄の可能性をもたらします。',
1270 |
1271 | 'share_value_low': '現在の仕事の価値は限られているかもしれませんが、将来の成長に必要な経験を提供するかもしれません。次のキャリアステップの準備をしながら、すべての側面から学びを得ることが大切です。',
1272 | 'share_value_medium': 'あなたの仕事は長所と改善すべき点の両方があるバランスのとれた価値を提供しています。ポジティブな側面を活かしながら、課題に対処するための戦略を立てましょう。',
1273 | 'share_value_high': '維持・発展させる価値のある高価値の職場を見つけました。あなたの強みを引き続き伸ばし、この役割が提供する満足感を大切にしましょう。',
1274 | 'share_summary_advice': '総合的なアドバイス',
1275 |
1276 | // 評価
1277 | 'rating_enter_salary': '給与を入力してください',
1278 | 'rating_terrible': '最悪',
1279 | 'rating_poor': '悪い',
1280 | 'rating_average': '普通',
1281 | 'rating_good': '良い',
1282 | 'rating_great': '素晴らしい',
1283 | 'rating_excellent': '非常に優れている',
1284 | 'rating_perfect': '理想的',
1285 | 'share_country': '勤務国・地域',
1286 | }
1287 | };
1288 |
1289 | // 提供上下文的组件
1290 | export const LanguageProvider: React.FC<{children: ReactNode}> = ({ children }) => {
1291 | // 从本地存储初始化语言,默认为中文
1292 | const [language, setLanguageState] = useState('zh');
1293 |
1294 | // 首次渲染时检查本地存储的语言设置
1295 | useEffect(() => {
1296 | const savedLanguage = localStorage.getItem('language') as Language;
1297 | if (savedLanguage && (savedLanguage === 'zh' || savedLanguage === 'en' || savedLanguage === 'ja')) {
1298 | setLanguageState(savedLanguage);
1299 | }
1300 | }, []);
1301 |
1302 | // 设置语言并保存到本地存储
1303 | const setLanguage = (newLanguage: Language) => {
1304 | setLanguageState(newLanguage);
1305 | localStorage.setItem('language', newLanguage);
1306 | };
1307 |
1308 | // 翻译函数
1309 | const t = (key: string): string => {
1310 | return translations[language][key] || key;
1311 | };
1312 |
1313 | return (
1314 |
1315 | {children}
1316 |
1317 | );
1318 | };
1319 |
1320 | // 使用上下文的钩子
1321 | export const useLanguage = () => useContext(LanguageContext);
--------------------------------------------------------------------------------
/components/LanguageSwitcher.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from 'react';
4 | import { useLanguage } from './LanguageContext';
5 |
6 | export const LanguageSwitcher: React.FC = () => {
7 | const { language, setLanguage } = useLanguage();
8 |
9 | return (
10 |
11 | setLanguage('zh')}
13 | className={`px-1.5 py-0.5 text-xs font-medium rounded-md transition-colors ${
14 | language === 'zh'
15 | ? 'bg-blue-500 text-white dark:bg-blue-600'
16 | : 'bg-gray-100 text-gray-800 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600'
17 | }`}
18 | >
19 | 中文
20 |
21 | setLanguage('en')}
23 | className={`px-1.5 py-0.5 text-xs font-medium rounded-md transition-colors ${
24 | language === 'en'
25 | ? 'bg-blue-500 text-white dark:bg-blue-600'
26 | : 'bg-gray-100 text-gray-800 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600'
27 | }`}
28 | >
29 | English
30 |
31 | setLanguage('ja')}
33 | className={`px-1.5 py-0.5 text-xs font-medium rounded-md transition-colors ${
34 | language === 'ja'
35 | ? 'bg-blue-500 text-white dark:bg-blue-600'
36 | : 'bg-gray-100 text-gray-800 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600'
37 | }`}
38 | >
39 | 日本語
40 |
41 |
42 | );
43 | };
--------------------------------------------------------------------------------
/components/ShareCard.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useRef, useState, useEffect } from 'react';
4 | import { ArrowLeft, Download } from 'lucide-react';
5 | import Link from 'next/link';
6 | import Image from 'next/image';
7 | import { useLanguage } from './LanguageContext';
8 | import { countryNames } from './LanguageContext'; // 导入countryNames对象
9 |
10 | // 扩展接口,支持更多属性
11 | interface ShareCardProps {
12 | // 基础数据
13 | value: string;
14 | assessment: string;
15 | assessmentColor: string;
16 | cityFactor: string;
17 | workHours: string;
18 | commuteHours: string;
19 | restTime: string;
20 | dailySalary: string;
21 | isYuan: string;
22 | workDaysPerYear: string;
23 | countryCode: string;
24 | countryName: string;
25 | currencySymbol: string;
26 |
27 | // 详细工作信息
28 | workDaysPerWeek: string;
29 | wfhDaysPerWeek: string;
30 | annualLeave: string;
31 | paidSickLeave: string;
32 | publicHolidays: string;
33 |
34 | // 工作环境
35 | workEnvironment: string;
36 | leadership: string;
37 | teamwork: string;
38 | homeTown: string;
39 | shuttle: string;
40 | canteen: string;
41 |
42 | // 学历和工作经验
43 | degreeType: string;
44 | schoolType: string;
45 | bachelorType: string;
46 | education: string;
47 | workYears: string;
48 | jobStability: string;
49 |
50 | // 新增属性
51 | hasShuttle: boolean;
52 | hasCanteen: boolean;
53 | }
54 |
55 | // 将中文评级转换为翻译键
56 | const getAssessmentKey = (assessment: string): string => {
57 | // 如果已经是翻译键,直接返回
58 | if (assessment.startsWith('rating_')) {
59 | return assessment;
60 | }
61 |
62 | // 否则,将中文评级转换为翻译键
63 | switch (assessment) {
64 | case '惨绝人寰': return 'rating_terrible';
65 | case '略惨': return 'rating_poor';
66 | case '一般': return 'rating_average';
67 | case '还不错': return 'rating_good';
68 | case '很爽': return 'rating_great';
69 | case '爽到爆炸': return 'rating_excellent';
70 | case '人生巅峰': return 'rating_perfect';
71 | case '请输入年薪': return 'rating_enter_salary';
72 | default: return assessment;
73 | }
74 | };
75 |
76 | // 获取CSS颜色代码
77 | const getColorFromClassName = (className: string): string => {
78 | switch(className) {
79 | case 'text-pink-800': return '#9d174d';
80 | case 'text-red-500': return '#ef4444';
81 | case 'text-orange-500': return '#f97316';
82 | case 'text-blue-500': return '#3b82f6';
83 | case 'text-green-500': return '#22c55e';
84 | case 'text-purple-500': return '#a855f7';
85 | case 'text-yellow-400': return '#facc15';
86 | default: return '#1f2937'; // text-gray-900
87 | }
88 | };
89 |
90 | // 获取城市名称
91 | const getCityName = (cityFactor: string, t: (key: string) => string): string => {
92 | if (cityFactor === '0.70') return t('city_tier1');
93 | else if (cityFactor === '0.80') return t('city_newtier1');
94 | else if (cityFactor === '1.0') return t('city_tier2');
95 | else if (cityFactor === '1.10') return t('city_tier3');
96 | else if (cityFactor === '1.25') return t('city_tier4');
97 | else if (cityFactor === '1.40') return t('city_county');
98 | else if (cityFactor === '1.50') return t('city_town');
99 | return t('city_tier3'); // 默认值
100 | };
101 |
102 | // 获取工作环境描述
103 | const getWorkEnvironmentDesc = (env: string, t: (key: string) => string): string => {
104 | if (env === '0.8') return t('env_remote');
105 | else if (env === '0.9') return t('env_factory');
106 | else if (env === '1.0') return t('env_normal');
107 | else if (env === '1.1') return t('env_cbd');
108 | return t('env_normal');
109 | };
110 |
111 | // 获取领导评价
112 | const getLeadershipDesc = (rating: string, t: (key: string) => string): string => {
113 | if (rating === '0.7') return t('leader_bad');
114 | else if (rating === '0.9') return t('leader_strict');
115 | else if (rating === '1.0') return t('leader_normal');
116 | else if (rating === '1.1') return t('leader_good');
117 | else if (rating === '1.3') return t('leader_favorite');
118 | return t('leader_normal');
119 | };
120 |
121 | // 获取同事环境评价
122 | const getTeamworkDesc = (rating: string, t: (key: string) => string): string => {
123 | if (rating === '0.9') return t('team_bad');
124 | else if (rating === '1.0') return t('team_normal');
125 | else if (rating === '1.1') return t('team_good');
126 | else if (rating === '1.2') return t('team_excellent');
127 | return t('team_normal');
128 | };
129 |
130 | // 获取班车服务描述
131 | const getShuttleDesc = (shuttle: string, t: (key: string) => string): string => {
132 | if (shuttle === '1.0') return t('shuttle_none');
133 | else if (shuttle === '0.9') return t('shuttle_inconvenient');
134 | else if (shuttle === '0.7') return t('shuttle_convenient');
135 | else if (shuttle === '0.5') return t('shuttle_direct');
136 | return t('shuttle_none');
137 | };
138 |
139 | // 获取食堂情况描述
140 | const getCanteenDesc = (canteen: string, t: (key: string) => string): string => {
141 | if (canteen === '1.0') return t('canteen_none');
142 | else if (canteen === '1.05') return t('canteen_average');
143 | else if (canteen === '1.1') return t('canteen_good');
144 | else if (canteen === '1.15') return t('canteen_excellent');
145 | return t('canteen_none');
146 | };
147 |
148 | // 获取合同类型描述
149 | const getJobStabilityDesc = (type: string, t: (key: string) => string): string => {
150 | if (type === 'private') return t('job_private');
151 | else if (type === 'foreign') return t('job_foreign');
152 | else if (type === 'state') return t('job_state');
153 | else if (type === 'government') return t('job_government');
154 | else if (type === 'dispatch') return t('job_dispatch');
155 | else if (type === 'freelance') return t('job_freelance');
156 | return t('job_private');
157 | };
158 |
159 | // 获取学历描述
160 | const getDegreeDesc = (type: string, t: (key: string) => string): string => {
161 | if (type === 'belowBachelor') return t('below_bachelor');
162 | else if (type === 'bachelor') return t('bachelor');
163 | else if (type === 'masters') return t('masters');
164 | else if (type === 'phd') return t('phd');
165 | return t('bachelor');
166 | };
167 |
168 | // 获取学校类型描述
169 | const getSchoolTypeDesc = (type: string, degree: string, t: (key: string) => string): string => {
170 | if (type === 'secondTier') return t('school_second_tier');
171 | else if (type === 'firstTier') {
172 | if (degree === 'bachelor') return t('school_first_tier_bachelor');
173 | return t('school_first_tier_higher');
174 | }
175 | else if (type === 'elite') {
176 | if (degree === 'bachelor') return t('school_elite_bachelor');
177 | return t('school_elite_higher');
178 | }
179 | return t('school_first_tier_bachelor');
180 | };
181 |
182 | // 获取emoji表情
183 | const getEmoji = (value: number): string => {
184 | if (value < 0.6) return '😭';
185 | if (value < 1.0) return '😔';
186 | if (value <= 1.8) return '😐';
187 | if (value <= 2.5) return '😊';
188 | if (value <= 3.2) return '😁';
189 | if (value <= 4.0) return '🤩';
190 | return '🎉';
191 | };
192 |
193 | // 获取工作年限描述
194 | const getWorkYearsDesc = (years: string, t: (key: string) => string): string => {
195 | if (years === '0') return t('fresh_graduate');
196 | else if (years === '1') return t('years_1_3');
197 | else if (years === '2') return t('years_3_5');
198 | else if (years === '4') return t('years_5_8');
199 | else if (years === '6') return t('years_8_10');
200 | else if (years === '10') return t('years_10_12');
201 | else if (years === '15') return t('years_above_12');
202 | return t('fresh_graduate');
203 | };
204 |
205 | // 获取当前语言环境下的国家名称
206 | const getCountryName = (countryCode: string, currentLanguage: string): string => {
207 | if (currentLanguage === 'en') {
208 | return countryNames.en[countryCode] || countryCode || 'Unknown';
209 | }
210 | if (currentLanguage === 'ja') {
211 | return countryNames.ja[countryCode] || countryCode || '不明';
212 | }
213 | return countryNames.zh[countryCode] || countryCode || '未知';
214 | };
215 |
216 | const ShareCard: React.FC = (props) => {
217 | const reportRef = useRef(null);
218 | const simpleReportRef = useRef(null); // 添加简化版报告的引用
219 | const [isDownloading, setIsDownloading] = useState(false);
220 | const [fadeIn, setFadeIn] = useState(false);
221 | const { t, language } = useLanguage();
222 |
223 | // 客户端渲染标志
224 | const [isClient, setIsClient] = useState(false);
225 |
226 | // 确保只在客户端执行
227 | useEffect(() => {
228 | setIsClient(true);
229 | }, []);
230 |
231 | // 页面载入动画效果
232 | useEffect(() => {
233 | // 确保只在客户端执行
234 | if (typeof window !== 'undefined') {
235 | setFadeIn(true);
236 | }
237 | }, []);
238 |
239 | // 生成个性化评价
240 | const personalizedComments = (() => {
241 | const comments = [];
242 | const valueNum = parseFloat(props.value);
243 |
244 | // 1. 根据总体性价比生成主评价
245 | let mainComment = "";
246 | if (valueNum < 0.6) {
247 | mainComment = t('share_low_value_assessment_1');
248 | } else if (valueNum < 1.0) {
249 | mainComment = t('share_low_value_assessment_2');
250 | } else if (valueNum <= 1.8) {
251 | mainComment = t('share_medium_value_assessment_1');
252 | } else if (valueNum <= 2.5) {
253 | mainComment = t('share_medium_value_assessment_2');
254 | } else if (valueNum <= 3.2) {
255 | mainComment = t('share_high_value_assessment_1');
256 | } else if (valueNum <= 4.0) {
257 | mainComment = t('share_high_value_assessment_2');
258 | } else {
259 | mainComment = t('share_high_value_assessment_3');
260 | }
261 | comments.push({
262 | title: t('share_final_assessment'),
263 | content: mainComment,
264 | emoji: getEmoji(valueNum),
265 | details: [
266 | { label: t('share_final_assessment'), value: `${props.value} (${t(getAssessmentKey(props.assessment))})` }
267 | ]
268 | });
269 |
270 | // 2. 工作城市评价
271 | const cityName = getCityName(props.cityFactor, t);
272 | const isHomeTown = props.homeTown === 'yes';
273 | let cityComment = "";
274 |
275 | // 先根据城市等级添加评价
276 | if (props.cityFactor === '0.70' || props.cityFactor === '0.80') {
277 | cityComment = t('share_tier1andnewtier1_city_comment');
278 | } else if (props.cityFactor === '1.0' || props.cityFactor === '1.10') {
279 | cityComment = t('share_tier2and3_city_comment');
280 | } else {
281 | cityComment = t('share_tier4andbelow_city_comment');
282 | }
283 |
284 | // 然后添加家乡相关评价
285 | if (isHomeTown) {
286 | cityComment += " " + t('share_hometown_comment');
287 | } else {
288 | cityComment += " " + t('share_not_hometown_comment');
289 | }
290 |
291 | comments.push({
292 | title: t('share_work_city'),
293 | content: cityComment,
294 | emoji: isHomeTown ? "🏡" : "🌆",
295 | details: [
296 | { label: t('share_work_city'), value: cityName },
297 | { label: t('share_is_hometown'), value: isHomeTown ? t('share_yes') : t('share_no') },
298 | { label: t('share_country'), value: getCountryName(props.countryCode, language) }
299 | ]
300 | });
301 |
302 | // 3. 通勤与WFH评价
303 | const commuteHoursNum = parseFloat(props.commuteHours);
304 | const wfhDaysNum = parseFloat(props.wfhDaysPerWeek);
305 | const workDaysNum = parseFloat(props.workDaysPerWeek);
306 | const wfhRatio = workDaysNum > 0 ? (wfhDaysNum / workDaysNum) : 0;
307 |
308 | let commuteComment = "";
309 |
310 | if (commuteHoursNum <= 1) {
311 | commuteComment = t('share_commute_short');
312 | } else if (commuteHoursNum <= 2) {
313 | commuteComment = t('share_commute_medium');
314 | } else {
315 | commuteComment = t('share_commute_long');
316 | }
317 |
318 | if (wfhRatio >= 0.6) {
319 | commuteComment += " " + t('share_wfh_high');
320 | } else if (wfhRatio >= 0.2) {
321 | commuteComment += " " + t('share_wfh_medium');
322 | }
323 |
324 | // 只有当用户勾选了班车选项,且班车对通勤有正面影响时才添加评价
325 | if (props.hasShuttle && (props.shuttle === '0.7' || props.shuttle === '0.5')) {
326 | commuteComment += " " + t('share_shuttle_service_good');
327 | }
328 |
329 | const commuteDetails = [
330 | { label: t('share_daily_commute_hours'), value: `${props.commuteHours} ${t('share_hours')}` },
331 | { label: t('share_remote_work'), value: `${props.wfhDaysPerWeek}/${props.workDaysPerWeek} ${t('share_days_per_week')} (${Math.round(wfhRatio * 100)}%)` }
332 | ];
333 |
334 | // 只有当用户勾选了班车选项时才添加班车信息
335 | if (props.hasShuttle) {
336 | commuteDetails.push({ label: t('share_shuttle_service'), value: getShuttleDesc(props.shuttle, t) });
337 | }
338 |
339 | comments.push({
340 | title: t('share_daily_commute_hours'),
341 | content: commuteComment,
342 | emoji: wfhRatio >= 0.5 ? "🏠" : "🚌",
343 | details: commuteDetails
344 | });
345 |
346 | // 4. 工作环境与人际关系评价
347 | const leadershipRating = props.leadership;
348 | const teamworkRating = props.teamwork;
349 | const workEnvironment = props.workEnvironment;
350 |
351 | let environmentComment = "";
352 |
353 | if (workEnvironment === '1.1') {
354 | environmentComment = t('share_cbd_environment');
355 | } else if (workEnvironment === '0.8' || workEnvironment === '0.9') {
356 | environmentComment = t('share_factory_environment');
357 | } else {
358 | environmentComment = t('share_normal_environment');
359 | }
360 |
361 | // 更细致的领导关系评价
362 | if (leadershipRating === '1.3') {
363 | environmentComment += " " + t('share_leadership_excellent');
364 | } else if (leadershipRating === '1.1') {
365 | environmentComment += " " + t('share_leadership_good');
366 | } else if (leadershipRating === '1.0') {
367 | environmentComment += " " + t('share_leadership_normal');
368 | } else if (leadershipRating === '0.9') {
369 | environmentComment += " " + t('share_leadership_strict');
370 | } else if (leadershipRating === '0.7') {
371 | environmentComment += " " + t('share_leadership_bad');
372 | }
373 |
374 | // 更细致的同事关系评价
375 | if (teamworkRating === '1.2') {
376 | environmentComment += " " + t('share_teamwork_excellent');
377 | } else if (teamworkRating === '1.1') {
378 | environmentComment += " " + t('share_teamwork_good');
379 | } else if (teamworkRating === '1.0') {
380 | environmentComment += " " + t('share_teamwork_normal');
381 | } else if (teamworkRating === '0.9') {
382 | environmentComment += " " + t('share_teamwork_bad');
383 | }
384 |
385 | const environmentDetails = [
386 | { label: t('share_office_environment'), value: getWorkEnvironmentDesc(workEnvironment, t) },
387 | { label: t('share_leadership_relation'), value: getLeadershipDesc(leadershipRating, t) },
388 | { label: t('share_colleague_relationship'), value: getTeamworkDesc(teamworkRating, t) }
389 | ];
390 |
391 | // 只有当用户勾选了食堂选项时才添加食堂信息
392 | if (props.hasCanteen) {
393 | environmentDetails.push({ label: t('share_canteen_quality'), value: getCanteenDesc(props.canteen, t) });
394 | }
395 |
396 | comments.push({
397 | title: t('share_work_environment_title'),
398 | content: environmentComment,
399 | emoji: "🏢",
400 | details: environmentDetails
401 | });
402 |
403 | // 5. 工作时间与强度评价
404 | const workHoursNum = parseFloat(props.workHours);
405 | const restTimeNum = parseFloat(props.restTime);
406 | const effectiveWorkTime = workHoursNum + parseFloat(props.commuteHours) - 0.5 * restTimeNum;
407 |
408 | let workTimeComment = "";
409 | if (effectiveWorkTime <= 8) {
410 | workTimeComment = t('share_workhours_balanced');
411 | } else if (effectiveWorkTime <= 11) {
412 | workTimeComment = t('share_workhours_long');
413 | } else {
414 | workTimeComment = t('share_workhours_excessive');
415 | }
416 |
417 | if (restTimeNum >= 2.5) {
418 | workTimeComment += " " + t('share_rest_adequate');
419 | } else if (restTimeNum <= 1) {
420 | workTimeComment += " " + t('share_rest_insufficient');
421 | }
422 |
423 | const annualLeaveNum = parseFloat(props.annualLeave);
424 | if (annualLeaveNum >= 15) {
425 | workTimeComment += " " + t('share_leave_abundant');
426 | } else if (annualLeaveNum <= 5) {
427 | workTimeComment += " " + t('share_leave_limited');
428 | }
429 |
430 | const totalLeave = parseFloat(props.annualLeave) + parseFloat(props.publicHolidays) + parseFloat(props.paidSickLeave) * 0.6;
431 |
432 | comments.push({
433 | title: t('share_work_hours_title'),
434 | content: workTimeComment,
435 | emoji: "⏱️",
436 | details: [
437 | { label: t('work_hours'), value: `${props.workHours} ${t('share_hours')}` },
438 | { label: t('share_daily_work_hours'), value: `${effectiveWorkTime.toFixed(1)} ${t('share_hours')}` },
439 | { label: t('rest_time'), value: `${props.restTime} ${t('share_hours')}` },
440 | { label: t('annual_leave'), value: `${props.annualLeave} ${t('share_days_per_year')}` },
441 | { label: t('paid_sick_leave'), value: `${props.paidSickLeave} ${t('share_days_per_year')}` },
442 | { label: t('public_holidays'), value: `${props.publicHolidays} ${t('share_days_per_year')}` }
443 | ]
444 | });
445 |
446 | // 6. 教育背景与职业发展评价
447 | const degreeType = props.degreeType;
448 | const workYears = props.workYears;
449 | const jobStability = props.jobStability;
450 |
451 | let careerComment = "";
452 | if (degreeType === 'phd') {
453 | careerComment = t('share_phd_comment');
454 | } else if (degreeType === 'masters') {
455 | careerComment = t('share_masters_comment');
456 | } else if (degreeType === 'bachelor') {
457 | careerComment = t('share_bachelor_comment');
458 | } else {
459 | careerComment = t('share_below_bachelor_comment');
460 | }
461 |
462 | if (workYears === '0') {
463 | careerComment += " " + t('share_fresh_graduate_comment');
464 | } else if (parseInt(workYears) >= 6) {
465 | careerComment += " " + t('share_experienced_comment');
466 | } else {
467 | careerComment += " " + t('share_mid_career_comment');
468 | }
469 |
470 | if (jobStability === 'government') {
471 | careerComment += " " + t('share_government_job_comment');
472 | } else if (jobStability === 'private' || jobStability === 'foreign' || jobStability === 'state') {
473 | careerComment += " " + t('share_private_job_comment');
474 | } else if (jobStability === 'dispatch') {
475 | careerComment += " " + t('share_dispatch_job_comment');
476 | } else if (jobStability === 'freelance') {
477 | careerComment += " " + t('share_freelance_job_comment');
478 | }
479 |
480 | comments.push({
481 | title: t('share_education_and_experience'),
482 | content: careerComment,
483 | emoji: "📚",
484 | details: [
485 | { label: t('share_highest_degree'), value: getDegreeDesc(degreeType, t) },
486 | { label: t('share_school_type_label'), value: getSchoolTypeDesc(props.schoolType, degreeType, t) },
487 | { label: t('share_work_years_label'), value: getWorkYearsDesc(workYears, t) },
488 | { label: t('share_contract_type_label'), value: getJobStabilityDesc(jobStability, t) }
489 | ]
490 | });
491 |
492 | // 7. 薪资评价
493 | const dailySalary = props.dailySalary;
494 | const isYuan = props.isYuan === 'true';
495 |
496 | let salaryComment = "";
497 | const salaryNumeric = parseFloat(dailySalary);
498 | if (isYuan) {
499 | if (salaryNumeric >= 1000) {
500 | salaryComment = t('share_salary_high_cny');
501 | } else if (salaryNumeric >= 500) {
502 | salaryComment = t('share_salary_medium_cny');
503 | } else {
504 | salaryComment = t('share_salary_low_cny');
505 | }
506 | } else {
507 | if (salaryNumeric >= 150) {
508 | salaryComment = t('share_salary_high_foreign');
509 | } else if (salaryNumeric >= 80) {
510 | salaryComment = t('share_salary_medium_foreign');
511 | } else {
512 | salaryComment = t('share_salary_low_foreign');
513 | }
514 | }
515 |
516 | // 考虑城市因素
517 | if (props.cityFactor === '0.70' || props.cityFactor === '0.80') {
518 | salaryComment += " " + t('share_high_cost_city');
519 | } else if (props.cityFactor === '1.25' || props.cityFactor === '1.40' || props.cityFactor === '1.50') {
520 | salaryComment += " " + t('share_low_cost_city');
521 | }
522 |
523 | comments.push({
524 | title: t('share_daily_salary'),
525 | content: salaryComment,
526 | emoji: "💰",
527 | details: [
528 | { label: t('share_daily_salary'), value: `${props.currencySymbol}${dailySalary}/${t('share_day')}` },
529 | { label: t('share_working_days_per_year'), value: `${props.workDaysPerYear} ${t('share_days')}` }
530 | ]
531 | });
532 |
533 | // 8. 总结性价比评价
534 | let valueComment = "";
535 | if (valueNum < 1.0) {
536 | valueComment = t('share_value_low');
537 | } else if (valueNum <= 2.0) {
538 | valueComment = t('share_value_medium');
539 | } else {
540 | valueComment = t('share_value_high');
541 | }
542 |
543 | comments.push({
544 | title: t('share_summary_advice'),
545 | content: valueComment,
546 | emoji: "💎",
547 | details: []
548 | });
549 |
550 | return comments;
551 | })();
552 |
553 | // 是否是移动设备(响应式设计辅助函数)
554 | const [isMobile, setIsMobile] = useState(false);
555 |
556 | // 检测设备类型
557 | useEffect(() => {
558 | if (typeof window !== 'undefined') {
559 | const checkMobile = () => {
560 | setIsMobile(window.innerWidth < 640);
561 | };
562 | checkMobile();
563 | window.addEventListener('resize', checkMobile);
564 | return () => window.removeEventListener('resize', checkMobile);
565 | }
566 | }, []);
567 |
568 | // 处理下载图片 - 使用简化版报告
569 | const handleDownload = async () => {
570 | if (!simpleReportRef.current || isDownloading) return;
571 |
572 | try {
573 | setIsDownloading(true);
574 |
575 | // 获取简化版报告元素
576 | const element = simpleReportRef.current;
577 |
578 | // 动态导入html2canvas,确保只在客户端加载
579 | const html2canvasModule = await import('html2canvas');
580 | const html2canvas = html2canvasModule.default;
581 |
582 | // 修复 html2canvas 在处理 rem 时文字基线问题
583 | const style = document.createElement('style');
584 | document.head.appendChild(style);
585 | style.sheet?.insertRule('body > div:last-child img { display: inline-block; }');
586 |
587 | // 使用html2canvas生成图片
588 | const canvas = await html2canvas(element, {
589 | scale: 2,
590 | backgroundColor: '#FFFFFF',
591 | useCORS: true,
592 | allowTaint: true,
593 | logging: false
594 | });
595 |
596 | // 生成 canvas 后移除临时的 style 标签
597 | style.remove();
598 |
599 | // 转换为图片并下载
600 | const image = canvas.toDataURL('image/png');
601 | const link = document.createElement('a');
602 | link.href = image;
603 | link.download = `${t('share_job_worth_report')}.png`;
604 | link.click();
605 |
606 | } catch (error) {
607 | console.error('生成分享图片失败:', error);
608 | alert('生成分享图片失败,请稍后再试');
609 | } finally {
610 | setIsDownloading(false);
611 | }
612 | };
613 |
614 | // 获取背景样式
615 | const getBackground = () => {
616 | const valueNum = parseFloat(props.value);
617 | if (valueNum < 0.6) return 'from-pink-100 to-red-100 dark:from-pink-900 dark:to-red-900';
618 | if (valueNum < 1.0) return 'from-red-100 to-orange-100 dark:from-red-900 dark:to-orange-900';
619 | if (valueNum <= 1.8) return 'from-orange-100 to-yellow-100 dark:from-orange-900 dark:to-yellow-900';
620 | if (valueNum <= 2.5) return 'from-blue-100 to-indigo-100 dark:from-blue-900 dark:to-indigo-900';
621 | if (valueNum <= 3.2) return 'from-green-100 to-emerald-100 dark:from-green-900 dark:to-emerald-900';
622 | if (valueNum <= 4.0) return 'from-purple-100 to-pink-100 dark:from-purple-900 dark:to-pink-900';
623 | return 'from-yellow-100 to-amber-100 dark:from-yellow-900 dark:to-amber-900';
624 | };
625 |
626 | return (
627 |
628 | {/* 返回按钮 */}
629 |
630 |
631 |
632 |
{t('share_back_to_calculator')}
633 |
634 |
635 |
636 |
637 | {/* 标题 - 移动端更紧凑 */}
638 |
639 |
{isClient ? getEmoji(parseFloat(props.value)) : '😊'}
640 |
641 | {t('share_your_job_worth_report')}
642 |
643 |
644 |
645 | {props.value}
646 |
647 | {isClient ? t(getAssessmentKey(props.assessment)) : ''}
648 |
649 |
650 |
651 | {/* 性价比评语卡片 - 移动端更紧凑 */}
652 |
653 | {isClient && personalizedComments.map((comment, index) => (
654 |
655 |
656 |
{comment.emoji}
657 |
658 |
{comment.title}
659 |
{comment.content}
660 |
661 | {/* 用户选项详情 - 移动端使用行内排列 */}
662 | {comment.details && comment.details.length > 0 && (
663 |
664 |
665 | {comment.details.map((detail, i) => (
666 | isMobile ? (
667 |
668 | {detail.label}:
669 | {detail.value}
670 |
671 | ) : (
672 |
673 | {detail.label}
674 | {detail.value}
675 |
676 | )
677 | ))}
678 |
679 |
680 | )}
681 |
682 |
683 |
684 | ))}
685 |
686 |
687 | {/* 底部信息 - 更小的文字 */}
688 |
689 |
{t('share_custom_made')}
690 |
worthjob.zippland.com
691 |
692 |
693 |
694 | {/* 操作按钮 - 更小的按钮 */}
695 |
696 |
701 |
702 | {isDownloading ? t('share_generating') : t('share_download_report')}
703 |
704 |
705 |
706 | {/* 简化版报告,仅用于下载,在页面中隐藏 */}
707 | {isClient && (
708 |
709 |
710 |
711 | {/* 报告头部 - 渐变背景 */}
712 |
713 |
714 |
{getEmoji(parseFloat(props.value))}
715 |
{t('share_job_worth_report')}
716 |
717 |
718 | {props.value} - {t(getAssessmentKey(props.assessment))}
719 |
720 |
721 |
722 |
723 |
724 | {/* 报告内容 */}
725 |
726 | {/* 数据表格 */}
727 |
728 | {/* 基础信息 */}
729 |
730 |
731 | 📊 {t('share_basic_info')}
732 |
733 |
734 |
735 |
{t('share_work_city')}
736 |
{getCityName(props.cityFactor, t)}
737 |
738 |
739 |
{t('share_country')}
740 |
{getCountryName(props.countryCode, language)}
741 |
742 |
743 |
{t('share_is_hometown')}
744 |
{props.homeTown === 'yes' ? t('share_yes') : t('share_no')}
745 |
746 |
747 |
{t('share_daily_salary')}
748 |
{props.currencySymbol}{props.dailySalary}/{t('share_day')}
749 |
750 |
751 |
{t('share_working_days_per_year')}
752 |
{props.workDaysPerYear} {t('share_days')}
753 |
754 |
755 |
756 |
757 | {/* 工作时间 */}
758 |
759 |
760 | ⏱️ {t('share_work_hours_title')}
761 |
762 |
763 |
764 | {t('share_daily_work_hours')}
765 | {props.workHours} {t('share_hours')}
766 |
767 |
768 | {t('share_daily_commute_hours')}
769 | {props.commuteHours} {t('share_hours')}
770 |
771 |
772 | {t('share_rest_time')}
773 | {props.restTime} {t('share_hours')}
774 |
775 |
776 | {t('share_weekly_work_days')}
777 | {props.workDaysPerWeek} {t('share_days')}
778 |
779 | {props.hasShuttle && (
780 |
781 | {t('share_shuttle_service')}
782 | {getShuttleDesc(props.shuttle, t)}
783 |
784 | )}
785 |
786 |
787 |
788 | {/* 工作环境 */}
789 |
790 |
791 | 🏢 {t('share_work_environment_title')}
792 |
793 |
794 |
795 | {t('share_office_environment')}
796 | {getWorkEnvironmentDesc(props.workEnvironment, t)}
797 |
798 |
799 | {t('share_leadership_relation')}
800 | {getLeadershipDesc(props.leadership, t)}
801 |
802 |
803 | {t('share_colleague_relationship')}
804 | {getTeamworkDesc(props.teamwork, t)}
805 |
806 | {props.hasCanteen && (
807 |
808 | {t('share_canteen_quality')}
809 | {getCanteenDesc(props.canteen, t)}
810 |
811 | )}
812 |
813 |
814 |
815 | {/* 教育背景 */}
816 |
817 |
818 | 📚 {t('share_education_and_experience')}
819 |
820 |
821 |
822 |
{t('share_highest_degree')}
823 |
{getDegreeDesc(props.degreeType, t)}
824 |
825 |
826 |
{t('share_school_type_label')}
827 |
{getSchoolTypeDesc(props.schoolType, props.degreeType, t)}
828 |
829 |
830 |
{t('share_work_years_label')}
831 |
{getWorkYearsDesc(props.workYears, t)}
832 |
833 |
834 |
{t('share_contract_type_label')}
835 |
{getJobStabilityDesc(props.jobStability, t)}
836 |
837 |
838 |
839 |
840 | {/* 结论 */}
841 |
842 |
843 |
844 | 💎 {t('share_final_assessment')}
845 |
846 |
847 |
{getEmoji(parseFloat(props.value))}
848 |
849 | {props.value} - {t(getAssessmentKey(props.assessment))}
850 |
851 |
852 |
853 | {parseFloat(props.value) < 1.0
854 | ? t('share_value_low')
855 | : parseFloat(props.value) <= 2.0
856 | ? t('share_value_medium')
857 | : t('share_value_high')
858 | }
859 |
860 |
861 |
862 |
863 |
864 |
865 | {/* 页脚 */}
866 |
867 |
868 |
869 |
874 |
875 |
{t('share_custom_made')}
876 |
worthjob.zippland.com
877 |
878 |
879 |
884 |
885 |
886 |
887 |
888 |
889 | )}
890 |
891 | );
892 | };
893 |
894 | export default ShareCard;
--------------------------------------------------------------------------------
/components/calculator.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useState, useCallback, useEffect, useRef } from 'react';
4 | import { Wallet, Github, FileText, Book, History, Eye , Star} from 'lucide-react'; // 添加新图标
5 | import Link from 'next/link'; // 导入Link组件用于导航
6 | import { useLanguage } from './LanguageContext';
7 | import { LanguageSwitcher } from './LanguageSwitcher';
8 | import { countryNames } from './LanguageContext';
9 |
10 | // 定义PPP转换因子映射表
11 | const pppFactors: Record = {
12 | 'AF': 18.71,
13 | 'AO': 167.66,
14 | 'AL': 41.01,
15 | 'AR': 28.67,
16 | 'AM': 157.09,
17 | 'AG': 2.06,
18 | 'AU': 1.47,
19 | 'AT': 0.76,
20 | 'AZ': 0.50,
21 | 'BI': 680.41,
22 | 'BE': 0.75,
23 | 'BJ': 211.97,
24 | 'BF': 209.84,
25 | 'BD': 32.81,
26 | 'BG': 0.70,
27 | 'BH': 0.18,
28 | 'BS': 0.88,
29 | 'BA': 0.66,
30 | 'BY': 0.77,
31 | 'BZ': 1.37,
32 | 'BO': 2.60,
33 | 'BR': 2.36,
34 | 'BB': 2.24,
35 | 'BN': 0.58,
36 | 'BT': 20.11,
37 | 'BW': 4.54,
38 | 'CF': 280.19,
39 | 'CA': 1.21,
40 | 'CH': 1.14,
41 | 'CL': 418.43,
42 | 'CN': 4.19,
43 | 'CI': 245.25,
44 | 'CM': 228.75,
45 | 'CD': 911.27,
46 | 'CG': 312.04,
47 | 'CO': 1352.79,
48 | 'KM': 182.34,
49 | 'CV': 46.51,
50 | 'CR': 335.86,
51 | 'CY': 0.61,
52 | 'CZ': 12.66,
53 | 'DE': 0.75,
54 | 'DJ': 105.29,
55 | 'DM': 1.69,
56 | 'DK': 6.60,
57 | 'DO': 22.90,
58 | 'DZ': 37.24,
59 | 'EC': 0.51,
60 | 'EG': 4.51,
61 | 'ES': 0.62,
62 | 'EE': 0.53,
63 | 'ET': 12.11,
64 | 'FI': 0.84,
65 | 'FJ': 0.91,
66 | 'FR': 0.73,
67 | 'GA': 265.46,
68 | 'GB': 0.70,
69 | 'GE': 0.90,
70 | 'GH': 2.33,
71 | 'GN': 4053.64,
72 | 'GM': 17.79,
73 | 'GW': 214.86,
74 | 'GQ': 229.16,
75 | 'GR': 0.54,
76 | 'GD': 1.64,
77 | 'GT': 4.01,
78 | 'GY': 73.60,
79 | 'HK': 6.07,
80 | 'HN': 10.91,
81 | 'HR': 3.21,
82 | 'HT': 40.20,
83 | 'HU': 148.01,
84 | 'ID': 4673.65,
85 | 'IN': 21.99,
86 | 'IE': 0.78,
87 | 'IR': 30007.63,
88 | 'IQ': 507.58,
89 | 'IS': 145.34,
90 | 'IL': 3.59,
91 | 'IT': 0.66,
92 | 'JM': 72.03,
93 | 'JO': 0.29,
94 | 'JP': 102.84,
95 | 'KZ': 139.91,
96 | 'KE': 43.95,
97 | 'KG': 18.28,
98 | 'KH': 1400.09,
99 | 'KI': 1.00,
100 | 'KN': 1.92,
101 | 'KR': 861.82,
102 | 'LA': 2889.36,
103 | 'LB': 1414.91,
104 | 'LR': 0.41,
105 | 'LY': 0.48,
106 | 'LC': 1.93,
107 | 'LK': 51.65,
108 | 'LS': 5.90,
109 | 'LT': 0.45,
110 | 'LU': 0.86,
111 | 'LV': 0.48,
112 | 'MO': 5.18,
113 | 'MA': 3.92,
114 | 'MD': 6.06,
115 | 'MG': 1178.10,
116 | 'MV': 8.35,
117 | 'MX': 9.52,
118 | 'MK': 18.83,
119 | 'ML': 211.41,
120 | 'MT': 0.57,
121 | 'MM': 417.35,
122 | 'ME': 0.33,
123 | 'MN': 931.67,
124 | 'MZ': 24.05,
125 | 'MR': 12.01,
126 | 'MU': 16.52,
127 | 'MW': 298.82,
128 | 'MY': 1.57,
129 | 'NA': 7.40,
130 | 'NE': 257.60,
131 | 'NG': 144.27,
132 | 'NI': 11.75,
133 | 'NL': 0.77,
134 | 'NO': 10.03,
135 | 'NP': 33.52,
136 | 'NZ': 1.45,
137 | 'PK': 38.74,
138 | 'PA': 0.46,
139 | 'PE': 1.80,
140 | 'PH': 19.51,
141 | 'PG': 2.11,
142 | 'PL': 1.78,
143 | 'PR': 0.92,
144 | 'PT': 0.57,
145 | 'PY': 2575.54,
146 | 'PS': 0.57,
147 | 'QA': 2.06,
148 | 'RO': 1.71,
149 | 'RU': 25.88,
150 | 'RW': 339.88,
151 | 'SA': 1.61,
152 | 'SD': 21.85,
153 | 'SN': 245.98,
154 | 'SG': 0.84,
155 | 'SB': 7.08,
156 | 'SL': 2739.26,
157 | 'SV': 0.45,
158 | 'SO': 9107.78,
159 | 'RS': 41.13,
160 | 'ST': 10.94,
161 | 'SR': 3.55,
162 | 'SK': 0.53,
163 | 'SI': 0.56,
164 | 'SE': 8.77,
165 | 'SZ': 6.36,
166 | 'SC': 7.82,
167 | 'TC': 1.07,
168 | 'TD': 220.58,
169 | 'TG': 236.83,
170 | 'TH': 12.34,
171 | 'TJ': 2.30,
172 | 'TL': 0.41,
173 | 'TT': 4.15,
174 | 'TN': 0.91,
175 | 'TR': 2.13,
176 | 'TV': 1.29,
177 | 'TW': 13.85,
178 | 'TZ': 888.32,
179 | 'UG': 1321.35,
180 | 'UA': 7.69,
181 | 'UY': 28.45,
182 | 'US': 1.00,
183 | 'UZ': 2297.17,
184 | 'VC': 1.54,
185 | 'VN': 7473.67,
186 | 'VU': 110.17,
187 | 'XK': 0.33,
188 | 'ZA': 6.93,
189 | 'ZM': 5.59,
190 | 'ZW': 24.98
191 | };
192 |
193 | // 添加各国货币符号映射
194 | const currencySymbols: Record = {
195 | 'AF': '؋', // 阿富汗尼
196 | 'AL': 'L', // 阿尔巴尼亚列克
197 | 'DZ': 'د.ج', // 阿尔及利亚第纳尔
198 | 'AO': 'Kz', // 安哥拉宽扎
199 | 'AR': '$', // 阿根廷比索
200 | 'AM': '֏', // 亚美尼亚德拉姆
201 | 'AU': 'A$', // 澳大利亚元
202 | 'AT': '€', // 欧元
203 | 'AZ': '₼', // 阿塞拜疆马纳特
204 | 'BI': 'FBu', // 布隆迪法郎
205 | 'BE': '€', // 欧元
206 | 'BJ': 'CFA', // 西非法郎
207 | 'BF': 'CFA', // 西非法郎
208 | 'BD': '৳', // 孟加拉塔卡
209 | 'BG': 'лв', // 保加利亚列弗
210 | 'BH': '.د.ب', // 巴林第纳尔
211 | 'BS': 'B$', // 巴哈马元
212 | 'BA': 'KM', // 波黑可兑换马克
213 | 'BY': 'Br', // 白俄罗斯卢布
214 | 'BZ': 'BZ$', // 伯利兹元
215 | 'BO': 'Bs', // 玻利维亚诺
216 | 'BR': 'R$', // 巴西雷亚尔
217 | 'BB': 'Bds$', // 巴巴多斯元
218 | 'BN': 'B$', // 文莱元
219 | 'BT': 'Nu.', // 不丹努扎姆
220 | 'BW': 'P', // 博茨瓦纳普拉
221 | 'CA': 'C$', // 加拿大元
222 | 'CH': 'CHF', // 瑞士法郎
223 | 'CL': 'CLP$', // 智利比索
224 | 'CN': '¥', // 人民币
225 | 'CI': 'CFA', // 西非法郎
226 | 'CM': 'FCFA', // 中非法郎
227 | 'CD': 'FC', // 刚果法郎
228 | 'CG': 'FCFA', // 中非法郎
229 | 'CO': 'Col$', // 哥伦比亚比索
230 | 'CR': '₡', // 哥斯达黎加科朗
231 | 'CY': '€', // 欧元
232 | 'CZ': 'Kč', // 捷克克朗
233 | 'DE': '€', // 欧元
234 | 'DK': 'kr', // 丹麦克朗
235 | 'DO': 'RD$', // 多米尼加比索
236 | 'EC': '$', // 美元(厄瓜多尔使用美元)
237 | 'EG': 'E£', // 埃及镑
238 | 'ES': '€', // 欧元
239 | 'EE': '€', // 欧元
240 | 'ET': 'Br', // 埃塞俄比亚比尔
241 | 'FI': '€', // 欧元
242 | 'FJ': 'FJ$', // 斐济元
243 | 'FR': '€', // 欧元
244 | 'GB': '£', // 英镑
245 | 'GE': '₾', // 格鲁吉亚拉里
246 | 'GH': '₵', // 加纳塞地
247 | 'GR': '€', // 欧元
248 | 'GT': 'Q', // 危地马拉格查尔
249 | 'HK': 'HK$', // 港元
250 | 'HN': 'L', // 洪都拉斯伦皮拉
251 | 'HR': '€', // 欧元(克罗地亚自2023年加入欧元区)
252 | 'HU': 'Ft', // 匈牙利福林
253 | 'ID': 'Rp', // 印尼盾
254 | 'IN': '₹', // 印度卢比
255 | 'IE': '€', // 欧元
256 | 'IR': '﷼', // 伊朗里亚尔
257 | 'IQ': 'ع.د', // 伊拉克第纳尔
258 | 'IS': 'kr', // 冰岛克朗
259 | 'IL': '₪', // 以色列新谢克尔
260 | 'IT': '€', // 欧元
261 | 'JM': 'J$', // 牙买加元
262 | 'JO': 'JD', // 约旦第纳尔
263 | 'JP': '¥', // 日元
264 | 'KE': 'KSh', // 肯尼亚先令
265 | 'KR': '₩', // 韩元
266 | 'KW': 'د.ك', // 科威特第纳尔
267 | 'LB': 'L£', // 黎巴嫩镑
268 | 'LK': 'Rs', // 斯里兰卡卢比
269 | 'LT': '€', // 欧元
270 | 'LU': '€', // 欧元
271 | 'LV': '€', // 欧元
272 | 'MA': 'د.م.', // 摩洛哥迪拉姆
273 | 'MX': 'Mex$', // 墨西哥比索
274 | 'MY': 'RM', // 马来西亚林吉特
275 | 'NG': '₦', // 尼日利亚奈拉
276 | 'NL': '€', // 欧元
277 | 'NO': 'kr', // 挪威克朗
278 | 'NP': 'रू', // 尼泊尔卢比
279 | 'NZ': 'NZ$', // 新西兰元
280 | 'PK': '₨', // 巴基斯坦卢比
281 | 'PA': 'B/.', // 巴拿马巴波亚
282 | 'PE': 'S/.', // 秘鲁索尔
283 | 'PH': '₱', // 菲律宾比索
284 | 'PL': 'zł', // 波兰兹罗提
285 | 'PT': '€', // 欧元
286 | 'QA': 'ر.ق', // 卡塔尔里亚尔
287 | 'RO': 'lei', // 罗马尼亚列伊
288 | 'RU': '₽', // 俄罗斯卢布
289 | 'SA': 'ر.س', // 沙特里亚尔
290 | 'SG': 'S$', // 新加坡元
291 | 'SK': '€', // 欧元
292 | 'SI': '€', // 欧元
293 | 'SE': 'kr', // 瑞典克朗
294 | 'TH': '฿', // 泰铢
295 | 'TR': '₺', // 土耳其里拉
296 | 'TW': 'NT$', // 新台币
297 | 'UA': '₴', // 乌克兰格里夫纳
298 | 'US': '$', // 美元
299 | 'UY': '$U', // 乌拉圭比索
300 | 'VN': '₫', // 越南盾
301 | 'ZA': 'R', // 南非兰特
302 | // 默认其他国家使用美元符号
303 | };
304 |
305 | // 定义历史记录项的接口
306 | interface HistoryItem {
307 | id: string;
308 | timestamp: number;
309 | value: string;
310 | assessment: string;
311 | assessmentColor: string;
312 | salary: string;
313 | countryCode: string;
314 | countryName: string;
315 |
316 | // 添加所有需要在分享页面展示的字段
317 | cityFactor: string;
318 | workHours: string;
319 | commuteHours: string;
320 | restTime: string;
321 | dailySalary: string;
322 | workDaysPerYear: string;
323 | workDaysPerWeek: string;
324 | wfhDaysPerWeek: string;
325 | annualLeave: string;
326 | paidSickLeave: string;
327 | publicHolidays: string;
328 | workEnvironment: string;
329 | leadership: string;
330 | teamwork: string;
331 | degreeType: string;
332 | schoolType: string;
333 | education: string;
334 | homeTown: string;
335 | shuttle: string;
336 | canteen: string;
337 | workYears: string;
338 | jobStability: string;
339 | bachelorType: string;
340 | hasShuttle: boolean;
341 | hasCanteen: boolean;
342 | }
343 |
344 | // 定义表单数据接口
345 | interface FormData {
346 | salary: string;
347 | nonChinaSalary: boolean;
348 | workDaysPerWeek: string;
349 | wfhDaysPerWeek: string;
350 | annualLeave: string;
351 | paidSickLeave: string;
352 | publicHolidays: string;
353 | workHours: string;
354 | commuteHours: string;
355 | restTime: string;
356 | cityFactor: string;
357 | workEnvironment: string;
358 | leadership: string;
359 | teamwork: string;
360 | homeTown: string;
361 | degreeType: string;
362 | schoolType: string;
363 | bachelorType: string;
364 | workYears: string;
365 | shuttle: string;
366 | canteen: string;
367 | jobStability: string;
368 | education: string;
369 | hasShuttle: boolean;
370 | hasCanteen: boolean;
371 | }
372 |
373 | // 定义计算结果接口
374 | interface Result {
375 | value: number;
376 | workDaysPerYear: number;
377 | dailySalary: number;
378 | assessment: string;
379 | assessmentColor: string;
380 | }
381 |
382 | const SalaryCalculator = () => {
383 | // 获取语言上下文
384 | const { t, language } = useLanguage();
385 |
386 | // 添加客户端检测
387 | const [isBrowser, setIsBrowser] = useState(false);
388 |
389 | // 添加滚动位置保存的引用
390 | const scrollPositionRef = useRef(0);
391 |
392 | // 添加历史记录状态
393 | const [history, setHistory] = useState([]);
394 | const [showHistory, setShowHistory] = useState(false);
395 |
396 | // 在组件挂载时标记为浏览器环境
397 | useEffect(() => {
398 | setIsBrowser(true);
399 |
400 | // 在客户端环境中执行重定向
401 | if (typeof window !== 'undefined') {
402 | const hostname = window.location.hostname;
403 | if (hostname !== 'worthjob.zippland.com' && hostname !== 'localhost' && !hostname.includes('127.0.0.1')) {
404 | window.location.href = 'https://worthjob.zippland.com' + window.location.pathname;
405 | }
406 | }
407 | }, []);
408 |
409 | // 添加用于创建分享图片的引用
410 | const shareResultsRef = useRef(null);
411 |
412 | // 状态管理 - 基础表单和选项
413 | const [formData, setFormData] = useState({
414 | salary: '',
415 | nonChinaSalary: false,
416 | workDaysPerWeek: '5',
417 | wfhDaysPerWeek: '0',
418 | annualLeave: '5',
419 | paidSickLeave: '3',
420 | publicHolidays: '13',
421 | workHours: '10',
422 | commuteHours: '2',
423 | restTime: '2',
424 | cityFactor: '1.0',
425 | workEnvironment: '1.0',
426 | leadership: '1.0',
427 | teamwork: '1.0',
428 | homeTown: 'no',
429 | degreeType: 'bachelor',
430 | schoolType: 'firstTier',
431 | bachelorType: 'firstTier',
432 | workYears: '0',
433 | shuttle: '1.0',
434 | canteen: '1.0',
435 | jobStability: 'private', // 新增:工作稳定度/类型
436 | education: '1.0',
437 | hasShuttle: false, // 确保这是一个明确的布尔值
438 | hasCanteen: false, // 确保这是一个明确的布尔值
439 | });
440 |
441 | const [showPPPInput, setShowPPPInput] = useState(false);
442 | // 修改为国家代码,默认为中国
443 | const [selectedCountry, setSelectedCountry] = useState('CN');
444 |
445 | // 初始化时从localStorage加载国家设置
446 | useEffect(() => {
447 | // 从本地存储读取国家设置
448 | if (typeof window !== 'undefined') {
449 | const savedCountry = localStorage.getItem('selectedCountry');
450 | if (savedCountry) {
451 | setSelectedCountry(savedCountry);
452 | }
453 | }
454 | }, []);
455 |
456 | // 当国家选择改变时保存到localStorage
457 | const handleCountryChange = (countryCode: string) => {
458 | setSelectedCountry(countryCode);
459 | if (typeof window !== 'undefined') {
460 | localStorage.setItem('selectedCountry', countryCode);
461 | }
462 | };
463 |
464 | const [result, setResult] = useState(null);
465 | const [showPPPList, setShowPPPList] = useState(false);
466 | const [assessment, setAssessment] = useState("");
467 | const [assessmentColor, setAssessmentColor] = useState("text-gray-500");
468 | const [visitorVisible, setVisitorVisible] = useState(false);
469 |
470 | // 添加检查document对象存在的逻辑
471 | useEffect(() => {
472 | // 确保在客户端环境中执行
473 | if (typeof window !== 'undefined' && typeof document !== 'undefined') {
474 | const savedHistory = localStorage.getItem('jobValueHistory');
475 | if (savedHistory) {
476 | try {
477 | const parsedHistory = JSON.parse(savedHistory) as Partial[];
478 |
479 | // 处理历史记录,为可能缺失的字段添加默认值
480 | const normalizedHistory: HistoryItem[] = parsedHistory.map((item: Partial) => ({
481 | id: item.id || Date.now().toString(),
482 | timestamp: item.timestamp || Date.now(),
483 | value: item.value || '0',
484 | assessment: item.assessment || 'rating_enter_salary',
485 | assessmentColor: item.assessmentColor || 'text-gray-500',
486 | salary: item.salary || '',
487 | countryCode: item.countryCode || 'CN',
488 | countryName: item.countryName || '中国',
489 |
490 | // 为缺失的分享页面字段添加默认值
491 | cityFactor: item.cityFactor || formData.cityFactor,
492 | workHours: item.workHours || formData.workHours,
493 | commuteHours: item.commuteHours || formData.commuteHours,
494 | restTime: item.restTime || formData.restTime,
495 | dailySalary: item.dailySalary || '0', // 简化,不使用函数
496 | workDaysPerYear: item.workDaysPerYear || '250', // 简化,使用默认值
497 | workDaysPerWeek: item.workDaysPerWeek || formData.workDaysPerWeek,
498 | wfhDaysPerWeek: item.wfhDaysPerWeek || formData.wfhDaysPerWeek,
499 | annualLeave: item.annualLeave || formData.annualLeave,
500 | paidSickLeave: item.paidSickLeave || formData.paidSickLeave,
501 | publicHolidays: item.publicHolidays || formData.publicHolidays,
502 | workEnvironment: item.workEnvironment || formData.workEnvironment,
503 | leadership: item.leadership || formData.leadership,
504 | teamwork: item.teamwork || formData.teamwork,
505 | degreeType: item.degreeType || formData.degreeType,
506 | schoolType: item.schoolType || formData.schoolType,
507 | education: item.education || formData.education,
508 | homeTown: item.homeTown || formData.homeTown,
509 | shuttle: item.shuttle || formData.shuttle,
510 | canteen: item.canteen || formData.canteen,
511 | workYears: item.workYears || formData.workYears,
512 | jobStability: item.jobStability || formData.jobStability,
513 | bachelorType: item.bachelorType || formData.bachelorType,
514 | // 确保 hasShuttle 和 hasCanteen 有合法的布尔值,即使历史记录中没有这些字段
515 | hasShuttle: typeof item.hasShuttle === 'boolean' ? item.hasShuttle : false,
516 | hasCanteen: typeof item.hasCanteen === 'boolean' ? item.hasCanteen : false,
517 | }));
518 |
519 | setHistory(normalizedHistory);
520 | } catch (e) {
521 | console.error('加载历史记录失败', e);
522 | }
523 | }
524 | }
525 | }, [formData]);
526 |
527 | // 监听访客统计加载
528 | useEffect(() => {
529 | // 延迟检查busuanzi是否已加载
530 | const timer = setTimeout(() => {
531 | const pv = document.getElementById('busuanzi_value_site_pv');
532 | const uv = document.getElementById('busuanzi_value_site_uv');
533 |
534 | if (pv && pv.innerText !== '') {
535 | // 直接在现有数字上加上1700000(原seeyoufarm统计数据)
536 | const currentCount = parseInt(pv.innerText, 10) || 0;
537 | pv.innerText = (currentCount + 1700000).toString();
538 |
539 | // 同时增加访客数的历史数据
540 | if (uv && uv.innerText !== '') {
541 | const currentUV = parseInt(uv.innerText, 10) || 0;
542 | uv.innerText = (currentUV + 250000).toString();
543 | }
544 |
545 | setVisitorVisible(true);
546 | } else {
547 | // 如果未加载,再次尝试
548 | const retryTimer = setTimeout(() => {
549 | const pv = document.getElementById('busuanzi_value_site_pv');
550 | const uv = document.getElementById('busuanzi_value_site_uv');
551 |
552 | if (pv && pv.innerText !== '') {
553 | // 直接在现有数字上加上1700000(原seeyoufarm统计数据)
554 | const currentCount = parseInt(pv.innerText, 10) || 0;
555 | pv.innerText = (currentCount + 1700000).toString();
556 |
557 | // 同时增加访客数的历史数据
558 | if (uv && uv.innerText !== '') {
559 | const currentUV = parseInt(uv.innerText, 10) || 0;
560 | uv.innerText = (currentUV + 1300000).toString();
561 | }
562 |
563 | setVisitorVisible(true);
564 | }
565 | }, 2000);
566 | return () => clearTimeout(retryTimer);
567 | }
568 | }, 1000);
569 |
570 | return () => clearTimeout(timer);
571 | }, []);
572 |
573 | // 添加滚动位置保存和恢复逻辑
574 | useEffect(() => {
575 | const handleBeforeStateChange = () => {
576 | // 保存当前滚动位置
577 | if (typeof window !== 'undefined') {
578 | scrollPositionRef.current = window.scrollY;
579 | }
580 | };
581 |
582 | const handleAfterStateChange = () => {
583 | // 恢复滚动位置
584 | if (typeof window !== 'undefined') {
585 | setTimeout(() => {
586 | window.scrollTo(0, scrollPositionRef.current);
587 | }, 0);
588 | }
589 | };
590 |
591 | // 添加到全局事件
592 | window.addEventListener('beforeStateChange', handleBeforeStateChange);
593 | window.addEventListener('afterStateChange', handleAfterStateChange);
594 |
595 | return () => {
596 | // 清理事件监听器
597 | window.removeEventListener('beforeStateChange', handleBeforeStateChange);
598 | window.removeEventListener('afterStateChange', handleAfterStateChange);
599 | };
600 | }, []);
601 |
602 | const calculateWorkingDays = useCallback(() => {
603 | const weeksPerYear = 52;
604 | const totalWorkDays = weeksPerYear * Number(formData.workDaysPerWeek); // 确保转换为数字
605 | const totalLeaves = Number(formData.annualLeave) + Number(formData.publicHolidays) + Number(formData.paidSickLeave) * 0.6; // 带薪病假按70%权重计算
606 | return Math.max(totalWorkDays - totalLeaves, 0);
607 | }, [formData.workDaysPerWeek, formData.annualLeave, formData.publicHolidays, formData.paidSickLeave]);
608 |
609 | const calculateDailySalary = useCallback(() => {
610 | if (!formData.salary) return 0;
611 | const workingDays = calculateWorkingDays();
612 |
613 | // 应用PPP转换因子标准化薪资
614 | // 如果选择了非中国地区,使用选定国家的PPP;否则使用中国默认值4.19
615 | const isNonChina = selectedCountry !== 'CN';
616 | const pppFactor = isNonChina ? pppFactors[selectedCountry] || 4.19 : 4.19;
617 | const standardizedSalary = Number(formData.salary) * (4.19 / pppFactor);
618 |
619 | return standardizedSalary / workingDays; // 除 0 不管, Infinity(爽到爆炸)!
620 | }, [formData.salary, selectedCountry, calculateWorkingDays]);
621 |
622 | // 新增:获取显示用的日薪(转回原始货币)
623 | const getDisplaySalary = useCallback(() => {
624 | const dailySalaryInCNY = calculateDailySalary();
625 | const isNonChina = selectedCountry !== 'CN';
626 | if (isNonChina) {
627 | // 非中国地区,转回本地货币
628 | const pppFactor = pppFactors[selectedCountry] || 4.19;
629 | return (dailySalaryInCNY * pppFactor / 4.19).toFixed(2);
630 | } else {
631 | return dailySalaryInCNY.toFixed(2);
632 | }
633 | }, [calculateDailySalary, selectedCountry]);
634 |
635 | const handleInputChange = useCallback((name: string, value: string | boolean) => {
636 | // 触发自定义事件,保存滚动位置
637 | if (typeof window !== 'undefined') {
638 | window.dispatchEvent(new Event('beforeStateChange'));
639 | }
640 |
641 | // 直接设置值,不进行任何验证
642 | setFormData(prev => ({
643 | ...prev,
644 | [name]: value
645 | }));
646 |
647 | // 在状态更新后,触发恢复滚动位置事件
648 | setTimeout(() => {
649 | if (typeof window !== 'undefined') {
650 | window.dispatchEvent(new Event('afterStateChange'));
651 | }
652 | }, 0);
653 | }, []);
654 |
655 | const calculateValue = () => {
656 | if (!formData.salary) return 0;
657 |
658 | const dailySalary = calculateDailySalary();
659 | const workHours = Number(formData.workHours);
660 | const commuteHours = Number(formData.commuteHours);
661 | const restTime = Number(formData.restTime);
662 |
663 | // 确保正确转换为数字,使用parseFloat可以更可靠地处理字符串转数字
664 | const workDaysPerWeek = parseFloat(formData.workDaysPerWeek) || 5;
665 |
666 | // 允许wfhDaysPerWeek为空字符串,计算时才处理为0
667 | const wfhInput = formData.wfhDaysPerWeek.trim();
668 | const wfhDaysPerWeek = wfhInput === '' ? 0 : Math.min(parseFloat(wfhInput) || 0, workDaysPerWeek);
669 |
670 | // 确保有办公室工作天数时才计算比例,否则设为0
671 | const officeDaysRatio = workDaysPerWeek > 0 ? (workDaysPerWeek - wfhDaysPerWeek) / workDaysPerWeek : 0;
672 |
673 | // 在计算结果中添加一个小的日志输出,以便调试
674 | console.log('WFH计算:', {
675 | workDaysPerWeek,
676 | wfhDaysPerWeek,
677 | officeDaysRatio,
678 | effectiveCommute: commuteHours * officeDaysRatio
679 | });
680 |
681 | // 班车系数只在勾选时使用,否则为1.0
682 | const shuttleFactor = formData.hasShuttle ? Number(formData.shuttle) : 1.0;
683 | const effectiveCommuteHours = commuteHours * officeDaysRatio * shuttleFactor;
684 |
685 | // 食堂系数只在勾选时使用,否则为1.0
686 | const canteenFactor = formData.hasCanteen ? Number(formData.canteen) : 1.0;
687 |
688 | // 工作环境因素,包含食堂和家乡因素
689 | const environmentFactor = Number(formData.workEnvironment) *
690 | Number(formData.leadership) *
691 | Number(formData.teamwork) *
692 | Number(formData.cityFactor) *
693 | canteenFactor;
694 |
695 | // 根据工作年限计算经验薪资倍数
696 | const workYears = Number(formData.workYears);
697 | let experienceSalaryMultiplier = 1.0;
698 |
699 | if (workYears === 0) {
700 | // 应届生:直接根据工作类型设定初始调整系数,反映稳定性/风险价值
701 | // 注意:这些系数在分母中,系数越小,最终价值越高
702 | if (formData.jobStability === 'government') {
703 | experienceSalaryMultiplier = 0.8; // 体制内稳定性高,价值相对高
704 | } else if (formData.jobStability === 'state') {
705 | experienceSalaryMultiplier = 0.9; // 央/国企较稳定,价值相对高
706 | } else if (formData.jobStability === 'foreign') {
707 | experienceSalaryMultiplier = 0.95; // 外企,较为稳定
708 | } else if (formData.jobStability === 'private') {
709 | experienceSalaryMultiplier = 1.0; // 私企作为基准
710 | } else if (formData.jobStability === 'dispatch') {
711 | experienceSalaryMultiplier = 1.1; // 派遣社员风险高,价值相对低
712 | } else if (formData.jobStability === 'freelance') {
713 | experienceSalaryMultiplier = 1.1; // 自由职业风险高,价值相对低
714 | }
715 | } else {
716 | // 非应届生:使用基于增长预期的模型
717 |
718 | // 基准薪资增长曲线(适用于私企)
719 | let baseSalaryMultiplier = 1.0;
720 | if (workYears === 1) baseSalaryMultiplier = 1.5; // 1年:1.50-2.00,取中间值
721 | else if (workYears <= 3) baseSalaryMultiplier = 2.2; // 2-3年:2.20-2.50,取中间值
722 | else if (workYears <= 5) baseSalaryMultiplier = 2.7; // 4-5年:2.70-3.00,取中间值
723 | else if (workYears <= 8) baseSalaryMultiplier = 3.2; // 6-8年:3.20-3.50,取中间值
724 | else if (workYears <= 10) baseSalaryMultiplier = 3.6; // 9-10年:3.60-3.80,取中间值
725 | else baseSalaryMultiplier = 3.9; // 11-13年:3.90-4.20,取中间值
726 |
727 | // 工作单位类型对涨薪幅度的影响系数
728 | let salaryGrowthFactor = 1.0; // 私企基准
729 | if (formData.jobStability === 'foreign') {
730 | salaryGrowthFactor = 0.8; // 外企涨薪幅度为私企的80%
731 | } else if (formData.jobStability === 'state') {
732 | salaryGrowthFactor = 0.4; // 央/国企涨薪幅度为私企的40%
733 | } else if (formData.jobStability === 'government') {
734 | salaryGrowthFactor = 0.2; // 体制内涨薪幅度为私企的20%
735 | } else if (formData.jobStability === 'dispatch') {
736 | salaryGrowthFactor = 1.2; // 派遣社员涨薪幅度为私企的120%(体现不稳定性)
737 | } else if (formData.jobStability === 'freelance') {
738 | salaryGrowthFactor = 1.2; // 自由职业涨薪幅度为私企的120%(体现不稳定性)
739 | }
740 |
741 | // 根据公式: 1 + (对应幅度-1) * 工作单位系数,计算最终薪资倍数
742 | experienceSalaryMultiplier = 1 + (baseSalaryMultiplier - 1) * salaryGrowthFactor;
743 | }
744 |
745 | // 薪资满意度应该受到经验薪资倍数的影响
746 | // 相同薪资,对于高经验者来说价值更低,对应的计算公式需要考虑经验倍数
747 | return (dailySalary * environmentFactor) /
748 | (35 * (workHours + effectiveCommuteHours - 0.5 * restTime) * Number(formData.education) * experienceSalaryMultiplier);
749 | };
750 |
751 | const value = calculateValue();
752 |
753 | const getValueAssessment = useCallback(() => {
754 | if (!formData.salary) return { text: t('rating_enter_salary'), color: "text-gray-500" };
755 | if (value < 0.6) return { text: t('rating_terrible'), color: "text-pink-800" };
756 | if (value < 1.0) return { text: t('rating_poor'), color: "text-red-500" };
757 | if (value <= 1.8) return { text: t('rating_average'), color: "text-orange-500" };
758 | if (value <= 2.5) return { text: t('rating_good'), color: "text-blue-500" };
759 | if (value <= 3.2) return { text: t('rating_great'), color: "text-green-500" };
760 | if (value <= 4.0) return { text: t('rating_excellent'), color: "text-purple-500" };
761 | return { text: t('rating_perfect'), color: "text-yellow-400" };
762 | }, [formData.salary, value, t]);
763 |
764 | // 获取评级的翻译键,用于分享链接
765 | const getValueAssessmentKey = useCallback(() => {
766 | if (!formData.salary) return 'rating_enter_salary';
767 | if (value < 0.6) return 'rating_terrible';
768 | if (value < 1.0) return 'rating_poor';
769 | if (value <= 1.8) return 'rating_average';
770 | if (value <= 2.5) return 'rating_good';
771 | if (value <= 3.2) return 'rating_great';
772 | if (value <= 4.0) return 'rating_excellent';
773 | return 'rating_perfect';
774 | }, [formData.salary, value]);
775 |
776 | const RadioGroup = ({ label, name, value, onChange, options }: {
777 | label: string;
778 | name: string;
779 | value: string;
780 | onChange: (name: string, value: string | boolean) => void;
781 | options: Array<{ label: string; value: string; }>;
782 | }) => (
783 |
784 |
{label}
785 |
786 | {options.map((option) => (
787 | {
794 | e.preventDefault(); // 阻止默认行为
795 | e.stopPropagation(); // 阻止事件冒泡
796 | onChange(name, option.value);
797 | }}
798 | type="button"
799 | >
800 | {option.label}
801 |
802 | ))}
803 |
804 |
805 | );
806 |
807 | // 根据学位类型和学校类型计算教育系数
808 | const calculateEducationFactor = useCallback(() => {
809 | const degreeType = formData.degreeType;
810 | const schoolType = formData.schoolType;
811 | const bachelorType = formData.bachelorType;
812 |
813 | // 使用更简单的方式计算系数,避免复杂的索引类型问题
814 | let factor = 1.0; // 默认值
815 |
816 | // 专科及以下固定为0.8
817 | if (degreeType === 'belowBachelor') {
818 | factor = 0.8;
819 | }
820 | // 本科学历
821 | else if (degreeType === 'bachelor') {
822 | if (schoolType === 'secondTier') factor = 0.9; // 二本三本
823 | else if (schoolType === 'firstTier') factor = 1.0; // 双非/QS100/USnews50
824 | else if (schoolType === 'elite') factor = 1.2; // 985/211/QS30/USnews20
825 | }
826 | // 硕士学历 - 考虑本科背景
827 | else if (degreeType === 'masters') {
828 | // 先获取本科背景的基础系数
829 | let bachelorBaseCoefficient = 0;
830 | if (bachelorType === 'secondTier') bachelorBaseCoefficient = 0.9; // 二本三本
831 | else if (bachelorType === 'firstTier') bachelorBaseCoefficient = 1.0; // 双非/QS100/USnews50
832 | else if (bachelorType === 'elite') bachelorBaseCoefficient = 1.2; // 985/211/QS30/USnews20
833 |
834 | // 再计算硕士学校的加成系数
835 | let mastersBonus = 0;
836 | if (schoolType === 'secondTier') mastersBonus = 0.4; // 二本三本硕士
837 | else if (schoolType === 'firstTier') mastersBonus = 0.5; // 双非/QS100/USnews50硕士
838 | else if (schoolType === 'elite') mastersBonus = 0.6; // 985/211/QS30/USnews20硕士
839 |
840 | // 最终学历系数 = 本科基础 + 硕士加成
841 | factor = bachelorBaseCoefficient + mastersBonus;
842 | }
843 | // 博士学历
844 | else if (degreeType === 'phd') {
845 | if (schoolType === 'secondTier') factor = 1.6; // 二本三本博士
846 | else if (schoolType === 'firstTier') factor = 1.8; // 双非/QS100/USnews50博士
847 | else if (schoolType === 'elite') factor = 2.0; // 985/211/QS30/USnews20博士
848 | }
849 |
850 | // 更新education字段
851 | if (formData.education !== String(factor)) {
852 | // 这里不使用handleInputChange以避免触发滚动保存/恢复逻辑
853 | setFormData(prev => ({
854 | ...prev,
855 | education: String(factor)
856 | }));
857 | }
858 |
859 | return factor;
860 | }, [formData.degreeType, formData.schoolType, formData.bachelorType, formData.education]);
861 |
862 | // 在组件初始化和学历选择变化时计算教育系数
863 | useEffect(() => {
864 | calculateEducationFactor();
865 | }, [formData.degreeType, formData.schoolType, calculateEducationFactor]);
866 |
867 | // 获取当前选择的国家名称(根据语言)
868 | const getCountryName = useCallback((countryCode: string) => {
869 | if (language === 'en') {
870 | return countryNames.en[countryCode] || countryCode || 'Unknown';
871 | }
872 | if (language === 'ja') {
873 | return countryNames.ja[countryCode] || countryCode || '不明';
874 | }
875 | return countryNames.zh[countryCode] || countryCode || '未知';
876 | }, [language]);
877 |
878 | // 保存当前记录到历史中
879 | const saveToHistory = useCallback(() => {
880 | if (!formData.salary || typeof window === 'undefined') return;
881 |
882 | const newHistoryItem: HistoryItem = {
883 | id: Date.now().toString(),
884 | timestamp: Date.now(),
885 | value: value.toFixed(2),
886 | assessment: getValueAssessmentKey(), // 使用翻译键而不是已翻译的文本
887 | assessmentColor: getValueAssessment().color,
888 | salary: formData.salary,
889 | countryCode: selectedCountry,
890 | countryName: getCountryName(selectedCountry),
891 |
892 | // 添加所有需要在分享页面展示的字段
893 | cityFactor: formData.cityFactor,
894 | workHours: formData.workHours,
895 | commuteHours: formData.commuteHours,
896 | restTime: formData.restTime,
897 | dailySalary: getDisplaySalary(),
898 | workDaysPerYear: calculateWorkingDays().toString(),
899 | workDaysPerWeek: formData.workDaysPerWeek,
900 | wfhDaysPerWeek: formData.wfhDaysPerWeek,
901 | annualLeave: formData.annualLeave,
902 | paidSickLeave: formData.paidSickLeave,
903 | publicHolidays: formData.publicHolidays,
904 | workEnvironment: formData.workEnvironment,
905 | leadership: formData.leadership,
906 | teamwork: formData.teamwork,
907 | degreeType: formData.degreeType,
908 | schoolType: formData.schoolType,
909 | education: formData.education,
910 | homeTown: formData.homeTown,
911 | shuttle: formData.hasShuttle ? formData.shuttle : '1.0',
912 | canteen: formData.hasCanteen ? formData.canteen : '1.0',
913 | workYears: formData.workYears,
914 | jobStability: formData.jobStability,
915 | bachelorType: formData.bachelorType,
916 | hasShuttle: formData.hasShuttle,
917 | hasCanteen: formData.hasCanteen,
918 | };
919 |
920 | try {
921 | const updatedHistory = [newHistoryItem, ...history.slice(0, 9)]; // 限制保存10条记录
922 | setHistory(updatedHistory);
923 | localStorage.setItem('jobValueHistory', JSON.stringify(updatedHistory));
924 | console.log('保存历史记录成功', newHistoryItem);
925 | } catch (e) {
926 | console.error('保存历史记录失败', e);
927 | }
928 |
929 | return newHistoryItem;
930 | }, [formData, value, getValueAssessmentKey, getValueAssessment, selectedCountry, history, getCountryName, calculateWorkingDays, getDisplaySalary, formData.hasShuttle, formData.hasCanteen]);
931 |
932 | // 删除单条历史记录
933 | const deleteHistoryItem = useCallback((id: string, e: React.MouseEvent) => {
934 | e.stopPropagation(); // 阻止事件冒泡
935 | e.preventDefault(); // 阻止默认行为
936 |
937 | try {
938 | const updatedHistory = history.filter(item => item.id !== id);
939 | setHistory(updatedHistory);
940 | localStorage.setItem('jobValueHistory', JSON.stringify(updatedHistory));
941 | console.log('删除历史记录成功', id);
942 | } catch (e) {
943 | console.error('删除历史记录失败', e);
944 | }
945 | }, [history]);
946 |
947 | // 清空所有历史记录
948 | const clearAllHistory = useCallback((e: React.MouseEvent) => {
949 | e.stopPropagation(); // 阻止事件冒泡
950 | e.preventDefault(); // 阻止默认行为
951 |
952 | try {
953 | setHistory([]);
954 | localStorage.removeItem('jobValueHistory');
955 | console.log('清空所有历史记录成功');
956 | } catch (e) {
957 | console.error('清空历史记录失败', e);
958 | }
959 | }, []);
960 |
961 | // 格式化日期
962 | const formatDate = (timestamp: number) => {
963 | const date = new Date(timestamp);
964 | return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
965 | };
966 |
967 | // 获取当前选择国家的货币符号
968 | const getCurrencySymbol = useCallback((countryCode: string) => {
969 | return currencySymbols[countryCode] || '$'; // 如果没有找到对应货币符号,默认使用美元符号
970 | }, []);
971 |
972 | return (
973 |
974 |
975 |
{t('title')}
976 |
977 |
988 |
989 |
990 |
v6.2.1
991 |
997 |
998 | {t('github')}
999 |
1000 |
1006 |
1007 | {t('xiaohongshu')}
1008 |
1009 | {/* 仅在客户端渲染历史记录按钮 */}
1010 | {isBrowser && (
1011 |
setShowHistory(!showHistory)}
1013 | 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"
1014 | >
1015 |
1016 | {t('history')}
1017 |
1018 | )}
1019 |
1020 |
1021 | {/* 历史记录列表 - 仅在客户端渲染 */}
1022 | {isBrowser && showHistory && (
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 | {t('history')}
1030 |
1031 |
1032 | {history.length > 0 && (
1033 |
1037 | {t('clear_all')}
1038 |
1039 | )}
1040 | {
1042 | e.stopPropagation(); // 阻止事件冒泡
1043 | setShowHistory(false);
1044 | }}
1045 | className="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 w-6 h-6 flex items-center justify-center rounded-full hover:bg-gray-100 dark:hover:bg-gray-700"
1046 | >
1047 | ×
1048 |
1049 |
1050 |
1051 |
1052 | {history.length > 0 ? (
1053 |
1054 | {history.map((item) => (
1055 |
1056 |
1057 |
1058 | {item.value}
1059 |
1060 | {item.countryCode !== 'CN' ? '$' : '¥'}{item.salary}
1061 |
1062 |
1063 |
1064 | {formatDate(item.timestamp)}
1065 |
1066 |
1067 |
1068 |
{
1070 | e.stopPropagation(); // 阻止事件冒泡
1071 | e.preventDefault(); // 阻止默认行为
1072 |
1073 | // 恢复历史记录中的值到当前表单
1074 | setFormData({
1075 | ...formData,
1076 | salary: item.salary,
1077 | cityFactor: item.cityFactor,
1078 | workHours: item.workHours,
1079 | commuteHours: item.commuteHours,
1080 | restTime: item.restTime,
1081 | workDaysPerWeek: item.workDaysPerWeek,
1082 | wfhDaysPerWeek: item.wfhDaysPerWeek,
1083 | annualLeave: item.annualLeave,
1084 | paidSickLeave: item.paidSickLeave,
1085 | publicHolidays: item.publicHolidays,
1086 | workEnvironment: item.workEnvironment,
1087 | leadership: item.leadership,
1088 | teamwork: item.teamwork,
1089 | degreeType: item.degreeType,
1090 | schoolType: item.schoolType,
1091 | education: item.education,
1092 | homeTown: item.homeTown,
1093 | shuttle: item.shuttle,
1094 | canteen: item.canteen,
1095 | workYears: item.workYears,
1096 | jobStability: item.jobStability,
1097 | bachelorType: item.bachelorType,
1098 | // 确保 hasShuttle 和 hasCanteen 有合法的布尔值
1099 | hasShuttle: typeof item.hasShuttle === 'boolean' ? item.hasShuttle : false,
1100 | hasCanteen: typeof item.hasCanteen === 'boolean' ? item.hasCanteen : false,
1101 | });
1102 |
1103 | // 设置国家
1104 | handleCountryChange(item.countryCode);
1105 |
1106 | // 关闭历史记录面板
1107 | setShowHistory(false);
1108 | }}
1109 | className="text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 p-1.5 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded"
1110 | title={t('restore_history')}
1111 | >
1112 |
1113 |
1114 |
1115 |
1116 |
1155 |
1156 |
1157 |
deleteHistoryItem(item.id, e)}
1159 | className="text-gray-400 hover:text-red-500 dark:hover:text-red-400 p-1.5 hover:bg-red-50 dark:hover:bg-red-900/20 rounded"
1160 | title={t('delete_history')}
1161 | >
1162 |
1163 |
1164 |
1165 |
1166 |
1167 |
1168 | ))}
1169 |
1170 | ) : (
1171 |
1172 |
1173 |
1174 |
1175 |
1176 |
1177 |
1178 | {t('no_history')}
1179 |
1180 |
1181 | {t('history_notice')}
1182 |
1183 |
1184 | )}
1185 |
1186 |
1187 |
1188 | )}
1189 |
1190 |
1191 |
1192 |
1193 |
1194 | {/* 访问统计 - 仅在客户端渲染 */}
1195 | {isBrowser && (
1196 |
1197 |
1198 | {t('visits')}:
1199 |
1200 |
1201 | {t('visitors')}:
1202 |
1203 |
1204 | )}
1205 |
1206 |
1207 |
1208 |
1209 | {/* 薪资与工作时间 section */}
1210 |
1211 |
1212 |
1213 | {selectedCountry !== 'CN' ?
1214 | `${t('annual_salary')}(${getCurrencySymbol(selectedCountry)})` :
1215 | t('annual_salary_cny')}
1216 |
1217 |
1218 |
1219 | handleInputChange('salary', e.target.value)}
1223 | placeholder={selectedCountry !== 'CN' ?
1224 | `${t('salary_placeholder')} ${getCurrencySymbol(selectedCountry)}` :
1225 | t('salary_placeholder_cny')}
1226 | 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"
1227 | />
1228 |
1229 |
1230 |
1231 |
1232 |
1233 | {t('country_selection')}
1234 |
1235 | ?
1236 |
1237 | {t('ppp_tooltip')}
1238 |
1239 |
1240 |
1241 |
handleCountryChange(e.target.value)}
1246 | className="mt-1 block w-full py-2 px-3 border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
1247 | >
1248 | {Object.keys(pppFactors).sort((a, b) => {
1249 | // 确保中国始终排在第一位
1250 | if (a === 'CN') return -1;
1251 | if (b === 'CN') return 1;
1252 | return new Intl.Collator(['zh', 'ja', 'en']).compare(getCountryName(a), getCountryName(b));
1253 | }).map(code => (
1254 |
1255 | {getCountryName(code)} ({pppFactors[code].toFixed(2)})
1256 |
1257 | ))}
1258 |
1259 |
1260 | {t('selected_ppp')}: {(pppFactors[selectedCountry] || 4.19).toFixed(2)}
1261 |
1262 |
1263 |
1264 |
1295 |
1296 |
1325 |
1326 |
1371 |
1372 |
1373 |
1374 |
1375 | {/* 环境系数 */}
1376 |
1377 | {/* 学历和工作年限 */}
1378 |
1379 |
1380 |
{t('education_level')}
1381 |
1382 |
1383 | {t('degree_type')}
1384 | handleInputChange('degreeType', e.target.value)}
1387 | 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"
1388 | >
1389 | {t('below_bachelor')}
1390 | {t('bachelor')}
1391 | {t('masters')}
1392 | {t('phd')}
1393 |
1394 |
1395 |
1396 | {t('school_type')}
1397 | handleInputChange('schoolType', e.target.value)}
1400 | 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"
1401 | disabled={formData.degreeType === 'belowBachelor'}
1402 | >
1403 | {t('school_second_tier')}
1404 | {formData.degreeType === 'bachelor' ? (
1405 | <>
1406 | {t('school_first_tier_bachelor')}
1407 | {t('school_elite_bachelor')}
1408 | >
1409 | ) : (
1410 | <>
1411 | {t('school_first_tier_higher')}
1412 | {t('school_elite_higher')}
1413 | >
1414 | )}
1415 |
1416 |
1417 |
1418 |
1419 | {/* 硕士显示本科背景选项 */}
1420 | {formData.degreeType === 'masters' && (
1421 |
1422 | {t('bachelor_background')}
1423 | handleInputChange('bachelorType', e.target.value)}
1426 | 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"
1427 | >
1428 | {t('school_second_tier')}
1429 | {t('school_first_tier_bachelor')}
1430 | {t('school_elite_bachelor')}
1431 |
1432 |
1433 | )}
1434 |
1435 |
1436 | {/* 工作年限选择 */}
1437 |
1438 | {t('work_years')}
1439 | handleInputChange('workYears', e.target.value)}
1442 | 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"
1443 | >
1444 | {t('fresh_graduate')}
1445 | {t('years_1_3')}
1446 | {t('years_3_5')}
1447 | {t('years_5_8')}
1448 | {t('years_8_10')}
1449 | {t('years_10_12')}
1450 | {t('years_above_12')}
1451 |
1452 |
1453 |
1454 |
1455 | {/* 添加工作类型RadioGroup */}
1456 |
1470 |
1471 |
1483 |
1484 |
1499 |
1500 |
1510 |
1511 |
1524 |
1525 |
1537 |
1538 | {/* 班车和食堂选项作为加分项,加上勾选框控制 */}
1539 |
1540 |
1541 | handleInputChange('hasShuttle', e.target.checked)}
1546 | className="h-4 w-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500"
1547 | />
1548 |
1549 | {t('shuttle')}
1550 |
1551 |
1552 |
1553 | {formData.hasShuttle && (
1554 |
1566 | )}
1567 |
1568 |
1569 |
1570 |
1571 | handleInputChange('hasCanteen', e.target.checked)}
1576 | className="h-4 w-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500"
1577 | />
1578 |
1579 | {t('canteen')}
1580 |
1581 |
1582 |
1583 | {formData.hasCanteen && (
1584 |
1596 | )}
1597 |
1598 |
1599 |
1600 |
1601 |
1602 | {/* 结果卡片优化 */}
1603 |
1604 |
1605 |
1606 |
{t('working_days_per_year')}
1607 |
{calculateWorkingDays()}{t('days_unit')}
1608 |
1609 |
1610 |
{t('average_daily_salary')}
1611 |
1612 | {getCurrencySymbol(selectedCountry)}{getDisplaySalary()}
1613 |
1614 |
1615 |
1616 |
{t('job_value')}
1617 |
1618 | {value.toFixed(2)}
1619 | ({getValueAssessment().text})
1620 |
1621 |
1622 |
1623 |
1624 | {/* 修改分享按钮为链接到分享页面,并保存到历史 */}
1625 |
1626 | formData.salary ? saveToHistory() : null}
1668 | >
1669 |
1670 | {t('view_report')}
1671 |
1672 |
1673 |
1674 |
1675 | );
1676 | };
1677 |
1678 | export default SalaryCalculator;
--------------------------------------------------------------------------------
/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | experimental: {
6 | reactCompiler: true
7 | }
8 | };
9 |
10 | export default nextConfig;
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "worth-calculator",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@notionhq/client": "^2.3.0",
13 | "@vercel/analytics": "^1.5.0",
14 | "html-to-image": "^1.11.13",
15 | "html2canvas": "^1.4.1",
16 | "lucide-react": "^0.454.0",
17 | "next": "15.0.2",
18 | "qrcode": "^1.5.4",
19 | "react": "19.0.0-rc-02c0e824-20241028",
20 | "react-dom": "19.0.0-rc-02c0e824-20241028"
21 | },
22 | "devDependencies": {
23 | "@types/node": "^20",
24 | "@types/qrcode": "^1.5.5",
25 | "@types/react": "^18",
26 | "@types/react-dom": "^18",
27 | "babel-plugin-react-compiler": "^19.0.0-beta-e993439-20250328",
28 | "eslint": "^8",
29 | "eslint-config-next": "15.0.2",
30 | "postcss": "^8",
31 | "tailwindcss": "^3.4.1",
32 | "typescript": "^5"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/public/favicon.ico
--------------------------------------------------------------------------------
/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/formula.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/public/formula.png
--------------------------------------------------------------------------------
/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/public/title.png
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/website.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/public/website.png
--------------------------------------------------------------------------------
/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | };
19 | export default config;
20 |
--------------------------------------------------------------------------------
/test.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/test.txt
--------------------------------------------------------------------------------
/test/test.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/test/test.txt
--------------------------------------------------------------------------------
/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zippland/worth-calculator/6b1638b24e8674b9457ba0982c202dceb208a3e8/title.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------