├── .prettierignore ├── app ├── robots.txt ├── icon.png ├── opengraph-image.png ├── _themes │ └── orangeTheme │ │ ├── media.css │ │ ├── theme.json │ │ └── theme.css ├── globals.css ├── _lib │ ├── store │ │ ├── useResetModal.ts │ │ ├── useShareModal.ts │ │ └── useBirth.ts │ └── rebirth.ts ├── _components │ ├── reshaped-provider.tsx │ ├── math-component.tsx │ ├── reset.tsx │ ├── ads.tsx │ ├── death-table.tsx │ ├── reset-modal.tsx │ ├── birth-table.tsx │ ├── icon.tsx │ ├── share-barlist.tsx │ ├── gender-icon.tsx │ ├── piechart.tsx │ ├── title.tsx │ ├── navbar.tsx │ ├── barlist.tsx │ ├── result-table.tsx │ ├── world-map.tsx │ ├── share-map.tsx │ ├── map.tsx │ ├── first-time-table.tsx │ ├── calculator.tsx │ └── share-modal.tsx ├── (home) │ ├── about │ │ ├── layout.tsx │ │ └── page.tsx │ ├── data │ │ ├── layout.tsx │ │ └── page.tsx │ ├── probability │ │ ├── layout.tsx │ │ └── page.tsx │ ├── layout.tsx │ └── page.tsx ├── layout.tsx └── _data │ ├── birthrate.json │ ├── world_birthrate.json │ └── birthrate_detailed.json ├── .eslintrc.json ├── bun.lockb ├── public └── ads.txt ├── images └── banner.png ├── .prettierrc.js ├── postcss.config.js ├── next.config.mjs ├── .gitignore ├── README.md ├── tailwind.config.ts ├── reshaped.config.js ├── tsconfig.json └── package.json /.prettierignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules 3 | -------------------------------------------------------------------------------- /app/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hahahumble/rebirth/HEAD/bun.lockb -------------------------------------------------------------------------------- /public/ads.txt: -------------------------------------------------------------------------------- 1 | google.com, pub-1341437621876451, DIRECT, f08c47fec0942fa0 -------------------------------------------------------------------------------- /app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hahahumble/rebirth/HEAD/app/icon.png -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hahahumble/rebirth/HEAD/images/banner.png -------------------------------------------------------------------------------- /app/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hahahumble/rebirth/HEAD/app/opengraph-image.png -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | singleQuote: true, 4 | printWidth: 80, 5 | tabWidth: 2, 6 | trailingComma: 'none', 7 | arrowParens: 'avoid', 8 | }; -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const { config } = require("reshaped/config/postcss"); 2 | 3 | module.exports = { 4 | plugins: { 5 | ...config.plugins, 6 | tailwindcss: {}, 7 | autoprefixer: {}, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /app/_themes/orangeTheme/media.css: -------------------------------------------------------------------------------- 1 | 2 | @custom-media --rs-viewport-m (min-width: 660px); 3 | @custom-media --rs-viewport-l (min-width: 900px); 4 | @custom-media --rs-viewport-xl (min-width: 1280px); 5 | @custom-media --rs-viewport-s (max-width: 659px); 6 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | transpilePackages: ["reshaped"], 4 | experimental: { 5 | optimizePackageImports: ["reshaped"], 6 | }, 7 | }; 8 | 9 | export default nextConfig; 10 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html, body { 6 | height: 100%; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | 11 | #root { 12 | height: 100%; 13 | } 14 | 15 | @layer base { 16 | img { 17 | @apply inline-block; 18 | } 19 | } -------------------------------------------------------------------------------- /app/_lib/store/useResetModal.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | interface ResetModalState { 4 | active: boolean; 5 | activate: () => void; 6 | deactivate: () => void; 7 | } 8 | 9 | const useResetModal = create(set => ({ 10 | active: false, 11 | activate: () => set({ active: true }), 12 | deactivate: () => set({ active: false }) 13 | })); 14 | 15 | export default useResetModal; 16 | -------------------------------------------------------------------------------- /app/_components/reshaped-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { ReactNode } from 'react'; 4 | import { Reshaped, View } from 'reshaped'; 5 | import '../_themes/orangeTheme/theme.css'; 6 | 7 | const ReshapedProvider = ({ children }: { children: ReactNode }) => { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default ReshapedProvider; 16 | -------------------------------------------------------------------------------- /app/_components/math-component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import katex from 'katex'; 3 | import 'katex/dist/katex.min.css'; 4 | 5 | interface MathComponentProps { 6 | formula: string; 7 | } 8 | 9 | const MathComponent: React.FC = ({ formula }) => { 10 | const html = katex.renderToString(formula, { 11 | throwOnError: false 12 | }); 13 | 14 | return
; 15 | }; 16 | 17 | export default MathComponent; 18 | -------------------------------------------------------------------------------- /app/(home)/about/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Container } from 'reshaped'; 2 | import React from 'react'; 3 | import type { Metadata } from 'next'; 4 | 5 | export const metadata: Metadata = { 6 | title: '关于 - 投胎模拟器「中国版」', 7 | description: '如果来世还在种花家,你会出生在哪里? - 投胎模拟器「中国版」' 8 | }; 9 | 10 | export default function AboutLayout({ 11 | children 12 | }: Readonly<{ 13 | children: React.ReactNode; 14 | }>) { 15 | return ( 16 | <> 17 | {children} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /app/(home)/data/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Container } from 'reshaped'; 2 | import React from 'react'; 3 | import type { Metadata } from 'next'; 4 | 5 | export const metadata: Metadata = { 6 | title: '数据来源 - 投胎模拟器「中国版」', 7 | description: '如果来世还在种花家,你会出生在哪里? - 投胎模拟器「中国版」' 8 | }; 9 | 10 | export default function DataLayout({ 11 | children 12 | }: Readonly<{ 13 | children: React.ReactNode; 14 | }>) { 15 | return ( 16 | <> 17 | {children} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /app/(home)/probability/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Container } from 'reshaped'; 2 | import React from 'react'; 3 | import type { Metadata } from 'next'; 4 | 5 | export const metadata: Metadata = { 6 | title: '概率计算器 - 投胎模拟器「中国版」', 7 | description: '如果来世还在种花家,你会出生在哪里? - 投胎模拟器「中国版」' 8 | }; 9 | 10 | export default function DataLayout({ 11 | children 12 | }: Readonly<{ 13 | children: React.ReactNode; 14 | }>) { 15 | return ( 16 | <> 17 | {children} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /.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.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # idea 39 | .idea 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![banner.png](images%2Fbanner.png) 2 | 3 | **项目网站**:https://toutai.cc/ 4 | 5 | 本项目根据公布的最新出生人口数据,同时包括了性别,计算出生在某地区的可能性。 6 | 7 | ### 数据来源 8 | 9 | - 中国大陆:[第七次人口普查](https://www.stats.gov.cn/sj/pcsj/rkpc/7rp/zk/indexch.htm)(2019.11.1 - 2020.10.31) 10 | - 香港特别行政区:[香港政府统计处](https://www.censtatd.gov.hk/tc/web_table.html?id=3)(2023) 11 | - 澳门特别行政区:[统计暨普查局](https://www.censtatd.gov.hk/tc/web_table.html?id=3)(2023) 12 | - 台湾地区:[人口统计资料](https://www.ris.gov.tw/app/portal/346)(2023) 13 | 14 | ### 开发 15 | 16 | - [Next.js](https://nextjs.org/) 17 | - [TypeScript](https://www.typescriptlang.org/) 18 | - [Bun](https://bun.sh/) 19 | 20 | ### 致谢 21 | 22 | https://uahh.site/reborn 23 | -------------------------------------------------------------------------------- /app/_components/reset.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { Button, Text, View } from 'reshaped'; 5 | import useResetModal from '@/lib/store/useResetModal'; 6 | 7 | function Reset() { 8 | const { activate } = useResetModal(); 9 | 10 | return ( 11 | 12 | 13 | 重置数据 14 | 15 | 此操作将清空所有数据,不可恢复 16 | 17 | 18 | 21 | 22 | ); 23 | } 24 | 25 | export default Reset; 26 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | const { getTheme } = require("reshaped/config/tailwind"); 3 | const reshapedConfig = require("./reshaped.config.js"); 4 | 5 | const config: Config = { 6 | content: [ 7 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 8 | './components/**/*.{js,ts,jsx,tsx,mdx}', 9 | './app/**/*.{js,ts,jsx,tsx,mdx}' 10 | ], 11 | theme: { 12 | extend: { 13 | ...getTheme(reshapedConfig), 14 | backgroundImage: { 15 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 16 | 'gradient-conic': 17 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))' 18 | } 19 | }, 20 | }, 21 | plugins: [] 22 | }; 23 | export default config; 24 | -------------------------------------------------------------------------------- /app/_components/ads.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | 3 | declare global { 4 | interface Window { 5 | adsbygoogle: any; 6 | } 7 | } 8 | 9 | const Ads = () => { 10 | useEffect(() => { 11 | try { 12 | (window.adsbygoogle = window.adsbygoogle || []).push({}); 13 | } catch (err) { 14 | console.log(err); 15 | } 16 | }, []); 17 | 18 | return ( 19 | 31 | ); 32 | }; 33 | export default Ads; 34 | -------------------------------------------------------------------------------- /app/(home)/probability/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text, View } from 'reshaped'; 3 | import Calculator from '@/components/calculator'; 4 | 5 | function Page() { 6 | return ( 7 | <> 8 | 9 | 15 | 16 | 概率计算器 17 | 18 | 19 | 根据真实的出生人口数据,计算投胎的可能性 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default Page; 31 | -------------------------------------------------------------------------------- /reshaped.config.js: -------------------------------------------------------------------------------- 1 | const { generateThemeColors } = require('reshaped/themes'); 2 | 3 | const config = { 4 | themes: { 5 | orangeTheme: { 6 | color: { 7 | ...generateThemeColors({ 8 | primary: '#ff4f04', 9 | critical: '#ff2732', 10 | positive: '#00ca78', 11 | neutral: '#dbd8d5' 12 | }), 13 | backgroundPage: { hex: "#f5f3ef" }, 14 | }, 15 | unit: { 16 | radiusSmall: { 17 | px: 5 18 | } 19 | }, 20 | shadow: { 21 | overlay: [ 22 | { 23 | offsetX: 0, 24 | offsetY: 1, 25 | blurRadius: 3, 26 | colorToken: 'black', 27 | opacity: 0.1 28 | } 29 | ] 30 | } 31 | } 32 | } 33 | }; 34 | 35 | module.exports = config; 36 | -------------------------------------------------------------------------------- /app/_lib/store/useShareModal.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | export interface ShareInfo { 4 | count: number; 5 | region: string; 6 | category: string; 7 | gender: string; 8 | order: string; 9 | probability: number; 10 | } 11 | 12 | interface ShareModalState { 13 | active: boolean; 14 | shareInfo: ShareInfo; 15 | activate: () => void; 16 | deactivate: () => void; 17 | setShareInfo: (info: ShareInfo) => void; 18 | } 19 | 20 | const useShareModal = create(set => ({ 21 | active: false, 22 | shareInfo: { 23 | count: 0, 24 | region: '', 25 | category: '', 26 | gender: '', 27 | order: '', 28 | probability: 0 29 | }, 30 | activate: () => set({ active: true }), 31 | deactivate: () => set({ active: false }), 32 | setShareInfo: (info: ShareInfo) => set({ shareInfo: info }) 33 | })); 34 | 35 | export default useShareModal; 36 | -------------------------------------------------------------------------------- /app/(home)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Container, View } from 'reshaped'; 2 | import React from 'react'; 3 | import type { Metadata } from 'next'; 4 | import Title from '@/components/title'; 5 | import Navbar from '@/components/navbar'; 6 | import ResetModal from '@/components/reset-modal'; 7 | import ShareModal from '@/components/share-modal'; 8 | 9 | export const metadata: Metadata = { 10 | title: '投胎模拟器「中国版」', 11 | description: '如果来世还在种花家,你会出生在哪里? - 投胎模拟器「中国版」' 12 | }; 13 | 14 | export default function HomepageLayout({ 15 | children 16 | }: Readonly<{ 17 | children: React.ReactNode; 18 | }>) { 19 | return ( 20 | <> 21 | 22 | 23 | 24 | 31 | 32 | <Navbar /> 33 | </View> 34 | {children} 35 | </Container> 36 | </> 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Inter } from 'next/font/google'; 2 | import './globals.css'; 3 | import './_themes/orangeTheme/theme.css'; 4 | import ReshapedProvider from '@/components/reshaped-provider'; 5 | import React from 'react'; 6 | import { Toaster } from 'sonner'; 7 | import Script from 'next/script'; 8 | import { Analytics } from '@vercel/analytics/next'; 9 | 10 | const inter = Inter({ subsets: ['latin'] }); 11 | 12 | export default function RootLayout({ 13 | children 14 | }: Readonly<{ 15 | children: React.ReactNode; 16 | }>) { 17 | return ( 18 | <html lang="en" data-rs-theme="orangeTheme" data-rs-color-mode="light"> 19 | <body className={inter.className}> 20 | <Toaster position="bottom-center" /> 21 | <ReshapedProvider>{children}</ReshapedProvider> 22 | <Analytics /> 23 | </body> 24 | <Script 25 | async 26 | src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1341437621876451" 27 | crossOrigin="anonymous" 28 | /> 29 | </html> 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "dom", 5 | "dom.iterable", 6 | "esnext" 7 | ], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "esModuleInterop": true, 13 | "module": "esnext", 14 | "moduleResolution": "bundler", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "preserve", 18 | "incremental": true, 19 | "baseUrl": "./app", 20 | "plugins": [ 21 | { 22 | "name": "next" 23 | } 24 | ], 25 | "paths": { 26 | "@/*": [ 27 | "./*" 28 | ], 29 | "@/data/*": [ 30 | "_data/*" 31 | ], 32 | "@/lib/*": [ 33 | "_lib/*" 34 | ], 35 | "@/components/*": [ 36 | "_components/*" 37 | ], 38 | "@/themes/*": [ 39 | "_themes/*" 40 | ] 41 | } 42 | }, 43 | "include": [ 44 | "next-env.d.ts", 45 | "**/*.ts", 46 | "**/*.tsx", 47 | ".next/types/**/*.ts" 48 | ], 49 | "exclude": [ 50 | "node_modules" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rebirth", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "build:themes": "reshaped theming --output app/_themes" 11 | }, 12 | "dependencies": { 13 | "@types/katex": "^0.16.7", 14 | "@vercel/analytics": "^1.5.0", 15 | "echarts": "^5.5.0", 16 | "html2canvas": "^1.4.1", 17 | "katex": "^0.16.9", 18 | "lucide-react": "^0.363.0", 19 | "next": "14.2.35", 20 | "prettier": "^3.2.5", 21 | "react": "^18", 22 | "react-dom": "^18", 23 | "react-minimal-pie-chart": "^8.4.0", 24 | "react-qr-code": "^2.0.12", 25 | "reshaped": "^2.10.3", 26 | "sonner": "^1.4.41", 27 | "zustand": "^4.5.2" 28 | }, 29 | "devDependencies": { 30 | "typescript": "^5", 31 | "@types/node": "^20", 32 | "@types/react": "^18", 33 | "@types/react-dom": "^18", 34 | "autoprefixer": "^10.0.1", 35 | "postcss": "^8", 36 | "tailwindcss": "^3.3.0", 37 | "eslint": "^8", 38 | "eslint-config-next": "14.1.4" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/_components/death-table.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Actionable, Table, Text, View } from 'reshaped'; 3 | 4 | function DeathTable() { 5 | return ( 6 | <View gap={4}> 7 | <View backgroundColor="neutral-faded" className="rounded-xl"> 8 | <Table border columnBorder> 9 | <Table.Row highlighted> 10 | <Table.Heading padding={1.5}> 11 | <Text align="center">新生儿死亡率</Text> 12 | </Table.Heading> 13 | </Table.Row> 14 | <Table.Row> 15 | <Table.Cell padding={1}> 16 | <View justify="center"> 17 | <Actionable href="https://www.stats.gov.cn/sj/zxfb/202304/t20230417_1938688.html"> 18 | <Text 19 | align="center" 20 | className="underline hover:underline hover:text-primary hover:cursor-pointer" 21 | > 22 | 3.1‰ 23 | </Text> 24 | </Actionable> 25 | </View> 26 | </Table.Cell> 27 | </Table.Row> 28 | </Table> 29 | </View> 30 | </View> 31 | ); 32 | } 33 | 34 | export default DeathTable; 35 | -------------------------------------------------------------------------------- /app/_lib/store/useBirth.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | import { persist, createJSONStorage } from 'zustand/middleware'; 3 | import { BirthResult } from '@/lib/rebirth'; 4 | 5 | interface BirthState { 6 | birthResults: BirthResult[]; 7 | addBirthResult: (result: BirthResult) => void; 8 | getLatestBirthResult: () => BirthResult | null; 9 | getBirthResultsCount: () => number; 10 | clearBirthResults: () => void; 11 | } 12 | 13 | export const useBirth = create<BirthState>()( 14 | persist( 15 | (set, get) => ({ 16 | birthResults: [], 17 | addBirthResult: (result: BirthResult) => 18 | set((state: BirthState) => ({ 19 | birthResults: [...state.birthResults, result] 20 | })), 21 | getLatestBirthResult: () => { 22 | const birthResults = get().birthResults; 23 | return birthResults.length > 0 24 | ? birthResults[birthResults.length - 1] 25 | : null; 26 | }, 27 | getBirthResultsCount: () => { 28 | return get().birthResults.length; 29 | }, 30 | clearBirthResults: () => { 31 | set({ birthResults: [] }); 32 | } 33 | }), 34 | { 35 | name: 'birth-storage', 36 | skipHydration: true, 37 | storage: createJSONStorage(() => localStorage) 38 | } 39 | ) 40 | ); 41 | -------------------------------------------------------------------------------- /app/_components/reset-modal.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { Button, Dismissible, Modal, Text, View } from 'reshaped'; 5 | import useResetModal from '@/lib/store/useResetModal'; 6 | import { useBirth } from '@/lib/store/useBirth'; 7 | 8 | function ResetModal() { 9 | const { active, deactivate } = useResetModal(); 10 | const { clearBirthResults } = useBirth(state => ({ 11 | clearBirthResults: state.clearBirthResults 12 | })); 13 | 14 | function handleReset() { 15 | clearBirthResults(); 16 | deactivate(); 17 | } 18 | 19 | return ( 20 | <Modal active={active} onClose={deactivate}> 21 | <View gap={3}> 22 | <Dismissible onClose={deactivate} closeAriaLabel="Close modal"> 23 | <Modal.Title>确定要重置数据?</Modal.Title> 24 | <Modal.Subtitle>此操作将清空所有数据,不可恢复</Modal.Subtitle> 25 | </Dismissible> 26 | <View gap={2} direction="row"> 27 | <View.Item columns={6}> 28 | <Button 29 | color="primary" 30 | variant="faded" 31 | fullWidth 32 | onClick={deactivate} 33 | > 34 | 取消 35 | </Button> 36 | </View.Item> 37 | <View.Item columns={6}> 38 | <Button 39 | color="critical" 40 | variant="faded" 41 | fullWidth 42 | onClick={handleReset} 43 | > 44 | 重置 45 | </Button> 46 | </View.Item> 47 | </View> 48 | </View> 49 | </Modal> 50 | ); 51 | } 52 | 53 | export default ResetModal; 54 | -------------------------------------------------------------------------------- /app/_components/birth-table.tsx: -------------------------------------------------------------------------------- 1 | import { Table, Text, View } from 'reshaped'; 2 | import { regions } from '@/lib/rebirth'; 3 | 4 | function BirthTable() { 5 | return ( 6 | <View gap={4}> 7 | <View backgroundColor="neutral-faded" className="rounded-xl"> 8 | <Table border columnBorder> 9 | <Table.Row highlighted> 10 | <Table.Heading padding={1.5}> 11 | <Text align="center">省份/地区</Text> 12 | </Table.Heading> 13 | <Table.Heading padding={1.5}> 14 | <Text align="center">男孩</Text> 15 | </Table.Heading> 16 | <Table.Heading padding={1.5}> 17 | <Text align="center">女孩</Text> 18 | </Table.Heading> 19 | <Table.Heading padding={1.5}> 20 | <Text align="center">合计</Text> 21 | </Table.Heading> 22 | </Table.Row> 23 | {regions.map((item, index) => ( 24 | <Table.Row key={index}> 25 | <Table.Cell padding={1}> 26 | <Text align="center">{item.name}</Text> 27 | </Table.Cell> 28 | <Table.Cell padding={1}> 29 | <Text align="center">{item.male}</Text> 30 | </Table.Cell> 31 | <Table.Cell padding={1}> 32 | <Text align="center">{item.female}</Text> 33 | </Table.Cell> 34 | <Table.Cell padding={1}> 35 | <Text align="center">{item.total}</Text> 36 | </Table.Cell> 37 | </Table.Row> 38 | ))} 39 | </Table> 40 | </View> 41 | </View> 42 | ); 43 | } 44 | 45 | export default BirthTable; 46 | -------------------------------------------------------------------------------- /app/(home)/data/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Actionable, Text, View } from 'reshaped'; 3 | import BirthTable from '@/components/birth-table'; 4 | import DeathTable from '@/components/death-table'; 5 | 6 | function Page() { 7 | return ( 8 | <> 9 | <View as="main"> 10 | <View paddingBottom={12} paddingTop={24} as="header"> 11 | <Text weight="medium" variant="featured-3" as="h1"> 12 | 数据来源 13 | </Text> 14 | <Text variant="body-2" color="neutral-faded" as="h2"> 15 | 中国大陆: 16 | <Actionable 17 | className="underline hover:underline hover:text-primary hover:cursor-pointer" 18 | href="https://www.stats.gov.cn/sj/pcsj/rkpc/7rp/zk/indexch.htm" 19 | > 20 | 第七次人口普查 21 | </Actionable> 22 | (2019.11.1 - 2020.10.31);香港特别行政区: 23 | <Actionable 24 | className="underline hover:underline hover:text-primary hover:cursor-pointer" 25 | href="https://www.censtatd.gov.hk/tc/web_table.html?id=3" 26 | > 27 | 香港政府统计处 28 | </Actionable> 29 | (2023);澳门特别行政区: 30 | <Actionable 31 | className="underline hover:underline hover:text-primary hover:cursor-pointer" 32 | href="https://www.dsec.gov.mo/zh-MO/Statistic?id=101" 33 | > 34 | 统计暨普查局 35 | </Actionable> 36 | (2023);台湾地区: 37 | <Actionable 38 | className="underline hover:underline hover:text-primary hover:cursor-pointer" 39 | href="https://www.ris.gov.tw/app/portal/346" 40 | > 41 | 人口统计资料 42 | </Actionable> 43 | (2023) 44 | </Text> 45 | </View> 46 | <View paddingTop={12} as="article"> 47 | <BirthTable /> 48 | </View> 49 | <View paddingTop={6} paddingBottom={8} as="article"> 50 | <DeathTable /> 51 | </View> 52 | </View> 53 | </> 54 | ); 55 | } 56 | 57 | export default Page; 58 | -------------------------------------------------------------------------------- /app/(home)/about/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Actionable, Divider, Text, View } from 'reshaped'; 3 | import MathComponent from '@/components/math-component'; 4 | import Reset from '@/components/reset'; 5 | 6 | function Page() { 7 | return ( 8 | <> 9 | <View as="main"> 10 | <View 11 | paddingBottom={12} 12 | paddingTop={24} 13 | width={{ s: '100%', m: 130 }} 14 | as="header" 15 | > 16 | <Text weight="medium" variant="featured-3" as="h1"> 17 | 关于 18 | </Text> 19 | <Text variant="body-2" color="neutral-faded" as="h2"> 20 | 关于本网站 21 | </Text> 22 | </View> 23 | <View as="article"> 24 | <View paddingBlock={4}> 25 | <View direction="column"> 26 | <Text variant="body-2">如果来世还在种花家,你会出生在哪里?</Text> 27 | <View gap={2} direction="column" paddingBottom={2}> 28 | <Text variant="body-2"> 29 | 本网站根据中国公布的最新出生人口数据,计算出生在某地区的可能性。 30 | 使用了以下公式计算出生在某地区的可能性: 31 | </Text> 32 | <MathComponent formula="\displaystyle{\text{出生在该地区的可能性} = \frac{该地区出生人口}{全国总出生人口}}" /> 33 | </View> 34 | <Text variant="body-2"> 35 | 参考项目: 36 | <Actionable 37 | className="underline hover:underline hover:text-primary hover:cursor-pointer" 38 | href="https://uahh.site/reborn" 39 | > 40 | https://uahh.site/reborn 41 | </Actionable> 42 | </Text> 43 | <Text variant="body-2"> 44 | 项目 GitHub 链接: 45 | <Actionable 46 | className="underline hover:underline hover:text-primary hover:cursor-pointer" 47 | href="https://github.com/hahahumble/rebirth" 48 | > 49 | 投胎模拟器 50 | </Actionable> 51 | </Text> 52 | </View> 53 | </View> 54 | <View paddingBlock={8}> 55 | <Divider /> 56 | </View> 57 | <Reset /> 58 | </View> 59 | </View> 60 | </> 61 | ); 62 | } 63 | 64 | export default Page; 65 | -------------------------------------------------------------------------------- /app/_components/icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function ChinaMap({ size = 24, color = '#DBD8D5' }) { 4 | return ( 5 | <svg 6 | width={size} 7 | height={0.93 * size} 8 | viewBox="0 0 572 532" 9 | fill="none" 10 | xmlns="http://www.w3.org/2000/svg" 11 | > 12 | <path 13 | d="M0 157.793V207.103L39.4483 246.552L19.7241 276.138L118.345 355.034L167.655 364.897L236.69 355.034V384.621L226.828 414.207L276.138 453.655L325.448 414.207L374.759 453.655L473.379 384.621L512.828 305.724L473.379 256.414L493.103 216.966L453.655 226.828L433.931 197.241L473.379 167.655L493.103 177.517L562.138 118.345L552.276 98.6207L572 88.7586V39.4483L542.414 59.1724L522.69 39.4483H502.966L483.241 0H433.931V59.1724H404.345V88.7586L443.793 98.6207L394.483 128.207H374.759L364.897 157.793L315.586 177.517L226.828 157.793L216.966 128.207L177.517 118.345V88.7586L157.793 69.0345L78.8966 108.483V147.931L0 157.793ZM373.544 484.456L358.145 499.855C356.441 501.559 355.09 503.581 354.168 505.807C353.246 508.033 352.771 510.418 352.771 512.828C352.771 515.237 353.246 517.622 354.168 519.848C355.09 522.074 356.441 524.096 358.145 525.8C360.116 527.771 362.511 529.267 365.147 530.174C367.783 531.082 370.591 531.376 373.358 531.035C376.125 530.695 378.778 529.728 381.115 528.208C383.453 526.688 385.413 524.656 386.847 522.266L398.053 503.591C399.85 500.595 400.595 497.085 400.168 493.618C399.741 490.151 398.167 486.926 395.697 484.456C394.242 483.001 392.516 481.847 390.615 481.06C388.715 480.273 386.678 479.867 384.621 479.867C382.564 479.867 380.527 480.273 378.626 481.06C376.726 481.847 374.999 483.001 373.544 484.456ZM509.096 399.546C507.226 407.025 507.325 414.861 509.383 422.291C511.441 429.721 515.388 436.491 520.839 441.943L524.128 445.232C526.659 447.763 529.845 449.539 533.329 450.361C536.813 451.184 540.457 451.02 543.852 449.888C546.254 449.088 548.474 447.822 550.386 446.164C552.298 444.505 553.865 442.487 554.997 440.223C556.129 437.959 556.804 435.494 556.984 432.969C557.163 430.444 556.844 427.909 556.043 425.508L546.905 398.094C545.597 394.17 543.088 390.758 539.733 388.34C536.378 385.922 532.348 384.621 528.212 384.621C523.818 384.621 519.55 386.089 516.086 388.794C512.622 391.498 510.162 395.283 509.096 399.546Z" 14 | fill="#DBD8D5" 15 | fillOpacity="0.5" 16 | /> 17 | </svg> 18 | ); 19 | } 20 | 21 | export default ChinaMap; 22 | -------------------------------------------------------------------------------- /app/_components/share-barlist.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text } from 'reshaped'; 2 | import React, { useMemo } from 'react'; 3 | import { useBirth } from '@/lib/store/useBirth'; 4 | import { BirthResult } from '@/lib/rebirth'; 5 | 6 | interface ProvinceData { 7 | province: string; 8 | probability: number; 9 | count: number; 10 | } 11 | 12 | const BarList: React.FC = () => { 13 | const birthResults = useBirth( 14 | (state: { birthResults: BirthResult[] }) => state.birthResults 15 | ); 16 | 17 | const provinceOccurrences = useMemo(() => { 18 | const occurrences: Record<string, number> = birthResults.reduce( 19 | (acc: Record<string, number>, result) => { 20 | acc[result.province] = (acc[result.province] || 0) + 1; 21 | return acc; 22 | }, 23 | {} as Record<string, number> 24 | ); 25 | 26 | return occurrences; 27 | }, [birthResults]); 28 | 29 | const totalBirthResults = birthResults.length; 30 | 31 | const sortedResults: ProvinceData[] = useMemo(() => { 32 | const results = Object.entries(provinceOccurrences) 33 | .map(([province, count]) => ({ 34 | province, 35 | probability: count / totalBirthResults, 36 | count 37 | })) 38 | .sort((a, b) => b.probability - a.probability) 39 | .slice(0, 5); 40 | 41 | return results; 42 | }, [provinceOccurrences, totalBirthResults]); 43 | 44 | const maxProbability = useMemo(() => { 45 | return sortedResults.length > 0 ? sortedResults[0].probability : 0; 46 | }, [sortedResults]); 47 | 48 | return ( 49 | <View direction="column" gap={1}> 50 | {sortedResults.map((item: ProvinceData) => ( 51 | <View key={item.province} direction="row" align="center" gap={2}> 52 | <View width={12}> 53 | <Text>{item.province}</Text> 54 | </View> 55 | <div 56 | className="bg-[#00ca78] h-8 rounded-xl" 57 | style={{ 58 | width: `${(item.probability / maxProbability) * 60}%` 59 | }} 60 | > 61 | <Text 62 | variant="body-3" 63 | className="absolute right-2 top-1/2 transform -translate-y-1/2" 64 | > 65 | {(item.probability * 100).toFixed(2)}% 66 | </Text> 67 | </div> 68 | </View> 69 | ))} 70 | </View> 71 | ); 72 | }; 73 | 74 | export default BarList; 75 | -------------------------------------------------------------------------------- /app/_components/gender-icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function MaleIcon({ size = 24 }) { 4 | return ( 5 | <svg 6 | width={size} 7 | height={size} 8 | viewBox="0 0 512 512" 9 | fill="none" 10 | xmlns="http://www.w3.org/2000/svg" 11 | > 12 | <path 13 | d="M435.6 130.4C434.1 131.6 433.2 132.2 432.5 133C399.8 165.7 367.1 198.3 334.4 231C330.9 234.5 330.9 236.7 333.3 240.9C359.6 285.6 366.1 333.4 351.3 382.8C334.3 439.6 297.4 479.3 241.7 500.1C191.9 518.8 142.5 515.4 95.7 490.5C47.7 465 17.2 424.7 5.10003 371.5C-5.19997 326 1.60003 282.6 25.1 242.3C51.8 196.5 91.7 167.9 143.3 156.9C187.9 147.5 230.5 154.6 270 177.9C275.3 181 278 180.5 282.2 176.3C313.6 144.5 345.2 113 376.8 81.5C378.3 80 379.7 78.5 381.9 76.1C379.3 75.9 377.8 75.8 376.4 75.8C358.2 75.7 339.9 76.1 321.8 75.4C300.9 74.6 285.8 57.9 286.1 37.2C286.4 16.8 302.9 0.299974 323.3 0.199974C345.8 -0.100026 368.3 0.0999741 390.9 0.0999741H469.6C496.5 0.0999741 511.9 15.5 511.9 42.4V185.8C511.9 210 495.2 226.6 471.8 226C453.8 225.6 437.2 210.1 436.6 192.1C435.9 173.5 436.3 154.9 436.2 136.3C435.9 134.6 435.7 132.9 435.6 130.4ZM178.6 435.8C235.1 436.5 281.6 390.7 282.7 333.4C283.8 277 237.2 229.7 179.9 229.1C123.2 228.5 76.6 274.9 76.1 332.4C75.7 388.4 121.9 435 178.6 435.8Z" 14 | fill="#43BCFF" 15 | /> 16 | </svg> 17 | ); 18 | } 19 | 20 | export function FemaleIcon({ size = 24 }) { 21 | return ( 22 | <svg 23 | height={size} 24 | viewBox="0 0 297 512" 25 | fill="none" 26 | xmlns="http://www.w3.org/2000/svg" 27 | > 28 | <path 29 | d="M226.24 355.44H182.32V292C247.44 276.48 296.08 217.92 296.08 148.08C296.16 66.4 229.68 0 148.08 0C66.48 0 0 66.4 0 148.08C0 217.92 48.64 276.48 113.76 292V355.44H69.84C50.96 355.44 35.6 370.8 35.6 389.68C35.6 408.56 50.96 423.92 69.84 423.92H113.76V477.68C113.76 496.56 129.12 511.92 148 511.92C166.88 511.92 182.24 496.56 182.24 477.68V423.92H226.16C245.04 423.92 260.4 408.56 260.4 389.68C260.411 385.187 259.535 380.737 257.823 376.583C256.111 372.429 253.596 368.654 250.423 365.474C247.25 362.293 243.481 359.77 239.331 358.048C235.181 356.326 230.733 355.44 226.24 355.44ZM68.56 148.08C68.56 104.24 104.24 68.56 148.08 68.56C191.92 68.56 227.6 104.24 227.6 148.08C227.6 191.92 191.92 227.6 148.08 227.6C104.24 227.6 68.56 191.92 68.56 148.08Z" 30 | fill="#FC7CB4" 31 | /> 32 | </svg> 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /app/_components/piechart.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | 3 | import { PieChart } from 'react-minimal-pie-chart'; 4 | import { useBirth } from '@/lib/store/useBirth'; 5 | import { BirthResult } from '@/lib/rebirth'; 6 | import { View, Text } from 'reshaped'; 7 | 8 | function Piechart() { 9 | const birthResults = useBirth( 10 | (state: { birthResults: BirthResult[] }) => state.birthResults 11 | ); 12 | 13 | // Calculate the gender counts 14 | const genderCounts = useMemo(() => { 15 | const counts = { male: 0, female: 0 }; 16 | 17 | birthResults.forEach(result => { 18 | if (result.gender === 'male') { 19 | counts.male += 1; 20 | } else if (result.gender === 'female') { 21 | counts.female += 1; 22 | } 23 | }); 24 | 25 | return counts; 26 | }, [birthResults]); 27 | 28 | // Create the data array for the PieChart 29 | const datas = useMemo( 30 | () => [ 31 | { title: '男孩', value: genderCounts.male, color: '#ff4f04' }, 32 | { title: '女孩', value: genderCounts.female, color: '#00ca78' } 33 | ], 34 | [genderCounts] 35 | ); 36 | 37 | const lineWidth = 60; 38 | 39 | return ( 40 | <> 41 | <View direction="row" justify="center" align="end" gap={{ s: 8, l: 16 }}> 42 | <PieChart 43 | className="w-52 sm:w-72" 44 | style={{ 45 | fontSize: '7px' 46 | }} 47 | data={datas} 48 | radius={48} 49 | lineWidth={lineWidth} 50 | segmentsStyle={{ transition: 'stroke .3s' }} 51 | segmentsShift={1} 52 | animate 53 | label={({ dataEntry }) => Math.round(dataEntry.percentage) + '%'} 54 | labelPosition={100 - lineWidth / 2} 55 | labelStyle={{ 56 | fill: '#fff', 57 | opacity: 0.75, 58 | pointerEvents: 'none' 59 | }} 60 | /> 61 | <View direction="column" gap={2} paddingBottom={4} width={28}> 62 | <View direction="row" gap={1} align="center"> 63 | <div className="w-4 h-4 bg-[#ff4f04] rounded-full" /> 64 | <Text>男孩:{genderCounts.male}</Text> 65 | </View> 66 | <View direction="row" gap={1} align="center"> 67 | <div className="w-4 h-4 bg-[#00ca78] rounded-full" /> 68 | <Text>女孩:{genderCounts.female}</Text> 69 | </View> 70 | </View> 71 | </View> 72 | </> 73 | ); 74 | } 75 | 76 | export default Piechart; 77 | -------------------------------------------------------------------------------- /app/_components/title.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Actionable, Text, View } from 'reshaped'; 3 | 4 | export function CarrotIcon({ size = 28, color = '#FF4F04' }) { 5 | return ( 6 | <svg 7 | width={size} 8 | height={size} 9 | viewBox="0 0 64 64" 10 | fill="none" 11 | xmlns="http://www.w3.org/2000/svg" 12 | > 13 | <rect width="64" height="64" rx="10" fill={color} /> 14 | <path 15 | d="M24.16 36.6667L19.3767 31.9067M39.7933 39L34.0533 33.26M9.29666 54.6333C9.29666 54.6333 32.3267 46.4667 39 39.7933C39.9759 38.8189 40.7504 37.6618 41.2792 36.3881C41.8079 35.1144 42.0806 33.749 42.0817 32.3699C42.0828 30.9908 41.8122 29.625 41.2855 28.3505C40.7587 27.0759 39.9861 25.9176 39.0117 24.9417C38.0373 23.9657 36.8802 23.1913 35.6065 22.6625C34.3327 22.1337 32.9674 21.861 31.5882 21.86C30.2091 21.8589 28.8433 22.1294 27.5688 22.6562C26.2942 23.183 25.1359 23.9556 24.16 24.93C17.4633 31.6267 9.29666 54.6333 9.29666 54.6333Z" 16 | stroke="white" 17 | strokeWidth="4" 18 | strokeLinecap="round" 19 | strokeLinejoin="round" 20 | /> 21 | <path 22 | d="M55.3333 25C55.3333 25 52.23 20.3333 47.1667 20.3333C43.34 20.3333 39 25 39 25C39 25 42.1033 29.6667 47.1667 29.6667C52.23 29.6667 55.3333 25 55.3333 25Z" 23 | stroke="white" 24 | strokeWidth="4" 25 | strokeLinecap="round" 26 | strokeLinejoin="round" 27 | /> 28 | <path 29 | d="M39 8.66666C39 8.66666 34.3333 11.77 34.3333 16.8333C34.3333 21.8967 39 25 39 25C39 25 43.6667 20.7067 43.6667 16.8333C43.6667 11.77 39 8.66666 39 8.66666Z" 30 | stroke="white" 31 | strokeWidth="4" 32 | strokeLinecap="round" 33 | strokeLinejoin="round" 34 | /> 35 | </svg> 36 | ); 37 | } 38 | 39 | function Title() { 40 | return ( 41 | <> 42 | <View direction="row" align="center" gap={2}> 43 | <Actionable href="/"> 44 | <View direction="row" align="center" gap={2}> 45 | <CarrotIcon /> 46 | <Text variant="body-1" weight="medium"> 47 | 投胎模拟器 48 | </Text> 49 | </View> 50 | </Actionable> 51 | <div className="bg-[#01ca78] px-2 py-1 rounded-xl hover:cursor-default"> 52 | <Text className="text-white" weight="medium" variant="caption-1"> 53 | 中国版 54 | </Text> 55 | </div> 56 | </View> 57 | </> 58 | ); 59 | } 60 | 61 | export default Title; 62 | -------------------------------------------------------------------------------- /app/_components/navbar.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { 5 | Actionable, 6 | View, 7 | Text, 8 | Popover, 9 | Button, 10 | Icon, 11 | Hidden, 12 | MenuItem 13 | } from 'reshaped'; 14 | import { Menu } from 'lucide-react'; 15 | 16 | function Navbar() { 17 | return ( 18 | <> 19 | <Hidden hide={{ s: true, m: false }}> 20 | <View direction="row" gap={5} align="center" as="nav"> 21 | <Actionable href="/data"> 22 | <Text 23 | variant="body-2" 24 | weight="medium" 25 | className="hover:text-primary hover:cursor-pointer" 26 | > 27 | 数据来源 28 | </Text> 29 | </Actionable> 30 | <Actionable href="/probability"> 31 | <Text 32 | variant="body-2" 33 | weight="medium" 34 | className="hover:text-primary hover:cursor-pointer" 35 | > 36 | 概率计算器 37 | </Text> 38 | </Actionable> 39 | <Actionable href="/about"> 40 | <Text 41 | variant="body-2" 42 | weight="medium" 43 | className="hover:text-primary hover:cursor-pointer" 44 | > 45 | 关于 46 | </Text> 47 | </Actionable> 48 | </View> 49 | </Hidden> 50 | <Hidden hide={{ s: false, m: true }}> 51 | <Popover position="bottom-end" padding={1} width="140px"> 52 | <Popover.Trigger> 53 | {attributes => ( 54 | <Button 55 | attributes={attributes} 56 | icon={<Icon size={4} svg={<Menu />} />} 57 | /> 58 | )} 59 | </Popover.Trigger> 60 | <Popover.Content> 61 | <MenuItem roundedCorners href="/data"> 62 | <Text 63 | variant="body-3" 64 | className="hover:text-primary hover:cursor-pointer" 65 | > 66 | 数据来源 67 | </Text> 68 | </MenuItem> 69 | <MenuItem roundedCorners href="/probability"> 70 | <Text 71 | variant="body-3" 72 | className="hover:text-primary hover:cursor-pointer" 73 | > 74 | 概率计算器 75 | </Text> 76 | </MenuItem> 77 | <MenuItem roundedCorners href="/about"> 78 | <Text 79 | variant="body-3" 80 | className="hover:text-primary hover:cursor-pointer" 81 | > 82 | 关于 83 | </Text> 84 | </MenuItem> 85 | </Popover.Content> 86 | </Popover> 87 | </Hidden> 88 | </> 89 | ); 90 | } 91 | 92 | export default Navbar; 93 | -------------------------------------------------------------------------------- /app/_components/barlist.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, useResponsiveClientValue } from 'reshaped'; 2 | import React, { useMemo } from 'react'; 3 | import { useBirth } from '@/lib/store/useBirth'; 4 | import { BirthResult } from '@/lib/rebirth'; 5 | 6 | interface ProvinceData { 7 | province: string; 8 | probability: number; 9 | count: number; 10 | } 11 | 12 | const BarList: React.FC = () => { 13 | const percentage = useResponsiveClientValue({ 14 | s: 56, 15 | l: 70 16 | }); 17 | 18 | const birthResults = useBirth( 19 | (state: { birthResults: BirthResult[] }) => state.birthResults 20 | ); 21 | 22 | // Calculate the total occurrences for each province 23 | const provinceOccurrences = useMemo(() => { 24 | const occurrences: Record<string, number> = birthResults.reduce( 25 | (acc: Record<string, number>, result) => { 26 | acc[result.province] = (acc[result.province] || 0) + 1; 27 | return acc; 28 | }, 29 | {} as Record<string, number> 30 | ); 31 | 32 | return occurrences; 33 | }, [birthResults]); 34 | 35 | // Calculate the total number of BirthResult entries 36 | const totalBirthResults = birthResults.length; 37 | 38 | // Calculate the probability for each province and sort the results 39 | const sortedResults: ProvinceData[] = useMemo(() => { 40 | const results = Object.entries(provinceOccurrences) 41 | .map(([province, count]) => ({ 42 | province, 43 | probability: count / totalBirthResults, 44 | count // Include the count in the results 45 | })) 46 | .sort((a, b) => b.probability - a.probability); 47 | 48 | return results; 49 | }, [provinceOccurrences, totalBirthResults]); 50 | 51 | // Find the maximum probability to use as a baseline 52 | const maxProbability = useMemo(() => { 53 | return sortedResults.length > 0 ? sortedResults[0].probability : 0; 54 | }, [sortedResults]); 55 | 56 | return ( 57 | <View direction="column" gap={1}> 58 | {sortedResults.map((item: ProvinceData) => ( 59 | <View key={item.province} direction="row" align="center" gap={2}> 60 | <View width={12}> 61 | <Text>{item.province}</Text> 62 | </View> 63 | <div 64 | className="bg-[#ebe8e7] h-8 rounded-xl" 65 | style={{ 66 | width: 67 | percentage !== undefined 68 | ? `${(item.probability / maxProbability) * percentage}%` 69 | : '0%' 70 | }} 71 | > 72 | <Text 73 | variant="body-3" 74 | color="neutral-faded" 75 | className="absolute right-2 top-1/2 transform -translate-y-1/2" 76 | > 77 | {(item.probability * 100).toFixed(2)}% 78 | </Text> 79 | </div> 80 | <Text variant="body-3" color="neutral-faded"> 81 | {item.count} 82 | </Text> 83 | </View> 84 | ))} 85 | </View> 86 | ); 87 | }; 88 | 89 | export default BarList; 90 | -------------------------------------------------------------------------------- /app/_components/result-table.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useState } from 'react'; 4 | import { Pagination, Table, Text, View } from 'reshaped'; 5 | import { translateGender } from '@/lib/rebirth'; 6 | import { useBirth } from '@/lib/store/useBirth'; 7 | 8 | function ResultTable() { 9 | const birthResults = useBirth(state => state.birthResults); 10 | const reversedResults = [...birthResults].reverse(); 11 | 12 | const [currentPage, setCurrentPage] = useState(1); 13 | const pageSize = 20; 14 | 15 | const startIndex = (currentPage - 1) * pageSize; 16 | const endIndex = startIndex + pageSize; 17 | const currentPageData = reversedResults.slice(startIndex, endIndex); 18 | 19 | const totalPages = Math.ceil(reversedResults.length / pageSize); 20 | 21 | const handlePageChange = (page: number) => { 22 | setCurrentPage(page); 23 | }; 24 | 25 | return ( 26 | <View gap={4}> 27 | <View backgroundColor="neutral-faded" className="rounded-xl"> 28 | <Table border columnBorder> 29 | <Table.Row highlighted> 30 | <Table.Heading padding={1.5}> 31 | <Text align="center">投胎次数</Text> 32 | </Table.Heading> 33 | <Table.Heading padding={1.5}> 34 | <Text align="center">性别</Text> 35 | </Table.Heading> 36 | <Table.Heading padding={1.5}> 37 | <Text align="center">省份/地区</Text> 38 | </Table.Heading> 39 | <Table.Heading padding={1.5}> 40 | <Text align="center">区域</Text> 41 | </Table.Heading> 42 | <Table.Heading padding={1.5}> 43 | <Text align="center">第几孩</Text> 44 | </Table.Heading> 45 | </Table.Row> 46 | {currentPageData.map((item, index) => ( 47 | <Table.Row key={index}> 48 | <Table.Cell padding={1}> 49 | <Text align="center"> 50 | {reversedResults.length - (startIndex + index)} 51 | </Text> 52 | </Table.Cell> 53 | <Table.Cell padding={1}> 54 | <Text align="center">{translateGender(item.gender)}</Text> 55 | </Table.Cell> 56 | <Table.Cell padding={1}> 57 | <Text align="center">{item.province}</Text> 58 | </Table.Cell> 59 | <Table.Cell padding={1}> 60 | <Text align="center">{item.category || '-'}</Text> 61 | </Table.Cell> 62 | <Table.Cell padding={1}> 63 | <Text align="center">{item.order || '-'}</Text> 64 | </Table.Cell> 65 | </Table.Row> 66 | ))} 67 | </Table> 68 | </View> 69 | {totalPages > 1 && ( 70 | <View align="center"> 71 | <Pagination 72 | total={totalPages} 73 | previousAriaLabel="上一页" 74 | nextAriaLabel="下一页" 75 | pageAriaLabel={args => `Page ${args.page}`} 76 | onChange={args => handlePageChange(args.page)} 77 | /> 78 | </View> 79 | )} 80 | </View> 81 | ); 82 | } 83 | 84 | export default ResultTable; 85 | -------------------------------------------------------------------------------- /app/_components/world-map.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import world from '@/data/world.json'; 4 | import * as echarts from 'echarts'; 5 | import { useEffect, useRef, useMemo } from 'react'; 6 | import { Loader2 } from 'lucide-react'; 7 | import { Text } from 'reshaped'; 8 | import { useBirth } from '@/lib/store/useBirth'; 9 | 10 | const WorldMap = () => { 11 | const chartRef = useRef<HTMLDivElement | null>(null); 12 | 13 | const echartsMapClick = () => { 14 | }; 15 | 16 | const mapOption = (mapName: any, data: any) => { 17 | const myChart = echarts.init(chartRef.current); 18 | 19 | echarts.registerMap(mapName, data); 20 | 21 | const option = { 22 | radius: '100%', 23 | tooltip: { 24 | trigger: 'item', 25 | padding: [5, 10], 26 | backgroundColor: '#ffffff', // tooltip 背景颜色 27 | borderColor: '#e1e1e1', // tooltip 边框颜色 28 | borderWidth: 1, // tooltip 边框宽度 29 | textStyle: { 30 | color: '#666', // tooltip 文字颜色 31 | fontSize: 14 // tooltip 字体大小 32 | }, 33 | extraCssText: 'box-shadow: 0 0 0px rgba(0, 0, 0, 0.3);' 34 | }, 35 | legend: { 36 | orient: 'horizontal', // 图例的排列方向 37 | // textStyle: { color: '#1a1e45' }, 38 | x: 'left', //图例的位置 39 | y: '-20000000000000' 40 | }, 41 | visualMap: { 42 | //颜色的设置 dataRange 43 | // textStyle: { color: '#1a1e45' }, 44 | x: 'left', 45 | y: 'bottom', 46 | // splitList: [{ start: 0, end: 150000 }], 47 | show: false, 48 | // text:['高','低'],// 文本,默认为数值文本 49 | color: ['#1a1e45'] 50 | }, 51 | geo: { 52 | map: 'world', // 此处需与注册地图命名保持一致 53 | type: 'map', 54 | zoom: 1.2, 55 | label: { 56 | normal: { 57 | show: false, 58 | textStyle: { 59 | color: '#FFFFFF' 60 | } 61 | }, 62 | emphasis: { 63 | show: false 64 | } 65 | }, 66 | roam: true, // 是否允许缩放 67 | scaleLimit: { 68 | // 设置缩放的最小和最大比例 69 | min: 1.2, 70 | max: 5 71 | }, 72 | itemStyle: { 73 | normal: { 74 | color: '#fcfcfd', // 地图背景色,用到组件配置的变量 75 | borderColor: '#bebfc0' // 省市边界线 76 | }, 77 | emphasis: { 78 | areaColor: '#afd8af' // 悬浮背景 79 | } 80 | }, 81 | data: [] 82 | } 83 | }; 84 | myChart.setOption(option); 85 | 86 | myChart.getZr().on('click', params => { 87 | if (params.target) { 88 | myChart.on('click', echartsMapClick); 89 | } 90 | }); 91 | }; 92 | 93 | const loadingChina = () => { 94 | mapOption('world', world); 95 | }; 96 | 97 | useEffect(() => { 98 | loadingChina(); 99 | }, []); 100 | 101 | return ( 102 | <div 103 | className="flex flex-row space-x-1.5 items-center justify-center md:min-h-[460px] min-h-[320px] md:w-[600px] w-full px-2" 104 | ref={chartRef} 105 | > 106 | <Loader2 size={16} className="animate-spin" /> 107 | <Text>地图加载中</Text> 108 | </div> 109 | ); 110 | }; 111 | 112 | export default WorldMap; 113 | -------------------------------------------------------------------------------- /app/_components/share-map.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import china from '@/data/china.json'; 4 | import * as echarts from 'echarts'; 5 | import { useEffect, useRef, useMemo } from 'react'; 6 | import { Loader, Text } from 'reshaped'; 7 | import { useBirth } from '@/lib/store/useBirth'; 8 | import { BirthResult } from '@/lib/rebirth'; 9 | 10 | const ShareMap = ({ region }: { region: string }) => { 11 | const birthResults = useBirth( 12 | (state: { birthResults: BirthResult[] }) => state.birthResults 13 | ); 14 | const chartRef = useRef<HTMLDivElement | null>(null); 15 | 16 | const dataList = useMemo(() => { 17 | const provinceMap: { [key: string]: number } = {}; 18 | 19 | birthResults.forEach(result => { 20 | if (provinceMap[result.province]) { 21 | provinceMap[result.province] += result.probability; 22 | } else { 23 | provinceMap[result.province] = result.probability; 24 | } 25 | }); 26 | 27 | return Object.entries(provinceMap).map(([name, value]) => ({ 28 | name, 29 | value 30 | })); 31 | }, [birthResults]); 32 | 33 | useEffect(() => { 34 | loadingChina(); 35 | }, [dataList, region]); 36 | 37 | const topNumber = 38 | dataList.length > 0 ? Math.max(...dataList.map(item => item.value)) : 0; 39 | const bottomNumber = 40 | dataList.length > 0 ? Math.min(...dataList.map(item => item.value)) : 0; 41 | 42 | const mapOption = (mapName: any, data: any) => { 43 | const myChart = echarts.init(chartRef.current); 44 | 45 | echarts.registerMap(mapName, data); 46 | 47 | const markPointData = region 48 | ? china.features.find(feature => feature.properties.name === region) 49 | : null; 50 | 51 | const option = { 52 | visualMap: { 53 | min: 0, 54 | max: 5, 55 | left: 'left', 56 | top: 'bottom', 57 | text: [topNumber.toFixed(2) + '%', bottomNumber.toFixed(2) + '%'], 58 | inRange: { 59 | color: ['#f5e1d6', '#ff4f04'] 60 | }, 61 | show: false 62 | }, 63 | geo: { 64 | map: 'china', 65 | roam: false, 66 | zoom: 1.23, 67 | label: { 68 | normal: { 69 | show: false, 70 | fontSize: '10', 71 | color: '#181716', 72 | fontWeight: 'medium' 73 | }, 74 | emphasis: { 75 | show: false, 76 | color: '#181716' 77 | } 78 | }, 79 | itemStyle: { 80 | normal: { 81 | borderColor: '#bebfc0', 82 | areaColor: '#fcfcfd' 83 | }, 84 | emphasis: { 85 | areaColor: '#afd8af' 86 | } 87 | } 88 | }, 89 | series: [ 90 | { 91 | name: '人口', 92 | type: 'map', 93 | geoIndex: 0, 94 | data: dataList, 95 | select: { 96 | disabled: true 97 | }, 98 | markPoint: { 99 | symbol: 'pin', 100 | symbolSize: 30, 101 | animationDuration: 100, // Animation duration 102 | itemStyle: { 103 | color: '#01ca78' // Color of markPoint 104 | }, 105 | data: markPointData 106 | ? [ 107 | { 108 | name: region, 109 | coord: markPointData.properties.cp 110 | } 111 | ] 112 | : [] 113 | } 114 | } 115 | ] 116 | }; 117 | myChart.setOption(option); 118 | }; 119 | 120 | const loadingChina = () => { 121 | mapOption('china', china); 122 | }; 123 | 124 | useEffect(() => { 125 | loadingChina(); 126 | }, [dataList]); 127 | 128 | return ( 129 | <div 130 | className="flex flex-row space-x-2 items-center justify-center h-48 w-full px-2" 131 | ref={chartRef} 132 | > 133 | <Loader /> 134 | <Text>地图加载中</Text> 135 | </div> 136 | ); 137 | }; 138 | 139 | export default ShareMap; 140 | -------------------------------------------------------------------------------- /app/_components/map.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import china from '@/data/china.json'; 4 | import * as echarts from 'echarts'; 5 | import { useEffect, useRef, useMemo } from 'react'; 6 | import { Loader, Text } from 'reshaped'; 7 | import { useBirth } from '@/lib/store/useBirth'; 8 | import { BirthResult } from '@/lib/rebirth'; 9 | 10 | const Map = () => { 11 | const birthResults = useBirth( 12 | (state: { birthResults: BirthResult[] }) => state.birthResults 13 | ); 14 | 15 | const chartRef = useRef<HTMLDivElement | null>(null); 16 | 17 | const dataList = useMemo(() => { 18 | const provinceMap: { [key: string]: number } = {}; 19 | 20 | birthResults.forEach(result => { 21 | if (provinceMap[result.province]) { 22 | provinceMap[result.province] += result.probability; 23 | } else { 24 | provinceMap[result.province] = result.probability; 25 | } 26 | }); 27 | 28 | return Object.entries(provinceMap).map(([name, value]) => ({ 29 | name, 30 | value 31 | })); 32 | }, [birthResults]); 33 | 34 | const topNumber = 35 | dataList.length > 0 ? Math.max(...dataList.map(item => item.value)) : 0; 36 | const bottomNumber = 37 | dataList.length > 0 ? Math.min(...dataList.map(item => item.value)) : 0; 38 | 39 | const echartsMapClick = () => {}; 40 | 41 | const mapOption = (mapName: any, data: any) => { 42 | const myChart = echarts.init(chartRef.current); 43 | 44 | echarts.registerMap(mapName, data); 45 | 46 | const latestBirthResult = useBirth.getState().getLatestBirthResult(); 47 | const currentProvince = latestBirthResult?.province; 48 | 49 | const markPointData = currentProvince 50 | ? china.features.find( 51 | feature => feature.properties.name === currentProvince 52 | ) 53 | : null; 54 | 55 | const option = { 56 | visualMap: { 57 | min: 0, 58 | max: 5, 59 | left: 'left', 60 | top: 'bottom', 61 | text: [topNumber.toFixed(2) + '%', bottomNumber.toFixed(2) + '%'], 62 | inRange: { 63 | color: ['#f5e1d6', '#ff4f04'] 64 | }, 65 | show: false 66 | }, 67 | geo: { 68 | map: 'china', 69 | roam: false, 70 | zoom: 1.23, 71 | label: { 72 | normal: { 73 | show: true, 74 | fontSize: '10', 75 | color: '#181716', 76 | fontWeight: 'medium' 77 | }, 78 | emphasis: { 79 | show: true, 80 | color: '#181716' 81 | } 82 | }, 83 | itemStyle: { 84 | normal: { 85 | borderColor: '#bebfc0', 86 | areaColor: '#fcfcfd' 87 | }, 88 | emphasis: { 89 | areaColor: '#afd8af' 90 | } 91 | } 92 | }, 93 | series: [ 94 | { 95 | name: '人口', 96 | type: 'map', 97 | geoIndex: 0, 98 | data: dataList, 99 | select: { 100 | disabled: true 101 | }, 102 | markPoint: { 103 | symbol: 'pin', 104 | symbolSize: 30, 105 | animationDuration: 100, // Animation duration 106 | itemStyle: { 107 | color: '#01ca78' // Color of markPoint 108 | }, 109 | data: markPointData 110 | ? [ 111 | { 112 | name: currentProvince, 113 | coord: markPointData.properties.cp 114 | } 115 | ] 116 | : [] 117 | } 118 | } 119 | ] 120 | }; 121 | myChart.setOption(option); 122 | 123 | myChart.getZr().on('click', params => { 124 | if (params.target) { 125 | myChart.on('click', echartsMapClick); 126 | } 127 | }); 128 | }; 129 | 130 | const loadingChina = () => { 131 | mapOption('china', china); 132 | }; 133 | 134 | useEffect(() => { 135 | loadingChina(); 136 | }, [dataList]); 137 | 138 | return ( 139 | <div 140 | className="flex flex-row space-x-2 items-center justify-center md:min-h-[460px] min-h-[320px] md:w-[600px] w-full px-2" 141 | ref={chartRef} 142 | > 143 | <Loader /> 144 | <Text>地图加载中</Text> 145 | </div> 146 | ); 147 | }; 148 | 149 | export default Map; 150 | -------------------------------------------------------------------------------- /app/_lib/rebirth.ts: -------------------------------------------------------------------------------- 1 | import data from '@/data/birthrate.json'; 2 | import dataDetailed from '@/data/birthrate_detailed.json'; 3 | 4 | const chinaBirthPopulation = 12123210; 5 | const hongKongBirthPopulation = 33200; 6 | const macauBirthPopulation = 3712; 7 | const taiwanBirthPopulation = 137413; 8 | 9 | const totalPopulation = 10 | chinaBirthPopulation + 11 | hongKongBirthPopulation + 12 | macauBirthPopulation + 13 | taiwanBirthPopulation; 14 | 15 | interface Region { 16 | id: string; 17 | name: string; 18 | total: number; 19 | male: number; 20 | female: number; 21 | } 22 | 23 | export const regions: Region[] = data.region.slice(1); // Do not include the first element 24 | 25 | interface CategoryData { 26 | [order: string]: { 27 | male: number; 28 | female: number; 29 | }; 30 | } 31 | 32 | export interface BirthData { 33 | id: number; 34 | name: string; 35 | display_name: string; 36 | town: CategoryData; 37 | city: CategoryData; 38 | countryside: CategoryData; 39 | } 40 | 41 | export interface BirthResult { 42 | province: string; 43 | id: number; 44 | category: string; 45 | gender: string; 46 | order: string; 47 | probability: number; 48 | } 49 | 50 | export const birthDataDetailed: BirthData[] = dataDetailed.slice(1); 51 | 52 | export function simulateBirth(): BirthResult { 53 | const randomNumber = Math.random() * totalPopulation; 54 | 55 | let cumulativePopulation = 0; 56 | for (const region of dataDetailed) { 57 | if (region.name === 'national') continue; 58 | for (const category of ['town', 'city', 'countryside'] as const) { 59 | for (const order of [ 60 | 'one', 61 | 'two', 62 | 'three', 63 | 'four', 64 | 'five_plus' 65 | ] as const) { 66 | for (const gender of ['male', 'female'] as const) { 67 | let population = region[category][order][gender]; 68 | if (!isSpecialProvince(region.name)) { 69 | population *= 10; 70 | } 71 | cumulativePopulation += population; 72 | if (cumulativePopulation > randomNumber) { 73 | const probability = population / totalPopulation; 74 | return { 75 | id: region.id, 76 | province: region.display_name, 77 | gender: gender, 78 | category: 79 | category === 'town' 80 | ? '城镇' 81 | : category === 'city' 82 | ? '城市' 83 | : '乡村', 84 | order: 85 | order === 'one' 86 | ? '一' 87 | : order === 'two' 88 | ? '二' 89 | : order === 'three' 90 | ? '三' 91 | : order === 'four' 92 | ? '四' 93 | : '五及以上', 94 | probability: probability 95 | }; 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | return { 103 | id: 0, 104 | province: '', 105 | gender: '', 106 | category: '', 107 | order: '', 108 | probability: 0 109 | }; 110 | } 111 | 112 | export function calculateBirthProbability( 113 | province: string, 114 | category: 'city' | 'town' | 'countryside', 115 | gender: 'male' | 'female', 116 | order: string 117 | ): { population: number; probability: number } { 118 | const region = birthDataDetailed.find(item => item.name === province); 119 | if (!region) { 120 | return { population: 0, probability: 0 }; 121 | } 122 | 123 | let categoryData; 124 | switch (category) { 125 | case 'town': 126 | categoryData = region.town; 127 | break; 128 | case 'city': 129 | categoryData = region.city; 130 | break; 131 | case 'countryside': 132 | categoryData = region.countryside; 133 | break; 134 | default: 135 | return { population: 0, probability: 0 }; 136 | } 137 | 138 | const genderData = categoryData[order][gender]; 139 | let population = genderData; 140 | if (!isSpecialProvince(province)) { 141 | population *= 10; 142 | } 143 | const probability = population / totalPopulation; 144 | 145 | return { population, probability }; 146 | } 147 | 148 | function isSpecialProvince(province: string): boolean { 149 | return ['xiang_gang', 'ao_men', 'tai_wan'].includes(province); 150 | } 151 | 152 | export function translateGender(gender: string): string { 153 | switch (gender) { 154 | case 'male': 155 | return '男'; 156 | case 'female': 157 | return '女'; 158 | default: 159 | return gender; 160 | } 161 | } 162 | 163 | export function translateGenderChild(gender: string): string { 164 | switch (gender) { 165 | case 'male': 166 | return '男孩'; 167 | case 'female': 168 | return '女孩'; 169 | default: 170 | return gender; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /app/_components/first-time-table.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useState } from 'react'; 4 | import { Icon, Pagination, Table, Text, Tooltip, View } from 'reshaped'; 5 | import { useBirth } from '@/lib/store/useBirth'; 6 | import { CircleHelp } from 'lucide-react'; 7 | import { BirthResult } from '@/lib/rebirth'; 8 | import { FemaleIcon, MaleIcon } from '@/components/gender-icon'; 9 | 10 | interface UniqueResult { 11 | province: string; 12 | firstBoyAppearance: number | string; 13 | firstGirlAppearance: number | string; 14 | } 15 | 16 | function FirstTimeTable() { 17 | const birthResults = useBirth(state => state.birthResults) as BirthResult[]; 18 | const [currentPage, setCurrentPage] = useState<number>(1); 19 | const pageSize = 20; 20 | 21 | const uniqueProvinces = Array.from( 22 | new Set(birthResults.map(item => item.province)) 23 | ); 24 | 25 | const uniqueResults: UniqueResult[] = uniqueProvinces.map(province => { 26 | const firstBoyIndex = birthResults.findIndex( 27 | item => item.province === province && item.gender === 'male' 28 | ); 29 | const firstGirlIndex = birthResults.findIndex( 30 | item => item.province === province && item.gender === 'female' 31 | ); 32 | return { 33 | province, 34 | firstBoyAppearance: firstBoyIndex >= 0 ? firstBoyIndex + 1 : 'N/A', 35 | firstGirlAppearance: firstGirlIndex >= 0 ? firstGirlIndex + 1 : 'N/A' 36 | }; 37 | }); 38 | 39 | uniqueResults.sort((a, b) => { 40 | const aFirstBoyAppearance = 41 | a.firstBoyAppearance !== 'N/A' ? Number(a.firstBoyAppearance) : Infinity; 42 | const aFirstGirlAppearance = 43 | a.firstGirlAppearance !== 'N/A' 44 | ? Number(a.firstGirlAppearance) 45 | : Infinity; 46 | const bFirstBoyAppearance = 47 | b.firstBoyAppearance !== 'N/A' ? Number(b.firstBoyAppearance) : Infinity; 48 | const bFirstGirlAppearance = 49 | b.firstGirlAppearance !== 'N/A' 50 | ? Number(b.firstGirlAppearance) 51 | : Infinity; 52 | 53 | const aMinAppearance = Math.min(aFirstBoyAppearance, aFirstGirlAppearance); 54 | const bMinAppearance = Math.min(bFirstBoyAppearance, bFirstGirlAppearance); 55 | 56 | return bMinAppearance - aMinAppearance; 57 | }); 58 | 59 | const totalPages = Math.ceil(uniqueResults.length / pageSize); 60 | 61 | const handlePageChange = (page: number) => { 62 | setCurrentPage(page); 63 | }; 64 | 65 | const startIndex = (currentPage - 1) * pageSize; 66 | const endIndex = startIndex + pageSize; 67 | const currentPageResults = uniqueResults.slice(startIndex, endIndex); 68 | 69 | return ( 70 | <View gap={4}> 71 | <View backgroundColor="neutral-faded" className="rounded-xl"> 72 | <Table border columnBorder> 73 | <Table.Row highlighted> 74 | <Table.Heading padding={1.5}> 75 | <Text align="center">省份/地区</Text> 76 | </Table.Heading> 77 | <Table.Heading padding={1.5}> 78 | <View align="center"> 79 | <Tooltip text="男孩和女孩第一次出现的序号" position="top"> 80 | {attributes => ( 81 | <View direction="row" align="center" gap={1}> 82 | <Text align="center">第一次出现</Text> 83 | <Icon svg={<CircleHelp />} attributes={attributes} /> 84 | </View> 85 | )} 86 | </Tooltip> 87 | </View> 88 | </Table.Heading> 89 | </Table.Row> 90 | {currentPageResults.map((item, index) => ( 91 | <Table.Row key={index}> 92 | <Table.Cell padding={1}> 93 | <Text align="center">{item.province}</Text> 94 | </Table.Cell> 95 | <Table.Cell padding={1}> 96 | <View direction="row" justify="center" gap={3}> 97 | {item.firstBoyAppearance !== 'N/A' && ( 98 | <View direction="row" align="center" gap={1}> 99 | <MaleIcon size={12} /> 100 | <Text align="center">{item.firstBoyAppearance}</Text> 101 | </View> 102 | )} 103 | {item.firstGirlAppearance !== 'N/A' && ( 104 | <View direction="row" align="center" gap={1}> 105 | <FemaleIcon size={14} /> 106 | <Text align="center">{item.firstGirlAppearance}</Text> 107 | </View> 108 | )} 109 | {item.firstBoyAppearance === 'N/A' && 110 | item.firstGirlAppearance === 'N/A' && ( 111 | <Text align="center">-</Text> 112 | )} 113 | </View> 114 | </Table.Cell> 115 | </Table.Row> 116 | ))} 117 | </Table> 118 | </View> 119 | {totalPages > 1 && ( 120 | <View align="center"> 121 | <Pagination 122 | total={totalPages} 123 | previousAriaLabel="上一页" 124 | nextAriaLabel="下一页" 125 | pageAriaLabel={args => `Page ${args.page}`} 126 | onChange={args => handlePageChange(args.page)} 127 | /> 128 | </View> 129 | )} 130 | </View> 131 | ); 132 | } 133 | 134 | export default FirstTimeTable; 135 | -------------------------------------------------------------------------------- /app/_data/birthrate.json: -------------------------------------------------------------------------------- 1 | { 2 | "region": [ 3 | { 4 | "id": "national", 5 | "name": "全国", 6 | "total": 12004692, 7 | "male": 6322003, 8 | "female": 5682689 9 | }, 10 | { 11 | "id": "bei_jing", 12 | "name": "北京", 13 | "total": 152864, 14 | "male": 79479, 15 | "female": 73385 16 | }, 17 | { 18 | "id": "tian_jin", 19 | "name": "天津", 20 | "total": 83097, 21 | "male": 43276, 22 | "female": 39821 23 | }, 24 | { 25 | "id": "he_bei", 26 | "name": "河北", 27 | "total": 608070, 28 | "male": 316857, 29 | "female": 291213 30 | }, 31 | { 32 | "id": "shan_xi_2", 33 | "name": "山西", 34 | "total": 288046, 35 | "male": 147611, 36 | "female": 140435 37 | }, 38 | { 39 | "id": "nei_meng_gu", 40 | "name": "内蒙古", 41 | "total": 173255, 42 | "male": 89566, 43 | "female": 83689 44 | }, 45 | { 46 | "id": "liao_ning", 47 | "name": "辽宁", 48 | "total": 220318, 49 | "male": 113266, 50 | "female": 107052 51 | }, 52 | { 53 | "id": "ji_lin", 54 | "name": "吉林", 55 | "total": 116554, 56 | "male": 60343, 57 | "female": 56211 58 | }, 59 | { 60 | "id": "hei_long_jiang", 61 | "name": "黑龙江", 62 | "total": 119821, 63 | "male": 61707, 64 | "female": 58114 65 | }, 66 | { 67 | "id": "shang_hai", 68 | "name": "上海", 69 | "total": 124777, 70 | "male": 64801, 71 | "female": 59976 72 | }, 73 | { 74 | "id": "jiang_su", 75 | "name": "江苏", 76 | "total": 563733, 77 | "male": 294029, 78 | "female": 269704 79 | }, 80 | { 81 | "id": "zhe_jiang", 82 | "name": "浙江", 83 | "total": 459658, 84 | "male": 240940, 85 | "female": 218718 86 | }, 87 | { 88 | "id": "an_hui", 89 | "name": "安徽", 90 | "total": 575917, 91 | "male": 305604, 92 | "female": 270313 93 | }, 94 | { 95 | "id": "fu_jian", 96 | "name": "福建", 97 | "total": 381804, 98 | "male": 207241, 99 | "female": 174563 100 | }, 101 | { 102 | "id": "jiang_xi", 103 | "name": "江西", 104 | "total": 427441, 105 | "male": 233443, 106 | "female": 193998 107 | }, 108 | { 109 | "id": "shan_dong", 110 | "name": "山东", 111 | "total": 868496, 112 | "male": 458738, 113 | "female": 409758 114 | }, 115 | { 116 | "id": "he_nan", 117 | "name": "河南", 118 | "total": 916791, 119 | "male": 476784, 120 | "female": 440007 121 | }, 122 | { 123 | "id": "hu_bei", 124 | "name": "湖北", 125 | "total": 477594, 126 | "male": 254740, 127 | "female": 222854 128 | }, 129 | { 130 | "id": "hu_nan", 131 | "name": "湖南", 132 | "total": 566431, 133 | "male": 301995, 134 | "female": 264436 135 | }, 136 | { 137 | "id": "guang_dong", 138 | "name": "广东", 139 | "total": 1292000, 140 | "male": 692476, 141 | "female": 599524 142 | }, 143 | { 144 | "id": "guang_xi", 145 | "name": "广西", 146 | "total": 568245, 147 | "male": 303315, 148 | "female": 264930 149 | }, 150 | { 151 | "id": "hai_nan", 152 | "name": "海南", 153 | "total": 104128, 154 | "male": 57303, 155 | "female": 46825 156 | }, 157 | { 158 | "id": "chong_qing", 159 | "name": "重庆", 160 | "total": 239633, 161 | "male": 124411, 162 | "female": 115222 163 | }, 164 | { 165 | "id": "si_chuan", 166 | "name": "四川", 167 | "total": 635353, 168 | "male": 330146, 169 | "female": 305207 170 | }, 171 | { 172 | "id": "gui_zhou", 173 | "name": "贵州", 174 | "total": 526610, 175 | "male": 279552, 176 | "female": 247058 177 | }, 178 | { 179 | "id": "yun_nan", 180 | "name": "云南", 181 | "total": 516555, 182 | "male": 267614, 183 | "female": 248941 184 | }, 185 | { 186 | "id": "xi_zang", 187 | "name": "西藏", 188 | "total": 50723, 189 | "male": 26030, 190 | "female": 24693 191 | }, 192 | { 193 | "id": "shan_xi_1", 194 | "name": "陕西", 195 | "total": 353175, 196 | "male": 183652, 197 | "female": 169523 198 | }, 199 | { 200 | "id": "gan_su", 201 | "name": "甘肃", 202 | "total": 263513, 203 | "male": 136530, 204 | "female": 126983 205 | }, 206 | { 207 | "id": "qing_hai", 208 | "name": "青海", 209 | "total": 67506, 210 | "male": 34863, 211 | "female": 32643 212 | }, 213 | { 214 | "id": "ning_xia", 215 | "name": "宁夏", 216 | "total": 83255, 217 | "male": 42773, 218 | "female": 40482 219 | }, 220 | { 221 | "id": "xin_jiang", 222 | "name": "新疆", 223 | "total": 179329, 224 | "male": 92918, 225 | "female": 86411 226 | }, 227 | { 228 | "id": "ao_men", 229 | "name": "澳门", 230 | "total": 3712, 231 | "male": 1905, 232 | "female": 1807 233 | }, 234 | { 235 | "id": "xiang_gang", 236 | "name": "香港", 237 | "total": 33200, 238 | "male": 17400, 239 | "female": 15800 240 | }, 241 | { 242 | "id": "tai_wan", 243 | "name": "台湾", 244 | "total": 137413, 245 | "male": 71208, 246 | "female": 66205 247 | } 248 | ] 249 | } -------------------------------------------------------------------------------- /app/_components/calculator.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useState } from 'react'; 4 | import { Button, FormControl, Select, Text, View } from 'reshaped'; 5 | import { calculateBirthProbability } from '@/lib/rebirth'; 6 | 7 | type Gender = 'male' | 'female'; 8 | type Category = 'city' | 'countryside' | 'town'; 9 | 10 | function Calculator() { 11 | const [province, setProvince] = useState(''); 12 | const [gender, setGender] = useState(''); 13 | const [category, setCategory] = useState(''); 14 | const [order, setOrder] = useState(''); 15 | const [probability, setProbability] = useState(0); 16 | const [population, setPopulation] = useState(0); 17 | 18 | const handleCalculate = () => { 19 | const { population, probability } = calculateBirthProbability( 20 | province, 21 | category as Category, 22 | gender as Gender, 23 | order 24 | ); 25 | setProbability(probability); 26 | setPopulation(population); 27 | }; 28 | 29 | return ( 30 | <View> 31 | <View paddingBlock={4} direction={{ s: 'column', m: 'row' }} gap={4}> 32 | <View.Item columns={{ s: 12, m: 3 }}> 33 | <FormControl> 34 | <FormControl.Label>出生地</FormControl.Label> 35 | <Select 36 | name="province" 37 | placeholder="选择出生地" 38 | options={[ 39 | { label: '北京', value: 'bei_jing' }, 40 | { label: '天津', value: 'tian_jin' }, 41 | { label: '河北', value: 'he_bei' }, 42 | { label: '山西', value: 'shan_xi_2' }, 43 | { label: '内蒙古', value: 'nei_meng_gu' }, 44 | { label: '辽宁', value: 'liao_ning' }, 45 | { label: '吉林', value: 'ji_lin' }, 46 | { label: '黑龙江', value: 'hei_long_jiang' }, 47 | { label: '上海', value: 'shang_hai' }, 48 | { label: '江苏', value: 'jiang_su' }, 49 | { label: '浙江', value: 'zhe_jiang' }, 50 | { label: '安徽', value: 'an_hui' }, 51 | { label: '福建', value: 'fu_jian' }, 52 | { label: '江西', value: 'jiang_xi' }, 53 | { label: '山东', value: 'shan_dong' }, 54 | { label: '河南', value: 'he_nan' }, 55 | { label: '湖北', value: 'hu_bei' }, 56 | { label: '湖南', value: 'hu_nan' }, 57 | { label: '广东', value: 'guang_dong' }, 58 | { label: '广西', value: 'guang_xi' }, 59 | { label: '海南', value: 'hai_nan' }, 60 | { label: '重庆', value: 'chong_qing' }, 61 | { label: '四川', value: 'si_chuan' }, 62 | { label: '贵州', value: 'gui_zhou' }, 63 | { label: '云南', value: 'yun_nan' }, 64 | { label: '西藏', value: 'xi_zang' }, 65 | { label: '陕西', value: 'shan_xi_1' }, 66 | { label: '甘肃', value: 'gan_su' }, 67 | { label: '青海', value: 'qing_hai' }, 68 | { label: '宁夏', value: 'ning_xia' }, 69 | { label: '新疆', value: 'xin_jiang' } 70 | ]} 71 | onChange={event => setProvince(event.value)} 72 | attributes={{ 73 | 'aria-autocomplete': 'none' 74 | }} 75 | /> 76 | </FormControl> 77 | </View.Item> 78 | <View.Item columns={{ s: 12, m: 3 }}> 79 | <FormControl> 80 | <FormControl.Label>性别</FormControl.Label> 81 | <Select 82 | name="gender" 83 | placeholder="选择性别" 84 | options={[ 85 | { label: '男孩', value: 'male' }, 86 | { label: '女孩', value: 'female' } 87 | ]} 88 | onChange={event => setGender(event.value)} 89 | attributes={{ 90 | 'aria-autocomplete': 'none' 91 | }} 92 | /> 93 | </FormControl> 94 | </View.Item> 95 | <View.Item columns={{ s: 12, m: 3 }}> 96 | <FormControl> 97 | <FormControl.Label>区域</FormControl.Label> 98 | <Select 99 | name="category" 100 | placeholder="选择区域" 101 | options={[ 102 | { label: '城市', value: 'city' }, 103 | { label: '乡村', value: 'countryside' }, 104 | { label: '城镇', value: 'town' } 105 | ]} 106 | onChange={event => setCategory(event.value)} 107 | attributes={{ 108 | 'aria-autocomplete': 'none' 109 | }} 110 | /> 111 | </FormControl> 112 | </View.Item> 113 | <View.Item columns={{ s: 12, m: 3 }}> 114 | <FormControl> 115 | <FormControl.Label>第几孩</FormControl.Label> 116 | <Select 117 | name="order" 118 | placeholder="选择第几孩" 119 | options={[ 120 | { label: '第一孩', value: 'one' }, 121 | { label: '第二孩', value: 'two' }, 122 | { label: '第三孩', value: 'three' }, 123 | { label: '第四孩', value: 'four' }, 124 | { label: '第五孩及以上', value: 'five_plus' } 125 | ]} 126 | onChange={event => setOrder(event.value)} 127 | attributes={{ 128 | 'aria-autocomplete': 'none' 129 | }} 130 | /> 131 | </FormControl> 132 | </View.Item> 133 | </View> 134 | <View paddingBlock={8} justify="center" direction="row"> 135 | <View width={{ s: 48, m: 32 }}> 136 | <Button 137 | color="primary" 138 | fullWidth 139 | rounded 140 | onClick={handleCalculate} 141 | disabled={!province || !order || !category || !gender} 142 | > 143 | 计算 144 | </Button> 145 | </View> 146 | </View> 147 | <View paddingBlock={6} justify="center" direction="column" align="center"> 148 | <View direction="row"> 149 | <Text variant="body-2"> 150 | 出生概率:{' '} 151 | </Text> 152 | <Text variant="body-2" color="primary" weight="medium"> 153 | {probability ? `${(probability * 100).toFixed(5)}%` : '0%'} 154 | </Text> 155 | </View> 156 | <View direction="row" gap={1}> 157 | <Text variant="body-2"> 158 | 每年大约有 159 | </Text> 160 | <Text weight="medium" variant="body-2" color="primary"> 161 | {population} 162 | </Text> 163 | <Text variant="body-2"> 164 | 个这样的新生儿 165 | </Text> 166 | </View> 167 | </View> 168 | </View> 169 | ); 170 | } 171 | 172 | export default Calculator; 173 | -------------------------------------------------------------------------------- /app/_themes/orangeTheme/theme.json: -------------------------------------------------------------------------------- 1 | {"fontFamily":{"title":{"family":"Inter, BlinkMacSystemFont, -apple-system, Roboto, Helvetica, Arial, sans-serif"},"body":{"family":"Inter, BlinkMacSystemFont, -apple-system, Roboto, Helvetica, Arial, sans-serif"}},"fontWeight":{"regular":{"weight":400},"medium":{"weight":500},"semibold":{"weight":600},"bold":{"weight":700},"heavy":{"weight":800},"black":{"weight":900}},"font":{"title1":{"fontSize":{"px":96},"lineHeight":{"px":100},"fontWeightToken":"heavy","fontFamilyToken":"title"},"title2":{"fontSize":{"px":80},"lineHeight":{"px":84},"fontWeightToken":"heavy","fontFamilyToken":"title"},"title3":{"fontSize":{"px":64},"lineHeight":{"px":68},"fontWeightToken":"heavy","fontFamilyToken":"title"},"title4":{"fontSize":{"px":56},"lineHeight":{"px":60},"fontWeightToken":"bold","fontFamilyToken":"title"},"title5":{"fontSize":{"px":48},"lineHeight":{"px":52},"fontWeightToken":"bold","fontFamilyToken":"title"},"title6":{"fontSize":{"px":36},"lineHeight":{"px":40},"fontWeightToken":"bold","fontFamilyToken":"title"},"featured1":{"fontSize":{"px":32},"lineHeight":{"px":40},"fontFamilyToken":"body"},"featured2":{"fontSize":{"px":24},"lineHeight":{"px":32},"fontFamilyToken":"body"},"featured3":{"fontSize":{"px":20},"lineHeight":{"px":28},"fontFamilyToken":"body"},"body1":{"fontSize":{"px":18},"lineHeight":{"px":28},"fontFamilyToken":"body"},"body2":{"fontSize":{"px":16},"lineHeight":{"px":24},"fontFamilyToken":"body"},"body3":{"fontSize":{"px":14},"lineHeight":{"px":20},"fontFamilyToken":"body"},"caption1":{"fontSize":{"px":12},"lineHeight":{"px":16},"fontFamilyToken":"body"},"caption2":{"fontSize":{"px":10},"lineHeight":{"px":12},"fontFamilyToken":"body"}},"unit":{"base":{"px":4},"radiusSmall":{"px":5},"radiusMedium":{"px":8},"radiusLarge":{"px":12},"x1":{"px":4},"x2":{"px":8},"x3":{"px":12},"x4":{"px":16},"x5":{"px":20},"x6":{"px":24},"x7":{"px":28},"x8":{"px":32},"x9":{"px":36},"x10":{"px":40}},"color":{"backgroundPrimary":{"hex":"#ff4f04","hexDark":"#f14e09"},"backgroundPrimaryFaded":{"hex":"#ffefe8","hexDark":"#39221f"},"backgroundPrimaryHighlighted":{"hex":"#ed4800","hexDark":"#fe5926"},"borderPrimary":{"hex":"#d64000","hexDark":"#fe633d"},"borderPrimaryFaded":{"hex":"#fedfd1","hexDark":"#512d28"},"foregroundPrimary":{"hex":"#ba3700","hexDark":"#ff623f"},"backgroundCritical":{"hex":"#ff2732","hexDark":"#f7252f"},"backgroundCriticalFaded":{"hex":"#ffe8e9","hexDark":"#3e1f20"},"backgroundCriticalHighlighted":{"hex":"#f4001b","hexDark":"#f7494e"},"borderCritical":{"hex":"#e10018","hexDark":"#f86669"},"borderCriticalFaded":{"hex":"#fdd8d9","hexDark":"#58292a"},"foregroundCritical":{"hex":"#ce0015","hexDark":"#ff5f63"},"backgroundWarning":{"hex":"#facc15","hexDark":"#f1c512"},"backgroundWarningFaded":{"hex":"#fffae9","hexDark":"#2c271f"},"backgroundWarningHighlighted":{"hex":"#edc113","hexDark":"#fed014"},"borderWarning":{"hex":"#cfa90f","hexDark":"#b4920a"},"borderWarningFaded":{"hex":"#faedbb","hexDark":"#3d3628"},"foregroundWarning":{"hex":"#7b6305","hexDark":"#b4920c"},"backgroundPositive":{"hex":"#00ca78","hexDark":"#07bd74"},"backgroundPositiveFaded":{"hex":"#e8fff6","hexDark":"#1f2a23"},"backgroundPositiveHighlighted":{"hex":"#00be70","hexDark":"#08c97c"},"borderPositive":{"hex":"#00a863","hexDark":"#06ac69"},"borderPositiveFaded":{"hex":"#bbfae3","hexDark":"#293b2f"},"foregroundPositive":{"hex":"#007543","hexDark":"#00ac65"},"backgroundNeutral":{"hex":"#dbd8d5","hexDark":"#212120"},"backgroundNeutralFaded":{"hex":"#f5f4f3","hexDark":"#2d271f"},"backgroundNeutralHighlighted":{"hex":"#d1cdc8","hexDark":"#292928"},"borderNeutral":{"hex":"#b6b2ad","hexDark":"#525250"},"borderNeutralFaded":{"hex":"#e1e1e1","hexDark":"#403528"},"foregroundNeutral":{"hex":"#181716","hexDark":"#f1f0ef"},"foregroundNeutralFaded":{"hex":"#686562","hexDark":"#cbc6c1"},"backgroundDisabled":{"hex":"#efeeec","hexDark":"#222221"},"backgroundDisabledFaded":{"hex":"#f7f6f6","hexDark":"#1a1a19"},"borderDisabled":{"hex":"#e4e2e0","hexDark":"#2a2a29"},"foregroundDisabled":{"hex":"#d0cbc7","hexDark":"#494947"},"backgroundElevationBase":{"hex":"#ffffff","hexDark":"#181817"},"backgroundElevationRaised":{"hex":"#ffffff","hexDark":"#1c1c1b"},"backgroundElevationOverlay":{"hex":"#ffffff","hexDark":"#20201f"},"backgroundPage":{"hex":"#f5f3ef"},"backgroundPageFaded":{"hex":"#faf9f9","hexDark":"#131313"},"brand":{"hex":"#ff4f04"},"white":{"hex":"#ffffff"},"black":{"hex":"#000000"},"onBackgroundPrimary":{"hex":"#ffffff"},"rgbBackgroundPrimary":{"hex":"255, 79, 4","hexDark":"241, 78, 9"},"rgbBackgroundPrimaryFaded":{"hex":"255, 239, 232","hexDark":"57, 34, 31"},"rgbBackgroundPrimaryHighlighted":{"hex":"237, 72, 0","hexDark":"254, 89, 38"},"rgbBorderPrimary":{"hex":"214, 64, 0","hexDark":"254, 99, 61"},"rgbBorderPrimaryFaded":{"hex":"254, 223, 209","hexDark":"81, 45, 40"},"onBackgroundCritical":{"hex":"#ffffff"},"rgbBackgroundCritical":{"hex":"255, 39, 50","hexDark":"247, 37, 47"},"rgbBackgroundCriticalFaded":{"hex":"255, 232, 233","hexDark":"62, 31, 32"},"rgbBackgroundCriticalHighlighted":{"hex":"244, 0, 27","hexDark":"247, 73, 78"},"rgbBorderCritical":{"hex":"225, 0, 24","hexDark":"248, 102, 105"},"rgbBorderCriticalFaded":{"hex":"253, 216, 217","hexDark":"88, 41, 42"},"onBackgroundWarning":{"hex":"#000000"},"rgbBackgroundWarning":{"hex":"250, 204, 21","hexDark":"241, 197, 18"},"rgbBackgroundWarningFaded":{"hex":"255, 250, 233","hexDark":"44, 39, 31"},"rgbBackgroundWarningHighlighted":{"hex":"237, 193, 19","hexDark":"254, 208, 20"},"rgbBorderWarning":{"hex":"207, 169, 15","hexDark":"180, 146, 10"},"rgbBorderWarningFaded":{"hex":"250, 237, 187","hexDark":"61, 54, 40"},"onBackgroundPositive":{"hex":"#ffffff"},"rgbBackgroundPositive":{"hex":"0, 202, 120","hexDark":"7, 189, 116"},"rgbBackgroundPositiveFaded":{"hex":"232, 255, 246","hexDark":"31, 42, 35"},"rgbBackgroundPositiveHighlighted":{"hex":"0, 190, 112","hexDark":"8, 201, 124"},"rgbBorderPositive":{"hex":"0, 168, 99","hexDark":"6, 172, 105"},"rgbBorderPositiveFaded":{"hex":"187, 250, 227","hexDark":"41, 59, 47"},"onBackgroundNeutral":{"hex":"#000000","hexDark":"#ffffff"},"rgbBackgroundNeutral":{"hex":"219, 216, 213","hexDark":"33, 33, 32"},"rgbBackgroundNeutralFaded":{"hex":"245, 244, 243","hexDark":"45, 39, 31"},"rgbBackgroundNeutralHighlighted":{"hex":"209, 205, 200","hexDark":"41, 41, 40"},"rgbBorderNeutral":{"hex":"182, 178, 173","hexDark":"82, 82, 80"},"rgbBorderNeutralFaded":{"hex":"225, 225, 225","hexDark":"64, 53, 40"},"rgbBackgroundDisabled":{"hex":"239, 238, 236","hexDark":"34, 34, 33"},"rgbBackgroundDisabledFaded":{"hex":"247, 246, 246","hexDark":"26, 26, 25"},"rgbBorderDisabled":{"hex":"228, 226, 224","hexDark":"42, 42, 41"},"rgbBackgroundElevationBase":{"hex":"255, 255, 255","hexDark":"24, 24, 23"},"rgbBackgroundElevationRaised":{"hex":"255, 255, 255","hexDark":"28, 28, 27"},"rgbBackgroundElevationOverlay":{"hex":"255, 255, 255","hexDark":"32, 32, 31"},"rgbBackgroundPage":{"hex":"245, 243, 239"},"rgbBackgroundPageFaded":{"hex":"250, 249, 249","hexDark":"19, 19, 19"},"onBrand":{"hex":"#ffffff"},"rgbWhite":{"hex":"255, 255, 255"},"rgbBlack":{"hex":"0, 0, 0"}},"duration":{"fast":{"ms":200},"medium":{"ms":300},"slow":{"ms":400}},"easing":{"standard":{"x1":0.4,"y1":0,"x2":0.2,"y2":1},"accelerate":{"x1":0.4,"y1":0,"x2":1,"y2":1},"decelerate":{"x1":0,"y1":0,"x2":0.2,"y2":1}},"shadow":{"raised":[{"offsetX":0,"offsetY":2,"blurRadius":3,"colorToken":"black","opacity":0.1},{"offsetX":0,"offsetY":1,"blurRadius":2,"spreadRadius":-1,"colorToken":"black","opacity":0.1}],"overlay":[{"offsetX":0,"offsetY":1,"blurRadius":3,"colorToken":"black","opacity":0.1}]},"viewport":{"m":{"minPx":660},"l":{"minPx":900},"xl":{"minPx":1280},"s":{"maxPx":659}}} -------------------------------------------------------------------------------- /app/(home)/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Button, View, Divider, Tabs, Text, Icon, Loader } from 'reshaped'; 4 | import Map from '@/components/map'; 5 | import React, { useEffect, useRef, useState } from 'react'; 6 | import ResultTable from '@/components/result-table'; 7 | import { toast } from 'sonner'; 8 | import { Share2, X } from 'lucide-react'; 9 | import { 10 | BirthResult, 11 | simulateBirth, 12 | translateGenderChild 13 | } from '@/_lib/rebirth'; 14 | import { useBirth } from '@/lib/store/useBirth'; 15 | import BarList from '@/components/barlist'; 16 | import Piechart from '@/components/piechart'; 17 | import FirstTimeTable from '@/components/first-time-table'; 18 | import useShareModal from '@/lib/store/useShareModal'; 19 | import Ads from '@/components/ads'; 20 | 21 | function Page() { 22 | const [isLoading, setIsLoading] = useState(true); 23 | const [isPressing, setIsPressing] = useState(false); 24 | 25 | const pressIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null); 26 | 27 | useEffect(() => { 28 | const rehydrate = async () => { 29 | await useBirth.persist.rehydrate(); 30 | setIsLoading(false); 31 | }; 32 | rehydrate(); 33 | }, []); 34 | 35 | const { activate, deactivate, setShareInfo } = useShareModal(state => ({ 36 | activate: state.activate, 37 | deactivate: state.deactivate, 38 | setShareInfo: state.setShareInfo 39 | })); 40 | 41 | const { addBirthResult, getBirthResultsCount } = useBirth(state => ({ 42 | addBirthResult: state.addBirthResult, 43 | getBirthResultsCount: state.getBirthResultsCount 44 | })); 45 | 46 | const handleRebirth = () => { 47 | const randomNumber = Math.floor(Math.random() * 10000); 48 | 49 | if (randomNumber < 31) { 50 | // 31 out of 10000 is 3.1‰ 51 | showRebirthErrorToast(); 52 | } else { 53 | const birthResult = simulateBirth(); 54 | addBirthResult(birthResult); 55 | showRebirthToast(birthResult, getBirthResultsCount()); 56 | } 57 | }; 58 | 59 | const startPress = () => { 60 | setIsPressing(true); 61 | pressIntervalRef.current = setInterval(() => { 62 | handleRebirth(); 63 | }, 150); 64 | }; 65 | 66 | const endPress = () => { 67 | setIsPressing(false); 68 | if (pressIntervalRef.current) { 69 | clearInterval(pressIntervalRef.current); 70 | pressIntervalRef.current = null; 71 | } 72 | }; 73 | 74 | const showRebirthToast = (birthResult: BirthResult, count: number) => { 75 | const countAtCreation = count; 76 | toast.custom(t => ( 77 | <div className="relative bg-white w-full sm:w-[354px] py-5 pl-3 pr-5 border-neutral-faded border rounded-xl"> 78 | <div className="flex flex-row justify-start space-x-2 items-center"> 79 | <Button 80 | variant="ghost" 81 | onClick={() => { 82 | activate(); 83 | setShareInfo({ 84 | count: countAtCreation, 85 | region: birthResult.province, 86 | category: birthResult.category, 87 | gender: birthResult.gender, 88 | order: birthResult.order, 89 | probability: birthResult.probability 90 | }); 91 | }} 92 | > 93 | <Icon size={4} color="neutral-faded" svg={<Share2 />} /> 94 | </Button> 95 | <Text> 96 | 第{' '} 97 | <span className="font-medium text-primary">{countAtCreation}</span>{' '} 98 | 次投胎, 99 | {['香港', '澳门', '台湾'].includes(birthResult.province) ? ( 100 | <> 101 | 你出生在 102 | <span className="font-medium text-primary"> 103 | {birthResult.province} 104 | </span> 105 | ,是一个 106 | <span className="font-medium text-primary"> 107 | {translateGenderChild(birthResult.gender)} 108 | </span> 109 | 。 110 | </> 111 | ) : ( 112 | <> 113 | 你出生在 114 | <span className="font-medium text-primary"> 115 | {birthResult.province} 116 | </span> 117 | 的 118 | <span className="font-medium text-primary"> 119 | {birthResult.category} 120 | </span> 121 | ,是一个 122 | <span className="font-medium text-primary"> 123 | {translateGenderChild(birthResult.gender)} 124 | </span> 125 | ,你是这个家庭 126 | <span className="font-medium text-primary"> 127 | 第{birthResult.order}个 128 | </span> 129 | 孩子。 130 | </> 131 | )} 132 | </Text> 133 | </div> 134 | 135 | <button 136 | className="absolute top-2 right-3" 137 | onClick={() => toast.dismiss(t)} 138 | > 139 | <Icon size={4} color="neutral-faded" svg={<X />} /> 140 | </button> 141 | </div> 142 | )); 143 | }; 144 | 145 | const showRebirthErrorToast = () => { 146 | toast.custom(t => ( 147 | <div className="relative bg-red-100 w-full sm:w-[354px] p-5 border-red-500 border rounded-xl"> 148 | <div className="flex flex-row justify-between"> 149 | <Text color="critical"> 150 | 抱歉,你在这次投胎中不幸夭折,再试一次吧! 151 | </Text> 152 | </div> 153 | <button 154 | className="absolute top-2 right-3" 155 | onClick={() => toast.dismiss(t)} 156 | > 157 | <Icon color="critical" size={4} svg={<X />} /> 158 | </button> 159 | </div> 160 | )); 161 | }; 162 | 163 | const renderTabPanel = (component: React.ReactNode) => { 164 | return getBirthResultsCount() > 0 ? ( 165 | component 166 | ) : ( 167 | <> 168 | {isLoading ? ( 169 | <View 170 | direction="row" 171 | gap={2} 172 | align="center" 173 | paddingBlock={4} 174 | height={64} 175 | justify="center" 176 | > 177 | <Loader /> 178 | <Text>数据加载中</Text> 179 | </View> 180 | ) : ( 181 | <View align="center" paddingBlock={4} height={64} justify="center"> 182 | <Text color="neutral">暂无投胎记录,点击投胎按钮开始!</Text> 183 | </View> 184 | )} 185 | </> 186 | ); 187 | }; 188 | 189 | return ( 190 | <> 191 | <View paddingInline={4} paddingBottom={9} className="select-none"> 192 | <View paddingBlock={4}> 193 | <Map /> 194 | </View> 195 | <View align="center"> 196 | <View 197 | direction="row" 198 | justify="center" 199 | paddingBlock={4} 200 | gap={4} 201 | position="relative" 202 | width="100%" 203 | > 204 | <View width={64}> 205 | <div 206 | onMouseDown={event => { 207 | if (event.button === 0) { 208 | startPress(); 209 | } 210 | }} 211 | onMouseUp={endPress} 212 | onMouseLeave={endPress} 213 | onTouchStart={startPress} 214 | onTouchEnd={endPress} 215 | > 216 | <Button 217 | color="primary" 218 | rounded 219 | fullWidth 220 | onClick={handleRebirth} 221 | > 222 | 投胎 223 | </Button> 224 | </div> 225 | </View> 226 | </View> 227 | <View width="100%" paddingBottom={2} paddingTop={4}> 228 | <Divider /> 229 | </View> 230 | <View width="100%" paddingBlock={2}> 231 | <Tabs variant="pills" defaultValue="record"> 232 | <View paddingBottom={3}> 233 | <Tabs.List> 234 | <Tabs.Item value="record">投胎记录</Tabs.Item> 235 | <Tabs.Item value="province">地区分布</Tabs.Item> 236 | <Tabs.Item value="gender">性别分布</Tabs.Item> 237 | <Tabs.Item value="first">第一次出现</Tabs.Item> 238 | </Tabs.List> 239 | </View> 240 | <Tabs.Panel value="record"> 241 | {renderTabPanel(<ResultTable />)} 242 | </Tabs.Panel> 243 | <Tabs.Panel value="province"> 244 | {renderTabPanel(<BarList />)} 245 | </Tabs.Panel> 246 | <Tabs.Panel value="gender"> 247 | {renderTabPanel(<Piechart />)} 248 | </Tabs.Panel> 249 | <Tabs.Panel value="first"> 250 | {renderTabPanel(<FirstTimeTable />)} 251 | </Tabs.Panel> 252 | </Tabs> 253 | </View> 254 | </View> 255 | </View> 256 | <View paddingBottom={4} paddingInline={4}> 257 | <Ads /> 258 | </View> 259 | </> 260 | ); 261 | } 262 | 263 | export default Page; 264 | -------------------------------------------------------------------------------- /app/_themes/orangeTheme/theme.css: -------------------------------------------------------------------------------- 1 | 2 | [data-rs-theme="orangeTheme"] { 3 | --rs-font-family-title: Inter, BlinkMacSystemFont, -apple-system, Roboto, Helvetica, Arial, sans-serif; 4 | --rs-font-family-body: Inter, BlinkMacSystemFont, -apple-system, Roboto, Helvetica, Arial, sans-serif; 5 | --rs-font-weight-regular: 400; 6 | --rs-font-weight-medium: 500; 7 | --rs-font-weight-semibold: 600; 8 | --rs-font-weight-bold: 700; 9 | --rs-font-weight-heavy: 800; 10 | --rs-font-weight-black: 900; 11 | --rs-font-size-title-1: 96px; 12 | --rs-line-height-title-1: 100px; 13 | --rs-font-family-title-1: var(--rs-font-family-title); 14 | --rs-font-weight-title-1: var(--rs-font-weight-heavy); 15 | --rs-font-size-title-2: 80px; 16 | --rs-line-height-title-2: 84px; 17 | --rs-font-family-title-2: var(--rs-font-family-title); 18 | --rs-font-weight-title-2: var(--rs-font-weight-heavy); 19 | --rs-font-size-title-3: 64px; 20 | --rs-line-height-title-3: 68px; 21 | --rs-font-family-title-3: var(--rs-font-family-title); 22 | --rs-font-weight-title-3: var(--rs-font-weight-heavy); 23 | --rs-font-size-title-4: 56px; 24 | --rs-line-height-title-4: 60px; 25 | --rs-font-family-title-4: var(--rs-font-family-title); 26 | --rs-font-weight-title-4: var(--rs-font-weight-bold); 27 | --rs-font-size-title-5: 48px; 28 | --rs-line-height-title-5: 52px; 29 | --rs-font-family-title-5: var(--rs-font-family-title); 30 | --rs-font-weight-title-5: var(--rs-font-weight-bold); 31 | --rs-font-size-title-6: 36px; 32 | --rs-line-height-title-6: 40px; 33 | --rs-font-family-title-6: var(--rs-font-family-title); 34 | --rs-font-weight-title-6: var(--rs-font-weight-bold); 35 | --rs-font-size-featured-1: 32px; 36 | --rs-line-height-featured-1: 40px; 37 | --rs-font-family-featured-1: var(--rs-font-family-body); 38 | --rs-font-size-featured-2: 24px; 39 | --rs-line-height-featured-2: 32px; 40 | --rs-font-family-featured-2: var(--rs-font-family-body); 41 | --rs-font-size-featured-3: 20px; 42 | --rs-line-height-featured-3: 28px; 43 | --rs-font-family-featured-3: var(--rs-font-family-body); 44 | --rs-font-size-body-1: 18px; 45 | --rs-line-height-body-1: 28px; 46 | --rs-font-family-body-1: var(--rs-font-family-body); 47 | --rs-font-size-body-2: 16px; 48 | --rs-line-height-body-2: 24px; 49 | --rs-font-family-body-2: var(--rs-font-family-body); 50 | --rs-font-size-body-3: 14px; 51 | --rs-line-height-body-3: 20px; 52 | --rs-font-family-body-3: var(--rs-font-family-body); 53 | --rs-font-size-caption-1: 12px; 54 | --rs-line-height-caption-1: 16px; 55 | --rs-font-family-caption-1: var(--rs-font-family-body); 56 | --rs-font-size-caption-2: 10px; 57 | --rs-line-height-caption-2: 12px; 58 | --rs-font-family-caption-2: var(--rs-font-family-body); 59 | --rs-unit-base: 4px; 60 | --rs-unit-radius-small: 5px; 61 | --rs-unit-radius-medium: 8px; 62 | --rs-unit-radius-large: 12px; 63 | --rs-unit-x1: 4px; 64 | --rs-unit-x2: 8px; 65 | --rs-unit-x3: 12px; 66 | --rs-unit-x4: 16px; 67 | --rs-unit-x5: 20px; 68 | --rs-unit-x6: 24px; 69 | --rs-unit-x7: 28px; 70 | --rs-unit-x8: 32px; 71 | --rs-unit-x9: 36px; 72 | --rs-unit-x10: 40px; 73 | --rs-color-background-page: #f5f3ef; 74 | --rs-color-brand: #ff4f04; 75 | --rs-color-white: #ffffff; 76 | --rs-color-black: #000000; 77 | --rs-color-on-background-primary: #ffffff; 78 | --rs-color-on-background-critical: #ffffff; 79 | --rs-color-on-background-warning: #000000; 80 | --rs-color-on-background-positive: #ffffff; 81 | --rs-color-rgb-background-page: 245, 243, 239; 82 | --rs-color-on-brand: #ffffff; 83 | --rs-color-rgb-white: 255, 255, 255; 84 | --rs-color-rgb-black: 0, 0, 0; 85 | --rs-duration-fast: 200ms; 86 | --rs-duration-medium: 300ms; 87 | --rs-duration-slow: 400ms; 88 | --rs-easing-standard: cubic-bezier(0.4, 0, 0.2, 1); 89 | --rs-easing-accelerate: cubic-bezier(0.4, 0, 1, 1); 90 | --rs-easing-decelerate: cubic-bezier(0, 0, 0.2, 1); 91 | --rs-shadow-raised: 0px 2px 3px 0px rgba(0, 0, 0, 0.1), 0px 1px 2px -1px rgba(0, 0, 0, 0.1); 92 | --rs-shadow-overlay: 0px 1px 3px 0px rgba(0, 0, 0, 0.1); 93 | } 94 | 95 | [data-rs-theme="orangeTheme"][data-rs-color-mode="light"] { 96 | --rs-color-background-primary: #ff4f04; 97 | --rs-color-background-primary-faded: #ffefe8; 98 | --rs-color-background-primary-highlighted: #ed4800; 99 | --rs-color-border-primary: #d64000; 100 | --rs-color-border-primary-faded: #fedfd1; 101 | --rs-color-foreground-primary: #ba3700; 102 | --rs-color-background-critical: #ff2732; 103 | --rs-color-background-critical-faded: #ffe8e9; 104 | --rs-color-background-critical-highlighted: #f4001b; 105 | --rs-color-border-critical: #e10018; 106 | --rs-color-border-critical-faded: #fdd8d9; 107 | --rs-color-foreground-critical: #ce0015; 108 | --rs-color-background-warning: #facc15; 109 | --rs-color-background-warning-faded: #fffae9; 110 | --rs-color-background-warning-highlighted: #edc113; 111 | --rs-color-border-warning: #cfa90f; 112 | --rs-color-border-warning-faded: #faedbb; 113 | --rs-color-foreground-warning: #7b6305; 114 | --rs-color-background-positive: #00ca78; 115 | --rs-color-background-positive-faded: #e8fff6; 116 | --rs-color-background-positive-highlighted: #00be70; 117 | --rs-color-border-positive: #00a863; 118 | --rs-color-border-positive-faded: #bbfae3; 119 | --rs-color-foreground-positive: #007543; 120 | --rs-color-background-neutral: #dbd8d5; 121 | --rs-color-background-neutral-faded: #f5f4f3; 122 | --rs-color-background-neutral-highlighted: #d1cdc8; 123 | --rs-color-border-neutral: #b6b2ad; 124 | --rs-color-border-neutral-faded: #e1e1e1; 125 | --rs-color-foreground-neutral: #181716; 126 | --rs-color-foreground-neutral-faded: #686562; 127 | --rs-color-background-disabled: #efeeec; 128 | --rs-color-background-disabled-faded: #f7f6f6; 129 | --rs-color-border-disabled: #e4e2e0; 130 | --rs-color-foreground-disabled: #d0cbc7; 131 | --rs-color-background-elevation-base: #ffffff; 132 | --rs-color-background-elevation-raised: #ffffff; 133 | --rs-color-background-elevation-overlay: #ffffff; 134 | --rs-color-background-page-faded: #faf9f9; 135 | --rs-color-rgb-background-primary: 255, 79, 4; 136 | --rs-color-rgb-background-primary-faded: 255, 239, 232; 137 | --rs-color-rgb-background-primary-highlighted: 237, 72, 0; 138 | --rs-color-rgb-border-primary: 214, 64, 0; 139 | --rs-color-rgb-border-primary-faded: 254, 223, 209; 140 | --rs-color-rgb-background-critical: 255, 39, 50; 141 | --rs-color-rgb-background-critical-faded: 255, 232, 233; 142 | --rs-color-rgb-background-critical-highlighted: 244, 0, 27; 143 | --rs-color-rgb-border-critical: 225, 0, 24; 144 | --rs-color-rgb-border-critical-faded: 253, 216, 217; 145 | --rs-color-rgb-background-warning: 250, 204, 21; 146 | --rs-color-rgb-background-warning-faded: 255, 250, 233; 147 | --rs-color-rgb-background-warning-highlighted: 237, 193, 19; 148 | --rs-color-rgb-border-warning: 207, 169, 15; 149 | --rs-color-rgb-border-warning-faded: 250, 237, 187; 150 | --rs-color-rgb-background-positive: 0, 202, 120; 151 | --rs-color-rgb-background-positive-faded: 232, 255, 246; 152 | --rs-color-rgb-background-positive-highlighted: 0, 190, 112; 153 | --rs-color-rgb-border-positive: 0, 168, 99; 154 | --rs-color-rgb-border-positive-faded: 187, 250, 227; 155 | --rs-color-on-background-neutral: #000000; 156 | --rs-color-rgb-background-neutral: 219, 216, 213; 157 | --rs-color-rgb-background-neutral-faded: 245, 244, 243; 158 | --rs-color-rgb-background-neutral-highlighted: 209, 205, 200; 159 | --rs-color-rgb-border-neutral: 182, 178, 173; 160 | --rs-color-rgb-border-neutral-faded: 225, 225, 225; 161 | --rs-color-rgb-background-disabled: 239, 238, 236; 162 | --rs-color-rgb-background-disabled-faded: 247, 246, 246; 163 | --rs-color-rgb-border-disabled: 228, 226, 224; 164 | --rs-color-rgb-background-elevation-base: 255, 255, 255; 165 | --rs-color-rgb-background-elevation-raised: 255, 255, 255; 166 | --rs-color-rgb-background-elevation-overlay: 255, 255, 255; 167 | --rs-color-rgb-background-page-faded: 250, 249, 249; 168 | } 169 | 170 | [data-rs-theme="orangeTheme"][data-rs-color-mode="dark"] { 171 | --rs-color-background-primary: #f14e09; 172 | --rs-color-background-primary-faded: #39221f; 173 | --rs-color-background-primary-highlighted: #fe5926; 174 | --rs-color-border-primary: #fe633d; 175 | --rs-color-border-primary-faded: #512d28; 176 | --rs-color-foreground-primary: #ff623f; 177 | --rs-color-background-critical: #f7252f; 178 | --rs-color-background-critical-faded: #3e1f20; 179 | --rs-color-background-critical-highlighted: #f7494e; 180 | --rs-color-border-critical: #f86669; 181 | --rs-color-border-critical-faded: #58292a; 182 | --rs-color-foreground-critical: #ff5f63; 183 | --rs-color-background-warning: #f1c512; 184 | --rs-color-background-warning-faded: #2c271f; 185 | --rs-color-background-warning-highlighted: #fed014; 186 | --rs-color-border-warning: #b4920a; 187 | --rs-color-border-warning-faded: #3d3628; 188 | --rs-color-foreground-warning: #b4920c; 189 | --rs-color-background-positive: #07bd74; 190 | --rs-color-background-positive-faded: #1f2a23; 191 | --rs-color-background-positive-highlighted: #08c97c; 192 | --rs-color-border-positive: #06ac69; 193 | --rs-color-border-positive-faded: #293b2f; 194 | --rs-color-foreground-positive: #00ac65; 195 | --rs-color-background-neutral: #212120; 196 | --rs-color-background-neutral-faded: #2d271f; 197 | --rs-color-background-neutral-highlighted: #292928; 198 | --rs-color-border-neutral: #525250; 199 | --rs-color-border-neutral-faded: #403528; 200 | --rs-color-foreground-neutral: #f1f0ef; 201 | --rs-color-foreground-neutral-faded: #cbc6c1; 202 | --rs-color-background-disabled: #222221; 203 | --rs-color-background-disabled-faded: #1a1a19; 204 | --rs-color-border-disabled: #2a2a29; 205 | --rs-color-foreground-disabled: #494947; 206 | --rs-color-background-elevation-base: #181817; 207 | --rs-color-background-elevation-raised: #1c1c1b; 208 | --rs-color-background-elevation-overlay: #20201f; 209 | --rs-color-background-page-faded: #131313; 210 | --rs-color-rgb-background-primary: 241, 78, 9; 211 | --rs-color-rgb-background-primary-faded: 57, 34, 31; 212 | --rs-color-rgb-background-primary-highlighted: 254, 89, 38; 213 | --rs-color-rgb-border-primary: 254, 99, 61; 214 | --rs-color-rgb-border-primary-faded: 81, 45, 40; 215 | --rs-color-rgb-background-critical: 247, 37, 47; 216 | --rs-color-rgb-background-critical-faded: 62, 31, 32; 217 | --rs-color-rgb-background-critical-highlighted: 247, 73, 78; 218 | --rs-color-rgb-border-critical: 248, 102, 105; 219 | --rs-color-rgb-border-critical-faded: 88, 41, 42; 220 | --rs-color-rgb-background-warning: 241, 197, 18; 221 | --rs-color-rgb-background-warning-faded: 44, 39, 31; 222 | --rs-color-rgb-background-warning-highlighted: 254, 208, 20; 223 | --rs-color-rgb-border-warning: 180, 146, 10; 224 | --rs-color-rgb-border-warning-faded: 61, 54, 40; 225 | --rs-color-rgb-background-positive: 7, 189, 116; 226 | --rs-color-rgb-background-positive-faded: 31, 42, 35; 227 | --rs-color-rgb-background-positive-highlighted: 8, 201, 124; 228 | --rs-color-rgb-border-positive: 6, 172, 105; 229 | --rs-color-rgb-border-positive-faded: 41, 59, 47; 230 | --rs-color-on-background-neutral: #ffffff; 231 | --rs-color-rgb-background-neutral: 33, 33, 32; 232 | --rs-color-rgb-background-neutral-faded: 45, 39, 31; 233 | --rs-color-rgb-background-neutral-highlighted: 41, 41, 40; 234 | --rs-color-rgb-border-neutral: 82, 82, 80; 235 | --rs-color-rgb-border-neutral-faded: 64, 53, 40; 236 | --rs-color-rgb-background-disabled: 34, 34, 33; 237 | --rs-color-rgb-background-disabled-faded: 26, 26, 25; 238 | --rs-color-rgb-border-disabled: 42, 42, 41; 239 | --rs-color-rgb-background-elevation-base: 24, 24, 23; 240 | --rs-color-rgb-background-elevation-raised: 28, 28, 27; 241 | --rs-color-rgb-background-elevation-overlay: 32, 32, 31; 242 | --rs-color-rgb-background-page-faded: 19, 19, 19; 243 | } 244 | -------------------------------------------------------------------------------- /app/_components/share-modal.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { Button, Dismissible, Icon, Modal, Tabs, Text, View } from 'reshaped'; 5 | import useShareModal, { ShareInfo } from '@/lib/store/useShareModal'; 6 | import html2canvas from 'html2canvas'; 7 | import { CarrotIcon } from '@/components/title'; 8 | import QRCode from 'react-qr-code'; 9 | import ShareMap from '@/components/share-map'; 10 | import { toast } from 'sonner'; 11 | import { X } from 'lucide-react'; 12 | import ChinaMap from '@/components/icon'; 13 | import { translateGenderChild } from '@/lib/rebirth'; 14 | 15 | function ShareStyle1({ shareInfo }: { shareInfo: ShareInfo }) { 16 | return ( 17 | <div 18 | className="w-full bg-orange-200 relative aspect-square" 19 | id="shareContent" 20 | > 21 | <View 22 | direction="column" 23 | padding={6} 24 | justify="space-between" 25 | height="100%" 26 | > 27 | <ShareMap region={shareInfo.region} /> 28 | <Text variant="body-2" weight="medium"> 29 | {['香港', '澳门', '台湾'].includes(shareInfo.region) ? ( 30 | <> 31 | 我在投胎模拟器第{' '} 32 | <span className="text-[#ba3700] font-medium"> 33 | {shareInfo.count} 34 | </span>{' '} 35 | 次投胎在 36 | <span className="text-[#ba3700] font-medium"> 37 | {shareInfo.region} 38 | </span> 39 | ,是一个 40 | <span className="text-[#ba3700] font-medium"> 41 | {translateGenderChild(shareInfo.gender)} 42 | </span> 43 | ,你也来试试吧! 44 | </> 45 | ) : ( 46 | <> 47 | 我在投胎模拟器第{' '} 48 | <span className="text-[#ba3700] font-medium"> 49 | {shareInfo.count} 50 | </span>{' '} 51 | 次投胎在 52 | <span className="text-[#ba3700] font-medium"> 53 | {shareInfo.region}的{shareInfo.category} 54 | </span> 55 | ,是家里 56 | <span className="text-[#ba3700] font-medium"> 57 | 第{shareInfo.order}个{translateGenderChild(shareInfo.gender)} 58 | </span> 59 | ,概率只有{' '} 60 | <span className="text-[#ba3700] font-medium"> 61 | {shareInfo.probability.toPrecision(2)}% 62 | </span> 63 | ,你也来试试吧! 64 | </> 65 | )} 66 | </Text> 67 | <View direction="row" justify="space-between" align="center"> 68 | <View direction="row" gap={2} align="center"> 69 | <CarrotIcon size={40} /> 70 | <View direction="column"> 71 | <Text color="primary" weight="medium" variant="body-1"> 72 | 投胎模拟器 73 | </Text> 74 | <Text color="primary" weight="medium"> 75 | toutai.cc 76 | </Text> 77 | </View> 78 | </View> 79 | <QRCode 80 | value="https://toutai.cc" 81 | bgColor="#fed8aa" 82 | className="w-12 h-12" 83 | /> 84 | </View> 85 | </View> 86 | </div> 87 | ); 88 | } 89 | 90 | function ShareStyle2({ shareInfo }: { shareInfo: ShareInfo }) { 91 | return ( 92 | <div 93 | className="w-full bg-[#f5f3ef] relative aspect-square" 94 | id="shareContent" 95 | > 96 | <View direction="column" padding={6} height="100%"> 97 | <div className="absolute right-2 top-12"> 98 | <ChinaMap size={180} /> 99 | </div> 100 | <View direction="column" justify="center" grow paddingTop={6}> 101 | {['香港', '澳门', '台湾'].includes(shareInfo.region) ? ( 102 | <> 103 | <Text variant="body-2" weight="medium" className="z-10"> 104 | 第{' '} 105 | <span className="text-[#ba3700] font-medium"> 106 | {shareInfo.count} 107 | </span>{' '} 108 | 次 109 | </Text> 110 | <Text variant="body-2" weight="medium" className="z-10"> 111 | 我投胎在了 112 | <span className="text-[#ba3700] font-medium"> 113 | {shareInfo.region} 114 | </span> 115 | </Text> 116 | <Text variant="body-2" weight="medium" className="z-10"> 117 | 是一个 118 | <span className="text-[#ba3700] font-medium"> 119 | {translateGenderChild(shareInfo.gender)} 120 | </span> 121 | </Text> 122 | <Text variant="body-2" weight="medium" className="z-10"> 123 | 概率只有{' '} 124 | <span className="text-[#ba3700] font-medium"> 125 | {shareInfo.probability.toPrecision(2)}% 126 | </span> 127 | </Text> 128 | </> 129 | ) : ( 130 | <> 131 | <Text variant="body-2" weight="medium" className="z-10"> 132 | 第{' '} 133 | <span className="text-[#ba3700] font-medium"> 134 | {shareInfo.count} 135 | </span>{' '} 136 | 次 137 | </Text> 138 | <Text variant="body-2" weight="medium" className="z-10"> 139 | 我投胎在了 140 | <span className="text-[#ba3700] font-medium"> 141 | {shareInfo.region}的{shareInfo.category} 142 | </span> 143 | </Text> 144 | <Text variant="body-2" weight="medium" className="z-10"> 145 | 是家里 146 | <span className="text-[#ba3700] font-medium"> 147 | 第{shareInfo.order}个{translateGenderChild(shareInfo.gender)} 148 | </span> 149 | </Text> 150 | <Text variant="body-2" weight="medium" className="z-10"> 151 | 概率只有{' '} 152 | <span className="text-[#ba3700] font-medium"> 153 | {shareInfo.probability.toPrecision(2)}% 154 | </span> 155 | </Text> 156 | </> 157 | )} 158 | </View> 159 | <View direction="row" justify="space-between" align="center"> 160 | <View direction="row" gap={2} align="center"> 161 | <CarrotIcon size={40} /> 162 | <View direction="column"> 163 | <Text color="primary" weight="medium" variant="body-1"> 164 | 投胎模拟器 165 | </Text> 166 | <Text color="primary" weight="medium"> 167 | toutai.cc 168 | </Text> 169 | </View> 170 | </View> 171 | <QRCode 172 | value="https://toutai.cc" 173 | bgColor="#f5f3ef" 174 | className="w-12 h-12" 175 | /> 176 | </View> 177 | </View> 178 | </div> 179 | ); 180 | } 181 | 182 | function ShareStyle3({ shareInfo }: { shareInfo: ShareInfo }) { 183 | return ( 184 | <div 185 | className="w-full bg-[#f5f3ef] relative aspect-square" 186 | id="shareContent" 187 | > 188 | <View 189 | direction="column" 190 | padding={6} 191 | height="100%" 192 | justify="space-between" 193 | > 194 | <ShareMap region={shareInfo.region} /> 195 | <View direction="row" justify="space-between" paddingBottom={4}> 196 | <View direction="column"> 197 | {['香港', '澳门', '台湾'].includes(shareInfo.region) ? ( 198 | <> 199 | <Text variant="body-2" weight="medium" className="z-10"> 200 | 第{' '} 201 | <span className="text-[#ba3700] font-medium"> 202 | {shareInfo.count} 203 | </span>{' '} 204 | 次 205 | </Text> 206 | <Text variant="body-2" weight="medium" className="z-10"> 207 | 我投胎在了 208 | <span className="text-[#ba3700] font-medium"> 209 | {shareInfo.region} 210 | </span> 211 | </Text> 212 | <Text variant="body-2" weight="medium" className="z-10"> 213 | 是一个 214 | <span className="text-[#ba3700] font-medium"> 215 | {translateGenderChild(shareInfo.gender)} 216 | </span> 217 | </Text> 218 | <Text variant="body-2" weight="medium" className="z-10"> 219 | 概率只有{' '} 220 | <span className="text-[#ba3700] font-medium"> 221 | {shareInfo.probability.toPrecision(2)}% 222 | </span> 223 | </Text> 224 | </> 225 | ) : ( 226 | <> 227 | <Text variant="body-2" weight="medium" className="z-10"> 228 | 第{' '} 229 | <span className="text-[#ba3700] font-medium"> 230 | {shareInfo.count} 231 | </span>{' '} 232 | 次 233 | </Text> 234 | <Text variant="body-2" weight="medium" className="z-10"> 235 | 我投胎在了 236 | <span className="text-[#ba3700] font-medium"> 237 | {shareInfo.region}的{shareInfo.category} 238 | </span> 239 | </Text> 240 | <Text variant="body-2" weight="medium" className="z-10"> 241 | 是家里 242 | <span className="text-[#ba3700] font-medium"> 243 | 第{shareInfo.order}个 244 | {translateGenderChild(shareInfo.gender)} 245 | </span> 246 | </Text> 247 | <Text variant="body-2" weight="medium" className="z-10"> 248 | 概率只有{' '} 249 | <span className="text-[#ba3700] font-medium"> 250 | {shareInfo.probability.toPrecision(2)}% 251 | </span> 252 | </Text> 253 | </> 254 | )} 255 | </View> 256 | <View justify="end" height="100%"> 257 | <Text color="neutral-faded">#投胎 #重开</Text> 258 | </View> 259 | </View> 260 | <View direction="row" justify="space-between" align="center"> 261 | <View direction="row" gap={2} align="center"> 262 | <CarrotIcon size={40} /> 263 | <View direction="column"> 264 | <Text color="primary" weight="medium" variant="body-1"> 265 | 投胎模拟器 266 | </Text> 267 | <Text color="primary" weight="medium"> 268 | toutai.cc 269 | </Text> 270 | </View> 271 | </View> 272 | <QRCode 273 | value="https://toutai.cc" 274 | bgColor="#f5f3ef" 275 | className="w-12 h-12" 276 | /> 277 | </View> 278 | </View> 279 | </div> 280 | ); 281 | } 282 | 283 | function ModalFooter({ 284 | onCancel, 285 | onSave 286 | }: { 287 | onCancel: () => void; 288 | onSave: () => void; 289 | }) { 290 | return ( 291 | <View gap={2} direction="row"> 292 | <View.Item columns={6}> 293 | <Button color="primary" variant="faded" fullWidth onClick={onCancel}> 294 | 取消 295 | </Button> 296 | </View.Item> 297 | <View.Item columns={6}> 298 | <Button color="primary" variant="solid" fullWidth onClick={onSave}> 299 | 保存图片 300 | </Button> 301 | </View.Item> 302 | </View> 303 | ); 304 | } 305 | 306 | function ShareModal() { 307 | const { active, deactivate, shareInfo } = useShareModal(); 308 | 309 | function handleSaveAsImage() { 310 | const shareContent = document.getElementById('shareContent'); 311 | 312 | if (shareContent) { 313 | html2canvas(shareContent, { scale: 3 }) 314 | .then(canvas => { 315 | const link = document.createElement('a'); 316 | link.download = '投胎模拟器-第' + shareInfo.count + '次.png'; 317 | link.href = canvas.toDataURL('image/png'); 318 | link.click(); 319 | 320 | toast.custom(t => ( 321 | <div className="relative bg-green-100 w-full sm:w-[354px] p-5 border-green-500 border rounded-xl"> 322 | <div className="flex flex-row justify-between"> 323 | <Text color="positive">图片保存成功!</Text> 324 | </div> 325 | <button 326 | className="absolute top-2 right-3" 327 | onClick={() => toast.dismiss(t)} 328 | > 329 | <Icon color="positive" size={4} svg={<X />} /> 330 | </button> 331 | </div> 332 | )); 333 | }) 334 | .catch(err => console.error('Error capturing image:', err)); 335 | } 336 | } 337 | 338 | return ( 339 | <Modal active={active} onClose={deactivate}> 340 | <View gap={3}> 341 | <Dismissible onClose={deactivate} closeAriaLabel="Close modal"> 342 | <Modal.Title>分享</Modal.Title> 343 | <Modal.Subtitle>分享你的投胎结果</Modal.Subtitle> 344 | </Dismissible> 345 | <Tabs variant="pills"> 346 | <View gap={3}> 347 | <View> 348 | <Tabs.Panel value="1"> 349 | <ShareStyle1 shareInfo={shareInfo} /> 350 | </Tabs.Panel> 351 | <Tabs.Panel value="2"> 352 | <ShareStyle2 shareInfo={shareInfo} /> 353 | </Tabs.Panel> 354 | <Tabs.Panel value="3"> 355 | <ShareStyle3 shareInfo={shareInfo} /> 356 | </Tabs.Panel> 357 | </View> 358 | <Tabs.List> 359 | <Tabs.Item value="1">样式一</Tabs.Item> 360 | <Tabs.Item value="2">样式二</Tabs.Item> 361 | <Tabs.Item value="3">样式三</Tabs.Item> 362 | </Tabs.List> 363 | </View> 364 | </Tabs> 365 | <ModalFooter onCancel={deactivate} onSave={handleSaveAsImage} /> 366 | </View> 367 | </Modal> 368 | ); 369 | } 370 | 371 | export default ShareModal; 372 | -------------------------------------------------------------------------------- /app/_data/world_birthrate.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "country": "Afghanistan", 4 | "name": "阿富汗", 5 | "population": 41.1, 6 | "birthRate": 36 7 | }, 8 | { 9 | "country": "Albania", 10 | "name": "阿尔巴尼亚", 11 | "population": 2.8, 12 | "birthRate": 10 13 | }, 14 | { 15 | "country": "Algeria", 16 | "name": "阿尔及利亚", 17 | "population": 44.9, 18 | "birthRate": 22 19 | }, 20 | { 21 | "country": "American Samoa", 22 | "name": "美属萨摩亚", 23 | "population": 0, 24 | "birthRate": null 25 | }, 26 | { 27 | "country": "Andorra", 28 | "name": "安道尔", 29 | "population": 0.1, 30 | "birthRate": null 31 | }, 32 | { 33 | "country": "Angola", 34 | "name": "安哥拉", 35 | "population": 35.6, 36 | "birthRate": 39 37 | }, 38 | { 39 | "country": "Antigua and Barbuda", 40 | "name": "安提瓜和巴布达", 41 | "population": 0.1, 42 | "birthRate": 12 43 | }, 44 | { 45 | "country": "Argentina", 46 | "name": "阿根廷", 47 | "population": 46.2, 48 | "birthRate": 14 49 | }, 50 | { 51 | "country": "Armenia", 52 | "name": "亚美尼亚", 53 | "population": 2.8, 54 | "birthRate": 12 55 | }, 56 | { 57 | "country": "Aruba", 58 | "name": "阿鲁巴", 59 | "population": 0.1, 60 | "birthRate": 7 61 | }, 62 | { 63 | "country": "Australia", 64 | "name": "澳大利亚", 65 | "population": 26, 66 | "birthRate": 12 67 | }, 68 | { 69 | "country": "Austria", 70 | "name": "奥地利", 71 | "population": 9, 72 | "birthRate": 10 73 | }, 74 | { 75 | "country": "Azerbaijan", 76 | "name": "阿塞拜疆", 77 | "population": 10.1, 78 | "birthRate": 11 79 | }, 80 | { 81 | "country": "Bahamas, The", 82 | "name": "巴哈马", 83 | "population": 0.4, 84 | "birthRate": 11 85 | }, 86 | { 87 | "country": "Bahrain", 88 | "name": "巴林", 89 | "population": 1.5, 90 | "birthRate": 12 91 | }, 92 | { 93 | "country": "Bangladesh", 94 | "name": "孟加拉国", 95 | "population": 171.2, 96 | "birthRate": 18 97 | }, 98 | { 99 | "country": "Barbados", 100 | "name": "巴巴多斯", 101 | "population": 0.3, 102 | "birthRate": 11 103 | }, 104 | { 105 | "country": "Belarus", 106 | "name": "白俄罗斯", 107 | "population": 9.2, 108 | "birthRate": 9 109 | }, 110 | { 111 | "country": "Belgium", 112 | "name": "比利时", 113 | "population": 11.7, 114 | "birthRate": 10 115 | }, 116 | { 117 | "country": "Belize", 118 | "name": "伯利兹", 119 | "population": 0.4, 120 | "birthRate": 18 121 | }, 122 | { 123 | "country": "Benin", 124 | "name": "贝宁", 125 | "population": 13.4, 126 | "birthRate": 37 127 | }, 128 | { 129 | "country": "Bermuda", 130 | "name": "百慕大", 131 | "population": 0.1, 132 | "birthRate": 8 133 | }, 134 | { 135 | "country": "Bhutan", 136 | "name": "不丹", 137 | "population": 0.8, 138 | "birthRate": 12 139 | }, 140 | { 141 | "country": "Bolivia", 142 | "name": "玻利维亚", 143 | "population": 12.2, 144 | "birthRate": 22 145 | }, 146 | { 147 | "country": "Bosnia and Herzegovina", 148 | "name": "波斯尼亚和黑塞哥维那", 149 | "population": 3.2, 150 | "birthRate": 8 151 | }, 152 | { 153 | "country": "Botswana", 154 | "name": "博茨瓦纳", 155 | "population": 2.6, 156 | "birthRate": 24 157 | }, 158 | { 159 | "country": "Brazil", 160 | "name": "巴西", 161 | "population": 215.3, 162 | "birthRate": 13 163 | }, 164 | { 165 | "country": "Brunei Darussalam", 166 | "name": "文莱", 167 | "population": 0.4, 168 | "birthRate": 14 169 | }, 170 | { 171 | "country": "Bulgaria", 172 | "name": "保加利亚", 173 | "population": 6.5, 174 | "birthRate": 9 175 | }, 176 | { 177 | "country": "Burkina Faso", 178 | "name": "布基纳法索", 179 | "population": 22.7, 180 | "birthRate": 36 181 | }, 182 | { 183 | "country": "Burundi", 184 | "name": "布隆迪", 185 | "population": 12.9, 186 | "birthRate": 35 187 | }, 188 | { 189 | "country": "Cabo Verde", 190 | "name": "佛得角", 191 | "population": 0.6, 192 | "birthRate": 17 193 | }, 194 | { 195 | "country": "Cambodia", 196 | "name": "柬埔寨", 197 | "population": 16.8, 198 | "birthRate": 19 199 | }, 200 | { 201 | "country": "Cameroon", 202 | "name": "喀麦隆", 203 | "population": 27.9, 204 | "birthRate": 35 205 | }, 206 | { 207 | "country": "Canada", 208 | "name": "加拿大", 209 | "population": 38.9, 210 | "birthRate": 10 211 | }, 212 | { 213 | "country": "Cayman Islands", 214 | "name": "开曼群岛", 215 | "population": 0.1, 216 | "birthRate": 12 217 | }, 218 | { 219 | "country": "Central African Republic", 220 | "name": "中非共和国", 221 | "population": 5.6, 222 | "birthRate": 43 223 | }, 224 | { 225 | "country": "Chad", 226 | "name": "乍得", 227 | "population": 17.7, 228 | "birthRate": 43 229 | }, 230 | { 231 | "country": "Channel Islands", 232 | "name": "海峡群岛", 233 | "population": 0.2, 234 | "birthRate": 10 235 | }, 236 | { 237 | "country": "Chile", 238 | "name": "智利", 239 | "population": 19.6, 240 | "birthRate": 12 241 | }, 242 | { 243 | "country": "China", 244 | "name": "中国", 245 | "population": 1412.2, 246 | "birthRate": 8 247 | }, 248 | { 249 | "country": "Hong Kong SAR, China", 250 | "name": "中国香港特别行政区", 251 | "population": 7.3, 252 | "birthRate": 5 253 | }, 254 | { 255 | "country": "Macao SAR, China", 256 | "name": "中国澳门特别行政区", 257 | "population": 0.7, 258 | "birthRate": 10 259 | }, 260 | { 261 | "country": "Colombia", 262 | "name": "哥伦比亚", 263 | "population": 51.9, 264 | "birthRate": 14 265 | }, 266 | { 267 | "country": "Comoros", 268 | "name": "科摩罗", 269 | "population": 0.8, 270 | "birthRate": 29 271 | }, 272 | { 273 | "country": "Congo, Dem. Rep.", 274 | "name": "刚果民主共和国", 275 | "population": 99, 276 | "birthRate": 42 277 | }, 278 | { 279 | "country": "Congo, Rep.", 280 | "name": "刚果共和国", 281 | "population": 6, 282 | "birthRate": 31 283 | }, 284 | { 285 | "country": "Costa Rica", 286 | "name": "哥斯达黎加", 287 | "population": 5.2, 288 | "birthRate": 12 289 | }, 290 | { 291 | "country": "Cote d'Ivoire", 292 | "name": "科特迪瓦", 293 | "population": 28.2, 294 | "birthRate": 34 295 | }, 296 | { 297 | "country": "Croatia", 298 | "name": "克罗地亚", 299 | "population": 3.9, 300 | "birthRate": 9 301 | }, 302 | { 303 | "country": "Cuba", 304 | "name": "古巴", 305 | "population": 11.2, 306 | "birthRate": 9 307 | }, 308 | { 309 | "country": "Curacao", 310 | "name": "库拉索", 311 | "population": 0.1, 312 | "birthRate": 9 313 | }, 314 | { 315 | "country": "Cyprus", 316 | "name": "塞浦路斯", 317 | "population": 1.3, 318 | "birthRate": 10 319 | }, 320 | { 321 | "country": "Czechia", 322 | "name": "捷克", 323 | "population": 10.7, 324 | "birthRate": 11 325 | }, 326 | { 327 | "country": "Denmark", 328 | "name": "丹麦", 329 | "population": 5.9, 330 | "birthRate": 11 331 | }, 332 | { 333 | "country": "Djibouti", 334 | "name": "吉布提", 335 | "population": 1.1, 336 | "birthRate": 22 337 | }, 338 | { 339 | "country": "Dominica", 340 | "name": "多米尼克", 341 | "population": 0.1, 342 | "birthRate": 13 343 | }, 344 | { 345 | "country": "Dominican Republic", 346 | "name": "多米尼加共和国", 347 | "population": 11.2, 348 | "birthRate": 18 349 | }, 350 | { 351 | "country": "Ecuador", 352 | "name": "厄瓜多尔", 353 | "population": 18, 354 | "birthRate": 17 355 | }, 356 | { 357 | "country": "Egypt, Arab Rep.", 358 | "name": "埃及", 359 | "population": 111, 360 | "birthRate": 23 361 | }, 362 | { 363 | "country": "El Salvador", 364 | "name": "萨尔瓦多", 365 | "population": 6.3, 366 | "birthRate": 16 367 | }, 368 | { 369 | "country": "Equatorial Guinea", 370 | "name": "赤道几内亚", 371 | "population": 1.7, 372 | "birthRate": 30 373 | }, 374 | { 375 | "country": "Eritrea", 376 | "name": "厄立特里亚", 377 | "population": 3.7, 378 | "birthRate": 29 379 | }, 380 | { 381 | "country": "Estonia", 382 | "name": "爱沙尼亚", 383 | "population": 1.3, 384 | "birthRate": 10 385 | }, 386 | { 387 | "country": "Eswatini", 388 | "name": "斯威士兰", 389 | "population": 1.2, 390 | "birthRate": 24 391 | }, 392 | { 393 | "country": "Ethiopia", 394 | "name": "埃塞俄比亚", 395 | "population": 123.4, 396 | "birthRate": 32 397 | }, 398 | { 399 | "country": "Faroe Islands", 400 | "name": "法罗群岛", 401 | "population": 0.1, 402 | "birthRate": 13 403 | }, 404 | { 405 | "country": "Fiji", 406 | "name": "斐济", 407 | "population": 0.9, 408 | "birthRate": 19 409 | }, 410 | { 411 | "country": "Finland", 412 | "name": "芬兰", 413 | "population": 5.6, 414 | "birthRate": 9 415 | }, 416 | { 417 | "country": "France", 418 | "name": "法国", 419 | "population": 68, 420 | "birthRate": 11 421 | }, 422 | { 423 | "country": "French Polynesia", 424 | "name": "法属波利尼西亚", 425 | "population": 0.3, 426 | "birthRate": 13 427 | }, 428 | { 429 | "country": "Gabon", 430 | "name": "加蓬", 431 | "population": 2.4, 432 | "birthRate": 27 433 | }, 434 | { 435 | "country": "Gambia, The", 436 | "name": "冈比亚", 437 | "population": 2.7, 438 | "birthRate": 33 439 | }, 440 | { 441 | "country": "Georgia", 442 | "name": "格鲁吉亚", 443 | "population": 3.7, 444 | "birthRate": 13 445 | }, 446 | { 447 | "country": "Germany", 448 | "name": "德国", 449 | "population": 83.8, 450 | "birthRate": 10 451 | }, 452 | { 453 | "country": "Ghana", 454 | "name": "加纳", 455 | "population": 33.5, 456 | "birthRate": 28 457 | }, 458 | { 459 | "country": "Greece", 460 | "name": "希腊", 461 | "population": 10.4, 462 | "birthRate": 8 463 | }, 464 | { 465 | "country": "Greenland", 466 | "name": "格陵兰", 467 | "population": 0.1, 468 | "birthRate": 13 469 | }, 470 | { 471 | "country": "Grenada", 472 | "name": "格林纳达", 473 | "population": 0.1, 474 | "birthRate": 16 475 | }, 476 | { 477 | "country": "Guam", 478 | "name": "关岛", 479 | "population": 0.2, 480 | "birthRate": 16 481 | }, 482 | { 483 | "country": "Guatemala", 484 | "name": "危地马拉", 485 | "population": 17.4, 486 | "birthRate": 21 487 | }, 488 | { 489 | "country": "Guinea", 490 | "name": "几内亚", 491 | "population": 13.9, 492 | "birthRate": 34 493 | }, 494 | { 495 | "country": "Guinea-Bissau", 496 | "name": "几内亚比绍", 497 | "population": 2.1, 498 | "birthRate": 31 499 | }, 500 | { 501 | "country": "Guyana", 502 | "name": "圭亚那", 503 | "population": 0.8, 504 | "birthRate": 20 505 | }, 506 | { 507 | "country": "Haiti", 508 | "name": "海地", 509 | "population": 11.6, 510 | "birthRate": 23 511 | }, 512 | { 513 | "country": "Honduras", 514 | "name": "洪都拉斯", 515 | "population": 10.4, 516 | "birthRate": 21 517 | }, 518 | { 519 | "country": "Hungary", 520 | "name": "匈牙利", 521 | "population": 9.6, 522 | "birthRate": 10 523 | }, 524 | { 525 | "country": "Iceland", 526 | "name": "冰岛", 527 | "population": 0.4, 528 | "birthRate": 13 529 | }, 530 | { 531 | "country": "India", 532 | "name": "印度", 533 | "population": 1417.2, 534 | "birthRate": 16 535 | }, 536 | { 537 | "country": "Indonesia", 538 | "name": "印度尼西亚", 539 | "population": 275.5, 540 | "birthRate": 16 541 | }, 542 | { 543 | "country": "Iran, Islamic Rep.", 544 | "name": "伊朗", 545 | "population": 88.6, 546 | "birthRate": 14 547 | }, 548 | { 549 | "country": "Iraq", 550 | "name": "伊拉克", 551 | "population": 44.5, 552 | "birthRate": 27 553 | }, 554 | { 555 | "country": "Ireland", 556 | "name": "爱尔兰", 557 | "population": 5.1, 558 | "birthRate": 12 559 | }, 560 | { 561 | "country": "Isle of Man", 562 | "name": "马恩岛", 563 | "population": 0.1, 564 | "birthRate": 9 565 | }, 566 | { 567 | "country": "Israel", 568 | "name": "以色列", 569 | "population": 9.6, 570 | "birthRate": 20 571 | }, 572 | { 573 | "country": "Italy", 574 | "name": "意大利", 575 | "population": 58.9, 576 | "birthRate": 7 577 | }, 578 | { 579 | "country": "Jamaica", 580 | "name": "牙买加", 581 | "population": 2.8, 582 | "birthRate": 12 583 | }, 584 | { 585 | "country": "Japan", 586 | "name": "日本", 587 | "population": 125.1, 588 | "birthRate": 7 589 | }, 590 | { 591 | "country": "Jordan", 592 | "name": "约旦", 593 | "population": 11.3, 594 | "birthRate": 22 595 | }, 596 | { 597 | "country": "Kazakhstan", 598 | "name": "哈萨克斯坦", 599 | "population": 19.6, 600 | "birthRate": 24 601 | }, 602 | { 603 | "country": "Kenya", 604 | "name": "肯尼亚", 605 | "population": 54, 606 | "birthRate": 28 607 | }, 608 | { 609 | "country": "Kiribati", 610 | "name": "基里巴斯", 611 | "population": 0.1, 612 | "birthRate": 27 613 | }, 614 | { 615 | "country": "Korea, Dem. People's Rep.", 616 | "name": "朝鲜", 617 | "population": 26.1, 618 | "birthRate": 13 619 | }, 620 | { 621 | "country": "Korea, Rep.", 622 | "name": "韩国", 623 | "population": 51.6, 624 | "birthRate": 5 625 | }, 626 | { 627 | "country": "Kosovo", 628 | "name": "科索沃", 629 | "population": 1.8, 630 | "birthRate": 11 631 | }, 632 | { 633 | "country": "Kuwait", 634 | "name": "科威特", 635 | "population": 4.3, 636 | "birthRate": 10 637 | }, 638 | { 639 | "country": "Kyrgyz Republic", 640 | "name": "吉尔吉斯斯坦", 641 | "population": 7, 642 | "birthRate": 22 643 | }, 644 | { 645 | "country": "Lao PDR", 646 | "name": "老挝", 647 | "population": 7.5, 648 | "birthRate": 22 649 | }, 650 | { 651 | "country": "Latvia", 652 | "name": "拉脱维亚", 653 | "population": 1.9, 654 | "birthRate": 9 655 | }, 656 | { 657 | "country": "Lebanon", 658 | "name": "黎巴嫩", 659 | "population": 5.5, 660 | "birthRate": 15 661 | }, 662 | { 663 | "country": "Lesotho", 664 | "name": "莱索托", 665 | "population": 2.3, 666 | "birthRate": 26 667 | }, 668 | { 669 | "country": "Liberia", 670 | "name": "利比里亚", 671 | "population": 5.3, 672 | "birthRate": 31 673 | }, 674 | { 675 | "country": "Libya", 676 | "name": "利比亚", 677 | "population": 6.8, 678 | "birthRate": 18 679 | }, 680 | { 681 | "country": "Liechtenstein", 682 | "name": "列支敦士登", 683 | "population": 0, 684 | "birthRate": 10 685 | }, 686 | { 687 | "country": "Lithuania", 688 | "name": "立陶宛", 689 | "population": 2.8, 690 | "birthRate": 8 691 | }, 692 | { 693 | "country": "Luxembourg", 694 | "name": "卢森堡", 695 | "population": 0.7, 696 | "birthRate": 11 697 | }, 698 | { 699 | "country": "Madagascar", 700 | "name": "马达加斯加", 701 | "population": 29.6, 702 | "birthRate": 31 703 | }, 704 | { 705 | "country": "Malawi", 706 | "name": "马拉维", 707 | "population": 20.4, 708 | "birthRate": 33 709 | }, 710 | { 711 | "country": "Malaysia", 712 | "name": "马来西亚", 713 | "population": 33.9, 714 | "birthRate": 15 715 | }, 716 | { 717 | "country": "Maldives", 718 | "name": "马尔代夫", 719 | "population": 0.5, 720 | "birthRate": 14 721 | }, 722 | { 723 | "country": "Mali", 724 | "name": "马里", 725 | "population": 22.6, 726 | "birthRate": 42 727 | }, 728 | { 729 | "country": "Malta", 730 | "name": "马耳他", 731 | "population": 0.5, 732 | "birthRate": 9 733 | }, 734 | { 735 | "country": "Marshall Islands", 736 | "name": "马绍尔群岛", 737 | "population": 0, 738 | "birthRate": 19 739 | }, 740 | { 741 | "country": "Mauritania", 742 | "name": "毛里塔尼亚", 743 | "population": 4.7, 744 | "birthRate": 33 745 | }, 746 | { 747 | "country": "Mauritius", 748 | "name": "毛里求斯", 749 | "population": 1.3, 750 | "birthRate": 10 751 | }, 752 | { 753 | "country": "Mexico", 754 | "name": "墨西哥", 755 | "population": 127.5, 756 | "birthRate": 15 757 | }, 758 | { 759 | "country": "Micronesia, Fed. Sts.", 760 | "name": "密克罗尼西亚联邦", 761 | "population": 0.1, 762 | "birthRate": 21 763 | }, 764 | { 765 | "country": "Moldova", 766 | "name": "摩尔多瓦", 767 | "population": 2.5, 768 | "birthRate": 12 769 | }, 770 | { 771 | "country": "Monaco", 772 | "name": "摩纳哥", 773 | "population": 0, 774 | "birthRate": null 775 | }, 776 | { 777 | "country": "Mongolia", 778 | "name": "蒙古", 779 | "population": 3.4, 780 | "birthRate": 21 781 | }, 782 | { 783 | "country": "Montenegro", 784 | "name": "黑山", 785 | "population": 0.6, 786 | "birthRate": 11 787 | }, 788 | { 789 | "country": "Morocco", 790 | "name": "摩洛哥", 791 | "population": 37.5, 792 | "birthRate": 18 793 | }, 794 | { 795 | "country": "Mozambique", 796 | "name": "莫桑比克", 797 | "population": 33, 798 | "birthRate": 37 799 | }, 800 | { 801 | "country": "Myanmar", 802 | "name": "缅甸", 803 | "population": 54.2, 804 | "birthRate": 17 805 | }, 806 | { 807 | "country": "Namibia", 808 | "name": "纳米比亚", 809 | "population": 2.6, 810 | "birthRate": 27 811 | }, 812 | { 813 | "country": "Nepal", 814 | "name": "尼泊尔", 815 | "population": 30.5, 816 | "birthRate": 20 817 | }, 818 | { 819 | "country": "Netherlands", 820 | "name": "荷兰", 821 | "population": 17.7, 822 | "birthRate": 10 823 | }, 824 | { 825 | "country": "New Caledonia", 826 | "name": "新喀里多尼亚", 827 | "population": 0.3, 828 | "birthRate": 14 829 | }, 830 | { 831 | "country": "New Zealand", 832 | "name": "新西兰", 833 | "population": 5.1, 834 | "birthRate": 11 835 | }, 836 | { 837 | "country": "Nicaragua", 838 | "name": "尼加拉瓜", 839 | "population": 6.9, 840 | "birthRate": 21 841 | }, 842 | { 843 | "country": "Niger", 844 | "name": "尼日尔", 845 | "population": 26.2, 846 | "birthRate": 45 847 | }, 848 | { 849 | "country": "Nigeria", 850 | "name": "尼日利亚", 851 | "population": 218.5, 852 | "birthRate": 37 853 | }, 854 | { 855 | "country": "North Macedonia", 856 | "name": "北马其顿", 857 | "population": 2.1, 858 | "birthRate": 10 859 | }, 860 | { 861 | "country": "Northern Mariana Islands", 862 | "name": "北马里亚纳群岛", 863 | "population": 0, 864 | "birthRate": null 865 | }, 866 | { 867 | "country": "Norway", 868 | "name": "挪威", 869 | "population": 5.5, 870 | "birthRate": 10 871 | }, 872 | { 873 | "country": "Oman", 874 | "name": "阿曼", 875 | "population": 4.6, 876 | "birthRate": 18 877 | }, 878 | { 879 | "country": "Pakistan", 880 | "name": "巴基斯坦", 881 | "population": 235.8, 882 | "birthRate": 28 883 | }, 884 | { 885 | "country": "Palau", 886 | "name": "帕劳", 887 | "population": 0, 888 | "birthRate": 13 889 | }, 890 | { 891 | "country": "Panama", 892 | "name": "巴拿马", 893 | "population": 4.4, 894 | "birthRate": 18 895 | }, 896 | { 897 | "country": "Papua New Guinea", 898 | "name": "巴布亚新几内亚", 899 | "population": 10.1, 900 | "birthRate": 26 901 | }, 902 | { 903 | "country": "Paraguay", 904 | "name": "巴拉圭", 905 | "population": 6.8, 906 | "birthRate": 21 907 | }, 908 | { 909 | "country": "Peru", 910 | "name": "秘鲁", 911 | "population": 34, 912 | "birthRate": 18 913 | }, 914 | { 915 | "country": "Philippines", 916 | "name": "菲律宾", 917 | "population": 115.6, 918 | "birthRate": 22 919 | }, 920 | { 921 | "country": "Poland", 922 | "name": "波兰", 923 | "population": 36.8, 924 | "birthRate": 9 925 | }, 926 | { 927 | "country": "Portugal", 928 | "name": "葡萄牙", 929 | "population": 10.4, 930 | "birthRate": 8 931 | }, 932 | { 933 | "country": "Puerto Rico", 934 | "name": "波多黎各", 935 | "population": 3.2, 936 | "birthRate": 6 937 | }, 938 | { 939 | "country": "Qatar", 940 | "name": "卡塔尔", 941 | "population": 2.7, 942 | "birthRate": 10 943 | }, 944 | { 945 | "country": "Romania", 946 | "name": "罗马尼亚", 947 | "population": 19, 948 | "birthRate": 9 949 | }, 950 | { 951 | "country": "Russian Federation", 952 | "name": "俄罗斯", 953 | "population": 144.2, 954 | "birthRate": 10 955 | }, 956 | { 957 | "country": "Rwanda", 958 | "name": "卢旺达", 959 | "population": 13.8, 960 | "birthRate": 30 961 | }, 962 | { 963 | "country": "Samoa", 964 | "name": "萨摩亚", 965 | "population": 0.2, 966 | "birthRate": 27 967 | }, 968 | { 969 | "country": "San Marino", 970 | "name": "圣马力诺", 971 | "population": 0, 972 | "birthRate": 6 973 | }, 974 | { 975 | "country": "Sao Tome and Principe", 976 | "name": "圣多美和普林西比", 977 | "population": 0.2, 978 | "birthRate": 28 979 | }, 980 | { 981 | "country": "Saudi Arabia", 982 | "name": "沙特阿拉伯", 983 | "population": 36.4, 984 | "birthRate": 17 985 | }, 986 | { 987 | "country": "Senegal", 988 | "name": "塞内加尔", 989 | "population": 17.3, 990 | "birthRate": 33 991 | }, 992 | { 993 | "country": "Serbia", 994 | "name": "塞尔维亚", 995 | "population": 6.7, 996 | "birthRate": 9 997 | }, 998 | { 999 | "country": "Seychelles", 1000 | "name": "塞舌尔", 1001 | "population": 0.1, 1002 | "birthRate": 17 1003 | }, 1004 | { 1005 | "country": "Sierra Leone", 1006 | "name": "塞拉利昂", 1007 | "population": 8.6, 1008 | "birthRate": 31 1009 | }, 1010 | { 1011 | "country": "Singapore", 1012 | "name": "新加坡", 1013 | "population": 5.6, 1014 | "birthRate": 9 1015 | }, 1016 | { 1017 | "country": "Sint Maarten (Dutch part)", 1018 | "name": "荷属圣马丁", 1019 | "population": 0, 1020 | "birthRate": 11 1021 | }, 1022 | { 1023 | "country": "Slovak Republic", 1024 | "name": "斯洛伐克", 1025 | "population": 5.4, 1026 | "birthRate": 10 1027 | }, 1028 | { 1029 | "country": "Slovenia", 1030 | "name": "斯洛文尼亚", 1031 | "population": 2.1, 1032 | "birthRate": 9 1033 | }, 1034 | { 1035 | "country": "Solomon Islands", 1036 | "name": "所罗门群岛", 1037 | "population": 0.7, 1038 | "birthRate": 30 1039 | }, 1040 | { 1041 | "country": "Somalia", 1042 | "name": "索马里", 1043 | "population": 17.6, 1044 | "birthRate": 44 1045 | }, 1046 | { 1047 | "country": "South Africa", 1048 | "name": "南非", 1049 | "population": 59.9, 1050 | "birthRate": 20 1051 | }, 1052 | { 1053 | "country": "South Sudan", 1054 | "name": "南苏丹", 1055 | "population": 10.9, 1056 | "birthRate": 29 1057 | }, 1058 | { 1059 | "country": "Spain", 1060 | "name": "西班牙", 1061 | "population": 47.8, 1062 | "birthRate": 7 1063 | }, 1064 | { 1065 | "country": "Sri Lanka", 1066 | "name": "斯里兰卡", 1067 | "population": 22.2, 1068 | "birthRate": 14 1069 | }, 1070 | { 1071 | "country": "St. Kitts and Nevis", 1072 | "name": "圣基茨和尼维斯", 1073 | "population": 0, 1074 | "birthRate": 12 1075 | }, 1076 | { 1077 | "country": "St. Lucia", 1078 | "name": "圣卢西亚", 1079 | "population": 0.2, 1080 | "birthRate": 11 1081 | }, 1082 | { 1083 | "country": "St. Martin (French part)", 1084 | "name": "法属圣马丁", 1085 | "population": 0, 1086 | "birthRate": 15 1087 | }, 1088 | { 1089 | "country": "St. Vincent and the Grenadines", 1090 | "name": "圣文森特和格林纳丁斯", 1091 | "population": 0.1, 1092 | "birthRate": 13 1093 | }, 1094 | { 1095 | "country": "Sudan", 1096 | "name": "苏丹", 1097 | "population": 46.9, 1098 | "birthRate": 34 1099 | }, 1100 | { 1101 | "country": "Suriname", 1102 | "name": "苏里南", 1103 | "population": 0.6, 1104 | "birthRate": 18 1105 | }, 1106 | { 1107 | "country": "Sweden", 1108 | "name": "瑞典", 1109 | "population": 10.5, 1110 | "birthRate": 11 1111 | }, 1112 | { 1113 | "country": "Switzerland", 1114 | "name": "瑞士", 1115 | "population": 8.8, 1116 | "birthRate": 10 1117 | }, 1118 | { 1119 | "country": "Syrian Arab Republic", 1120 | "name": "叙利亚", 1121 | "population": 22.1, 1122 | "birthRate": 20 1123 | }, 1124 | { 1125 | "country": "Tajikistan", 1126 | "name": "塔吉克斯坦", 1127 | "population": 10, 1128 | "birthRate": 27 1129 | }, 1130 | { 1131 | "country": "Tanzania", 1132 | "name": "坦桑尼亚", 1133 | "population": 65.5, 1134 | "birthRate": 36 1135 | }, 1136 | { 1137 | "country": "Thailand", 1138 | "name": "泰国", 1139 | "population": 71.7, 1140 | "birthRate": 9 1141 | }, 1142 | { 1143 | "country": "Timor-Leste", 1144 | "name": "东帝汶", 1145 | "population": 1.3, 1146 | "birthRate": 25 1147 | }, 1148 | { 1149 | "country": "Togo", 1150 | "name": "多哥", 1151 | "population": 8.8, 1152 | "birthRate": 32 1153 | }, 1154 | { 1155 | "country": "Tonga", 1156 | "name": "汤加", 1157 | "population": 0.1, 1158 | "birthRate": 23 1159 | }, 1160 | { 1161 | "country": "Trinidad and Tobago", 1162 | "name": "特立尼达和多巴哥", 1163 | "population": 1.5, 1164 | "birthRate": 12 1165 | }, 1166 | { 1167 | "country": "Tunisia", 1168 | "name": "突尼斯", 1169 | "population": 12.4, 1170 | "birthRate": 16 1171 | }, 1172 | { 1173 | "country": "Turkiye", 1174 | "name": "土耳其", 1175 | "population": 85, 1176 | "birthRate": 15 1177 | }, 1178 | { 1179 | "country": "Turkmenistan", 1180 | "name": "土库曼斯坦", 1181 | "population": 6.4, 1182 | "birthRate": 22 1183 | }, 1184 | { 1185 | "country": "Turks and Caicos Islands", 1186 | "name": "特克斯和凯科斯群岛", 1187 | "population": 0, 1188 | "birthRate": 12 1189 | }, 1190 | { 1191 | "country": "Tuvalu", 1192 | "name": "图瓦卢", 1193 | "population": 0, 1194 | "birthRate": 23 1195 | }, 1196 | { 1197 | "country": "Uganda", 1198 | "name": "乌干达", 1199 | "population": 47.2, 1200 | "birthRate": 37 1201 | }, 1202 | { 1203 | "country": "Ukraine", 1204 | "name": "乌克兰", 1205 | "population": 38, 1206 | "birthRate": 7 1207 | }, 1208 | { 1209 | "country": "United Arab Emirates", 1210 | "name": "阿拉伯联合酋长国", 1211 | "population": 9.4, 1212 | "birthRate": 10 1213 | }, 1214 | { 1215 | "country": "United Kingdom", 1216 | "name": "英国", 1217 | "population": 67, 1218 | "birthRate": 10 1219 | }, 1220 | { 1221 | "country": "United States", 1222 | "name": "美国", 1223 | "population": 333.3, 1224 | "birthRate": 11 1225 | }, 1226 | { 1227 | "country": "Uruguay", 1228 | "name": "乌拉圭", 1229 | "population": 3.4, 1230 | "birthRate": 10 1231 | }, 1232 | { 1233 | "country": "Uzbekistan", 1234 | "name": "乌兹别克斯坦", 1235 | "population": 35.6, 1236 | "birthRate": 26 1237 | }, 1238 | { 1239 | "country": "Vanuatu", 1240 | "name": "瓦努阿图", 1241 | "population": 0.3, 1242 | "birthRate": 29 1243 | }, 1244 | { 1245 | "country": "Venezuela, RB", 1246 | "name": "委内瑞拉", 1247 | "population": 28.3, 1248 | "birthRate": 16 1249 | }, 1250 | { 1251 | "country": "Viet Nam", 1252 | "name": "越南", 1253 | "population": 98.2, 1254 | "birthRate": 15 1255 | }, 1256 | { 1257 | "country": "Virgin Islands (U.S.)", 1258 | "name": "美属维尔京群岛", 1259 | "population": 0.1, 1260 | "birthRate": 12 1261 | }, 1262 | { 1263 | "country": "West Bank and Gaza", 1264 | "name": "西岸和加沙", 1265 | "population": 5, 1266 | "birthRate": 28 1267 | }, 1268 | { 1269 | "country": "Yemen, Rep.", 1270 | "name": "也门", 1271 | "population": 33.7, 1272 | "birthRate": 31 1273 | }, 1274 | { 1275 | "country": "Zambia", 1276 | "name": "赞比亚", 1277 | "population": 20, 1278 | "birthRate": 35 1279 | }, 1280 | { 1281 | "country": "Zimbabwe", 1282 | "name": "津巴布韦", 1283 | "population": 16.3, 1284 | "birthRate": 31 1285 | } 1286 | ] -------------------------------------------------------------------------------- /app/_data/birthrate_detailed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "national", 5 | "display_name": "全国", 6 | "town": { 7 | "one": { 8 | "male": 68521, 9 | "female": 59884 10 | }, 11 | "two": { 12 | "male": 70420, 13 | "female": 66008 14 | }, 15 | "three": { 16 | "male": 16771, 17 | "female": 12333 18 | }, 19 | "four": { 20 | "male": 2592, 21 | "female": 1914 22 | }, 23 | "five_plus": { 24 | "male": 708, 25 | "female": 541 26 | } 27 | }, 28 | "city": { 29 | "one": { 30 | "male": 142438, 31 | "female": 127127 32 | }, 33 | "two": { 34 | "male": 113936, 35 | "female": 105184 36 | }, 37 | "three": { 38 | "male": 16248, 39 | "female": 11005 40 | }, 41 | "four": { 42 | "male": 1980, 43 | "female": 1664 44 | }, 45 | "five_plus": { 46 | "male": 446, 47 | "female": 273 48 | } 49 | }, 50 | "countryside": { 51 | "one": { 52 | "male": 83698, 53 | "female": 73362 54 | }, 55 | "two": { 56 | "male": 85359, 57 | "female": 81394 58 | }, 59 | "three": { 60 | "male": 29369, 61 | "female": 23594 62 | }, 63 | "four": { 64 | "male": 6453, 65 | "female": 4898 66 | }, 67 | "five_plus": { 68 | "male": 2299, 69 | "female": 1902 70 | } 71 | } 72 | }, 73 | { 74 | "id": 2, 75 | "name": "bei_jing", 76 | "display_name": "北京", 77 | "city": { 78 | "one": { 79 | "male": 4603, 80 | "female": 4114 81 | }, 82 | "two": { 83 | "male": 2411, 84 | "female": 2230 85 | }, 86 | "three": { 87 | "male": 128, 88 | "female": 98 89 | }, 90 | "four": { 91 | "male": 10, 92 | "female": 12 93 | }, 94 | "five_plus": { 95 | "male": 1, 96 | "female": 7 97 | } 98 | }, 99 | "town": { 100 | "one": { 101 | "male": 337, 102 | "female": 305 103 | }, 104 | "two": { 105 | "male": 222, 106 | "female": 215 107 | }, 108 | "three": { 109 | "male": 8, 110 | "female": 12 111 | }, 112 | "four": { 113 | "male": 0, 114 | "female": 1 115 | }, 116 | "five_plus": { 117 | "male": 1, 118 | "female": 0 119 | } 120 | }, 121 | "countryside": { 122 | "one": { 123 | "male": 606, 124 | "female": 549 125 | }, 126 | "two": { 127 | "male": 465, 128 | "female": 445 129 | }, 130 | "three": { 131 | "male": 21, 132 | "female": 21 133 | }, 134 | "four": { 135 | "male": 4, 136 | "female": 2 137 | }, 138 | "five_plus": { 139 | "male": 0, 140 | "female": 0 141 | } 142 | } 143 | }, 144 | { 145 | "id": 3, 146 | "name": "tian_jin", 147 | "display_name": "天津", 148 | "city": { 149 | "one": { 150 | "male": 2171, 151 | "female": 1978 152 | }, 153 | "two": { 154 | "male": 1207, 155 | "female": 1162 156 | }, 157 | "three": { 158 | "male": 88, 159 | "female": 64 160 | }, 161 | "four": { 162 | "male": 12, 163 | "female": 8 164 | }, 165 | "five_plus": { 166 | "male": 0, 167 | "female": 1 168 | } 169 | }, 170 | "town": { 171 | "one": { 172 | "male": 115, 173 | "female": 95 174 | }, 175 | "two": { 176 | "male": 105, 177 | "female": 109 178 | }, 179 | "three": { 180 | "male": 13, 181 | "female": 13 182 | }, 183 | "four": { 184 | "male": 1, 185 | "female": 0 186 | }, 187 | "five_plus": { 188 | "male": 0, 189 | "female": 0 190 | } 191 | }, 192 | "countryside": { 193 | "one": { 194 | "male": 274, 195 | "female": 229 196 | }, 197 | "two": { 198 | "male": 250, 199 | "female": 271 200 | }, 201 | "three": { 202 | "male": 39, 203 | "female": 17 204 | }, 205 | "four": { 206 | "male": 2, 207 | "female": 1 208 | }, 209 | "five_plus": { 210 | "male": 1, 211 | "female": 0 212 | } 213 | } 214 | }, 215 | { 216 | "id": 4, 217 | "name": "he_bei", 218 | "display_name": "河北", 219 | "city": { 220 | "one": { 221 | "male": 4905, 222 | "female": 4489 223 | }, 224 | "two": { 225 | "male": 4601, 226 | "female": 4440 227 | }, 228 | "three": { 229 | "male": 533, 230 | "female": 364 231 | }, 232 | "four": { 233 | "male": 48, 234 | "female": 47 235 | }, 236 | "five_plus": { 237 | "male": 8, 238 | "female": 8 239 | } 240 | }, 241 | "town": { 242 | "one": { 243 | "male": 4120, 244 | "female": 3791 245 | }, 246 | "two": { 247 | "male": 4795, 248 | "female": 4648 249 | }, 250 | "three": { 251 | "male": 1189, 252 | "female": 867 253 | }, 254 | "four": { 255 | "male": 152, 256 | "female": 98 257 | }, 258 | "five_plus": { 259 | "male": 24, 260 | "female": 13 261 | } 262 | }, 263 | "countryside": { 264 | "one": { 265 | "male": 3674, 266 | "female": 3326 267 | }, 268 | "two": { 269 | "male": 4919, 270 | "female": 4900 271 | }, 272 | "three": { 273 | "male": 1807, 274 | "female": 1427 275 | }, 276 | "four": { 277 | "male": 259, 278 | "female": 176 279 | }, 280 | "five_plus": { 281 | "male": 53, 282 | "female": 31 283 | } 284 | } 285 | }, 286 | { 287 | "id": 5, 288 | "name": "shan_xi_2", 289 | "display_name": "山西", 290 | "city": { 291 | "one": { 292 | "male": 3323, 293 | "female": 3073 294 | }, 295 | "two": { 296 | "male": 2629, 297 | "female": 2573 298 | }, 299 | "three": { 300 | "male": 206, 301 | "female": 146 302 | }, 303 | "four": { 304 | "male": 19, 305 | "female": 20 306 | }, 307 | "five_plus": { 308 | "male": 6, 309 | "female": 0 310 | } 311 | }, 312 | "town": { 313 | "one": { 314 | "male": 1891, 315 | "female": 1794 316 | }, 317 | "two": { 318 | "male": 1980, 319 | "female": 2026 320 | }, 321 | "three": { 322 | "male": 238, 323 | "female": 157 324 | }, 325 | "four": { 326 | "male": 19, 327 | "female": 17 328 | }, 329 | "five_plus": { 330 | "male": 3, 331 | "female": 1 332 | } 333 | }, 334 | "countryside": { 335 | "one": { 336 | "male": 2464, 337 | "female": 2322 338 | }, 339 | "two": { 340 | "male": 2037, 341 | "female": 2265 342 | }, 343 | "three": { 344 | "male": 241, 345 | "female": 231 346 | }, 347 | "four": { 348 | "male": 45, 349 | "female": 40 350 | }, 351 | "five_plus": { 352 | "male": 2, 353 | "female": 7 354 | } 355 | } 356 | }, 357 | { 358 | "id": 6, 359 | "name": "nei_meng_gu", 360 | "display_name": "内蒙古", 361 | "city": { 362 | "one": { 363 | "male": 2462, 364 | "female": 2312 365 | }, 366 | "two": { 367 | "male": 1621, 368 | "female": 1572 369 | }, 370 | "three": { 371 | "male": 100, 372 | "female": 84 373 | }, 374 | "four": { 375 | "male": 4, 376 | "female": 18 377 | }, 378 | "five_plus": { 379 | "male": 2, 380 | "female": 1 381 | } 382 | }, 383 | "town": { 384 | "one": { 385 | "male": 1641, 386 | "female": 1598 387 | }, 388 | "two": { 389 | "male": 1454, 390 | "female": 1370 391 | }, 392 | "three": { 393 | "male": 125, 394 | "female": 96 395 | }, 396 | "four": { 397 | "male": 8, 398 | "female": 16 399 | }, 400 | "five_plus": { 401 | "male": 4, 402 | "female": 0 403 | } 404 | }, 405 | "countryside": { 406 | "one": { 407 | "male": 902, 408 | "female": 819 409 | }, 410 | "two": { 411 | "male": 983, 412 | "female": 958 413 | }, 414 | "three": { 415 | "male": 141, 416 | "female": 107 417 | }, 418 | "four": { 419 | "male": 15, 420 | "female": 10 421 | }, 422 | "five_plus": { 423 | "male": 3, 424 | "female": 2 425 | } 426 | } 427 | }, 428 | { 429 | "id": 7, 430 | "name": "liao_ning", 431 | "display_name": "辽宁", 432 | "city": { 433 | "one": { 434 | "male": 5869, 435 | "female": 5297 436 | }, 437 | "two": { 438 | "male": 2318, 439 | "female": 2277 440 | }, 441 | "three": { 442 | "male": 129, 443 | "female": 91 444 | }, 445 | "four": { 446 | "male": 16, 447 | "female": 9 448 | }, 449 | "five_plus": { 450 | "male": 3, 451 | "female": 2 452 | } 453 | }, 454 | "town": { 455 | "one": { 456 | "male": 915, 457 | "female": 795 458 | }, 459 | "two": { 460 | "male": 517, 461 | "female": 561 462 | }, 463 | "three": { 464 | "male": 33, 465 | "female": 21 466 | }, 467 | "four": { 468 | "male": 5, 469 | "female": 5 470 | }, 471 | "five_plus": { 472 | "male": 0, 473 | "female": 0 474 | } 475 | }, 476 | "countryside": { 477 | "one": { 478 | "male": 974, 479 | "female": 936 480 | }, 481 | "two": { 482 | "male": 758, 483 | "female": 805 484 | }, 485 | "three": { 486 | "male": 103, 487 | "female": 62 488 | }, 489 | "four": { 490 | "male": 9, 491 | "female": 10 492 | }, 493 | "five_plus": { 494 | "male": 1, 495 | "female": 2 496 | } 497 | } 498 | }, 499 | { 500 | "id": 8, 501 | "name": "ji_lin", 502 | "display_name": "吉林", 503 | "city": { 504 | "one": { 505 | "male": 2153, 506 | "female": 2057 507 | }, 508 | "two": { 509 | "male": 830, 510 | "female": 829 511 | }, 512 | "three": { 513 | "male": 49, 514 | "female": 50 515 | }, 516 | "four": { 517 | "male": 3, 518 | "female": 9 519 | }, 520 | "five_plus": { 521 | "male": 1, 522 | "female": 1 523 | } 524 | }, 525 | "town": { 526 | "one": { 527 | "male": 1042, 528 | "female": 992 529 | }, 530 | "two": { 531 | "male": 523, 532 | "female": 530 533 | }, 534 | "three": { 535 | "male": 23, 536 | "female": 19 537 | }, 538 | "four": { 539 | "male": 1, 540 | "female": 4 541 | }, 542 | "five_plus": { 543 | "male": 0, 544 | "female": 0 545 | } 546 | }, 547 | "countryside": { 548 | "one": { 549 | "male": 759, 550 | "female": 694 551 | }, 552 | "two": { 553 | "male": 568, 554 | "female": 556 555 | }, 556 | "three": { 557 | "male": 70, 558 | "female": 46 559 | }, 560 | "four": { 561 | "male": 5, 562 | "female": 3 563 | }, 564 | "five_plus": { 565 | "male": 1, 566 | "female": 3 567 | } 568 | } 569 | }, 570 | { 571 | "id": 9, 572 | "name": "hei_long_jiang", 573 | "display_name": "黑龙江", 574 | "city": { 575 | "one": { 576 | "male": 2761, 577 | "female": 2499 578 | }, 579 | "two": { 580 | "male": 899, 581 | "female": 881 582 | }, 583 | "three": { 584 | "male": 44, 585 | "female": 39 586 | }, 587 | "four": { 588 | "male": 2, 589 | "female": 7 590 | }, 591 | "five_plus": { 592 | "male": 0, 593 | "female": 0 594 | } 595 | }, 596 | "town": { 597 | "one": { 598 | "male": 986, 599 | "female": 888 600 | }, 601 | "two": { 602 | "male": 492, 603 | "female": 549 604 | }, 605 | "three": { 606 | "male": 29, 607 | "female": 19 608 | }, 609 | "four": { 610 | "male": 2, 611 | "female": 2 612 | }, 613 | "five_plus": { 614 | "male": 1, 615 | "female": 0 616 | } 617 | }, 618 | "countryside": { 619 | "one": { 620 | "male": 1014, 621 | "female": 906 622 | }, 623 | "two": { 624 | "male": 532, 625 | "female": 625 626 | }, 627 | "three": { 628 | "male": 48, 629 | "female": 40 630 | }, 631 | "four": { 632 | "male": 5, 633 | "female": 1 634 | }, 635 | "five_plus": { 636 | "male": 0, 637 | "female": 3 638 | } 639 | } 640 | }, 641 | { 642 | "id": 10, 643 | "name": "shang_hai", 644 | "display_name": "上海", 645 | "town": { 646 | "one": { 647 | "male": 562, 648 | "female": 560 649 | }, 650 | "two": { 651 | "male": 369, 652 | "female": 285 653 | }, 654 | "three": { 655 | "male": 39, 656 | "female": 23 657 | }, 658 | "four": { 659 | "male": 4, 660 | "female": 2 661 | }, 662 | "five_plus": { 663 | "male": 0, 664 | "female": 1 665 | } 666 | }, 667 | "city": { 668 | "one": { 669 | "male": 4329, 670 | "female": 3941 671 | }, 672 | "two": { 673 | "male": 1889, 674 | "female": 1781 675 | }, 676 | "three": { 677 | "male": 149, 678 | "female": 90 679 | }, 680 | "four": { 681 | "male": 18, 682 | "female": 8 683 | }, 684 | "five_plus": { 685 | "male": 3, 686 | "female": 1 687 | } 688 | }, 689 | "countryside": { 690 | "one": { 691 | "male": 338, 692 | "female": 348 693 | }, 694 | "two": { 695 | "male": 265, 696 | "female": 259 697 | }, 698 | "three": { 699 | "male": 32, 700 | "female": 31 701 | }, 702 | "four": { 703 | "male": 4, 704 | "female": 2 705 | }, 706 | "five_plus": { 707 | "male": 0, 708 | "female": 0 709 | } 710 | } 711 | }, 712 | { 713 | "id": 11, 714 | "name": "jiang_su", 715 | "display_name": "江苏", 716 | "town": { 717 | "one": { 718 | "male": 4042, 719 | "female": 3634 720 | }, 721 | "two": { 722 | "male": 3595, 723 | "female": 3532 724 | }, 725 | "three": { 726 | "male": 583, 727 | "female": 386 728 | }, 729 | "four": { 730 | "male": 62, 731 | "female": 52 732 | }, 733 | "five_plus": { 734 | "male": 9, 735 | "female": 13 736 | } 737 | }, 738 | "city": { 739 | "one": { 740 | "male": 9360, 741 | "female": 8329 742 | }, 743 | "two": { 744 | "male": 6493, 745 | "female": 6009 746 | }, 747 | "three": { 748 | "male": 601, 749 | "female": 376 750 | }, 751 | "four": { 752 | "male": 33, 753 | "female": 50 754 | }, 755 | "five_plus": { 756 | "male": 9, 757 | "female": 4 758 | } 759 | }, 760 | "countryside": { 761 | "one": { 762 | "male": 2729, 763 | "female": 2427 764 | }, 765 | "two": { 766 | "male": 2291, 767 | "female": 2183 768 | }, 769 | "three": { 770 | "male": 453, 771 | "female": 355 772 | }, 773 | "four": { 774 | "male": 69, 775 | "female": 45 776 | }, 777 | "five_plus": { 778 | "male": 16, 779 | "female": 9 780 | } 781 | } 782 | }, 783 | { 784 | "id": 12, 785 | "name": "zhe_jiang", 786 | "display_name": "浙江", 787 | "town": { 788 | "one": { 789 | "male": 2664, 790 | "female": 2333 791 | }, 792 | "two": { 793 | "male": 2566, 794 | "female": 2306 795 | }, 796 | "three": { 797 | "male": 324, 798 | "female": 239 799 | }, 800 | "four": { 801 | "male": 33, 802 | "female": 19 803 | }, 804 | "five_plus": { 805 | "male": 11, 806 | "female": 7 807 | } 808 | }, 809 | "city": { 810 | "one": { 811 | "male": 7691, 812 | "female": 7046 813 | }, 814 | "two": { 815 | "male": 6209, 816 | "female": 5844 817 | }, 818 | "three": { 819 | "male": 769, 820 | "female": 503 821 | }, 822 | "four": { 823 | "male": 77, 824 | "female": 69 825 | }, 826 | "five_plus": { 827 | "male": 15, 828 | "female": 15 829 | } 830 | }, 831 | "countryside": { 832 | "one": { 833 | "male": 2663, 834 | "female": 2445 835 | }, 836 | "two": { 837 | "male": 2415, 838 | "female": 2185 839 | }, 840 | "three": { 841 | "male": 328, 842 | "female": 251 843 | }, 844 | "four": { 845 | "male": 40, 846 | "female": 36 847 | }, 848 | "five_plus": { 849 | "male": 15, 850 | "female": 1 851 | } 852 | } 853 | }, 854 | { 855 | "id": 13, 856 | "name": "an_hui", 857 | "display_name": "安徽", 858 | "town": { 859 | "one": { 860 | "male": 4324, 861 | "female": 3802 862 | }, 863 | "two": { 864 | "male": 4689, 865 | "female": 4301 866 | }, 867 | "three": { 868 | "male": 912, 869 | "female": 502 870 | }, 871 | "four": { 872 | "male": 74, 873 | "female": 49 874 | }, 875 | "five_plus": { 876 | "male": 17, 877 | "female": 8 878 | } 879 | }, 880 | "city": { 881 | "one": { 882 | "male": 3963, 883 | "female": 3628 884 | }, 885 | "two": { 886 | "male": 3568, 887 | "female": 3331 888 | }, 889 | "three": { 890 | "male": 375, 891 | "female": 192 892 | }, 893 | "four": { 894 | "male": 32, 895 | "female": 29 896 | }, 897 | "five_plus": { 898 | "male": 4, 899 | "female": 3 900 | } 901 | }, 902 | "countryside": { 903 | "one": { 904 | "male": 4003, 905 | "female": 3547 906 | }, 907 | "two": { 908 | "male": 5032, 909 | "female": 4569 910 | }, 911 | "three": { 912 | "male": 1501, 913 | "female": 987 914 | }, 915 | "four": { 916 | "male": 253, 917 | "female": 144 918 | }, 919 | "five_plus": { 920 | "male": 37, 921 | "female": 37 922 | } 923 | } 924 | }, 925 | { 926 | "id": 14, 927 | "name": "fu_jian", 928 | "display_name": "福建", 929 | "town": { 930 | "one": { 931 | "male": 2270, 932 | "female": 1943 933 | }, 934 | "two": { 935 | "male": 2891, 936 | "female": 2395 937 | }, 938 | "three": { 939 | "male": 838, 940 | "female": 481 941 | }, 942 | "four": { 943 | "male": 96, 944 | "female": 43 945 | }, 946 | "five_plus": { 947 | "male": 14, 948 | "female": 9 949 | } 950 | }, 951 | "city": { 952 | "one": { 953 | "male": 4007, 954 | "female": 3705 955 | }, 956 | "two": { 957 | "male": 4440, 958 | "female": 3790 959 | }, 960 | "three": { 961 | "male": 781, 962 | "female": 483 963 | }, 964 | "four": { 965 | "male": 70, 966 | "female": 31 967 | }, 968 | "five_plus": { 969 | "male": 10, 970 | "female": 4 971 | } 972 | }, 973 | "countryside": { 974 | "one": { 975 | "male": 1956, 976 | "female": 1734 977 | }, 978 | "two": { 979 | "male": 2407, 980 | "female": 2040 981 | }, 982 | "three": { 983 | "male": 799, 984 | "female": 506 985 | }, 986 | "four": { 987 | "male": 115, 988 | "female": 64 989 | }, 990 | "five_plus": { 991 | "male": 23, 992 | "female": 22 993 | } 994 | } 995 | }, 996 | { 997 | "id": 15, 998 | "name": "jiang_xi", 999 | "display_name": "江西", 1000 | "town": { 1001 | "one": { 1002 | "male": 2729, 1003 | "female": 2230 1004 | }, 1005 | "two": { 1006 | "male": 3020, 1007 | "female": 2663 1008 | }, 1009 | "three": { 1010 | "male": 1078, 1011 | "female": 669 1012 | }, 1013 | "four": { 1014 | "male": 165, 1015 | "female": 103 1016 | }, 1017 | "five_plus": { 1018 | "male": 52, 1019 | "female": 36 1020 | } 1021 | }, 1022 | "city": { 1023 | "one": { 1024 | "male": 2923, 1025 | "female": 2522 1026 | }, 1027 | "two": { 1028 | "male": 3100, 1029 | "female": 2683 1030 | }, 1031 | "three": { 1032 | "male": 778, 1033 | "female": 463 1034 | }, 1035 | "four": { 1036 | "male": 95, 1037 | "female": 56 1038 | }, 1039 | "five_plus": { 1040 | "male": 23, 1041 | "female": 13 1042 | } 1043 | }, 1044 | "countryside": { 1045 | "one": { 1046 | "male": 3356, 1047 | "female": 2767 1048 | }, 1049 | "two": { 1050 | "male": 3495, 1051 | "female": 3134 1052 | }, 1053 | "three": { 1054 | "male": 1685, 1055 | "female": 1106 1056 | }, 1057 | "four": { 1058 | "male": 358, 1059 | "female": 189 1060 | }, 1061 | "five_plus": { 1062 | "male": 84, 1063 | "female": 58 1064 | } 1065 | } 1066 | }, 1067 | { 1068 | "id": 16, 1069 | "name": "shan_dong", 1070 | "display_name": "山东", 1071 | "town": { 1072 | "one": { 1073 | "male": 4141, 1074 | "female": 3741 1075 | }, 1076 | "two": { 1077 | "male": 5792, 1078 | "female": 5386 1079 | }, 1080 | "three": { 1081 | "male": 1745, 1082 | "female": 1314 1083 | }, 1084 | "four": { 1085 | "male": 183, 1086 | "female": 128 1087 | }, 1088 | "five_plus": { 1089 | "male": 30, 1090 | "female": 27 1091 | } 1092 | }, 1093 | "city": { 1094 | "one": { 1095 | "male": 8911, 1096 | "female": 8143 1097 | }, 1098 | "two": { 1099 | "male": 10254, 1100 | "female": 9328 1101 | }, 1102 | "three": { 1103 | "male": 1579, 1104 | "female": 967 1105 | }, 1106 | "four": { 1107 | "male": 115, 1108 | "female": 111 1109 | }, 1110 | "five_plus": { 1111 | "male": 27, 1112 | "female": 15 1113 | } 1114 | }, 1115 | "countryside": { 1116 | "one": { 1117 | "male": 4067, 1118 | "female": 3649 1119 | }, 1120 | "two": { 1121 | "male": 5726, 1122 | "female": 5452 1123 | }, 1124 | "three": { 1125 | "male": 2783, 1126 | "female": 2116 1127 | }, 1128 | "four": { 1129 | "male": 327, 1130 | "female": 232 1131 | }, 1132 | "five_plus": { 1133 | "male": 73, 1134 | "female": 53 1135 | } 1136 | } 1137 | }, 1138 | { 1139 | "id": 17, 1140 | "name": "he_nan", 1141 | "display_name": "河南", 1142 | "town": { 1143 | "one": { 1144 | "male": 6028, 1145 | "female": 5025 1146 | }, 1147 | "two": { 1148 | "male": 6165, 1149 | "female": 5959 1150 | }, 1151 | "three": { 1152 | "male": 1884, 1153 | "female": 1504 1154 | }, 1155 | "four": { 1156 | "male": 238, 1157 | "female": 163 1158 | }, 1159 | "five_plus": { 1160 | "male": 41, 1161 | "female": 32 1162 | } 1163 | }, 1164 | "city": { 1165 | "one": { 1166 | "male": 5988, 1167 | "female": 5259 1168 | }, 1169 | "two": { 1170 | "male": 5634, 1171 | "female": 5499 1172 | }, 1173 | "three": { 1174 | "male": 941, 1175 | "female": 647 1176 | }, 1177 | "four": { 1178 | "male": 90, 1179 | "female": 55 1180 | }, 1181 | "five_plus": { 1182 | "male": 21, 1183 | "female": 1 1184 | } 1185 | }, 1186 | "countryside": { 1187 | "one": { 1188 | "male": 7367, 1189 | "female": 6372 1190 | }, 1191 | "two": { 1192 | "male": 7884, 1193 | "female": 7984 1194 | }, 1195 | "three": { 1196 | "male": 3566, 1197 | "female": 3017 1198 | }, 1199 | "four": { 1200 | "male": 621, 1201 | "female": 362 1202 | }, 1203 | "five_plus": { 1204 | "male": 133, 1205 | "female": 89 1206 | } 1207 | } 1208 | }, 1209 | { 1210 | "id": 18, 1211 | "name": "hu_bei", 1212 | "display_name": "湖北", 1213 | "town": { 1214 | "one": { 1215 | "male": 2740, 1216 | "female": 2224 1217 | }, 1218 | "two": { 1219 | "male": 2815, 1220 | "female": 2446 1221 | }, 1222 | "three": { 1223 | "male": 394, 1224 | "female": 223 1225 | }, 1226 | "four": { 1227 | "male": 40, 1228 | "female": 22 1229 | }, 1230 | "five_plus": { 1231 | "male": 5, 1232 | "female": 4 1233 | } 1234 | }, 1235 | "city": { 1236 | "one": { 1237 | "male": 6092, 1238 | "female": 5498 1239 | }, 1240 | "two": { 1241 | "male": 4529, 1242 | "female": 4024 1243 | }, 1244 | "three": { 1245 | "male": 402, 1246 | "female": 222 1247 | }, 1248 | "four": { 1249 | "male": 27, 1250 | "female": 33 1251 | }, 1252 | "five_plus": { 1253 | "male": 13, 1254 | "female": 3 1255 | } 1256 | }, 1257 | "countryside": { 1258 | "one": { 1259 | "male": 3972, 1260 | "female": 3410 1261 | }, 1262 | "two": { 1263 | "male": 4197, 1264 | "female": 4024 1265 | }, 1266 | "three": { 1267 | "male": 799, 1268 | "female": 454 1269 | }, 1270 | "four": { 1271 | "male": 79, 1272 | "female": 57 1273 | }, 1274 | "five_plus": { 1275 | "male": 13, 1276 | "female": 11 1277 | } 1278 | } 1279 | }, 1280 | { 1281 | "id": 19, 1282 | "name": "hu_nan", 1283 | "display_name": "湖南", 1284 | "town": { 1285 | "one": { 1286 | "male": 4204, 1287 | "female": 3409 1288 | }, 1289 | "two": { 1290 | "male": 4382, 1291 | "female": 4044 1292 | }, 1293 | "three": { 1294 | "male": 927, 1295 | "female": 699 1296 | }, 1297 | "four": { 1298 | "male": 153, 1299 | "female": 90 1300 | }, 1301 | "five_plus": { 1302 | "male": 30, 1303 | "female": 21 1304 | } 1305 | }, 1306 | "city": { 1307 | "one": { 1308 | "male": 4364, 1309 | "female": 3720 1310 | }, 1311 | "two": { 1312 | "male": 4281, 1313 | "female": 3908 1314 | }, 1315 | "three": { 1316 | "male": 619, 1317 | "female": 367 1318 | }, 1319 | "four": { 1320 | "male": 77, 1321 | "female": 44 1322 | }, 1323 | "five_plus": { 1324 | "male": 11, 1325 | "female": 13 1326 | } 1327 | }, 1328 | "countryside": { 1329 | "one": { 1330 | "male": 4912, 1331 | "female": 4005 1332 | }, 1333 | "two": { 1334 | "male": 5038, 1335 | "female": 4686 1336 | }, 1337 | "three": { 1338 | "male": 1494, 1339 | "female": 1094 1340 | }, 1341 | "four": { 1342 | "male": 241, 1343 | "female": 182 1344 | }, 1345 | "five_plus": { 1346 | "male": 44, 1347 | "female": 44 1348 | } 1349 | } 1350 | }, 1351 | { 1352 | "id": 20, 1353 | "name": "guang_dong", 1354 | "display_name": "广东", 1355 | "town": { 1356 | "one": { 1357 | "male": 4188, 1358 | "female": 3519 1359 | }, 1360 | "two": { 1361 | "male": 4047, 1362 | "female": 3613 1363 | }, 1364 | "three": { 1365 | "male": 1628, 1366 | "female": 1297 1367 | }, 1368 | "four": { 1369 | "male": 418, 1370 | "female": 289 1371 | }, 1372 | "five_plus": { 1373 | "male": 125, 1374 | "female": 102 1375 | } 1376 | }, 1377 | "city": { 1378 | "one": { 1379 | "male": 21426, 1380 | "female": 17986 1381 | }, 1382 | "two": { 1383 | "male": 19378, 1384 | "female": 17388 1385 | }, 1386 | "three": { 1387 | "male": 4521, 1388 | "female": 3192 1389 | }, 1390 | "four": { 1391 | "male": 775, 1392 | "female": 559 1393 | }, 1394 | "five_plus": { 1395 | "male": 171, 1396 | "female": 108 1397 | } 1398 | }, 1399 | "countryside": { 1400 | "one": { 1401 | "male": 6719, 1402 | "female": 5806 1403 | }, 1404 | "two": { 1405 | "male": 6632, 1406 | "female": 5899 1407 | }, 1408 | "three": { 1409 | "male": 3300, 1410 | "female": 2731 1411 | }, 1412 | "four": { 1413 | "male": 1085, 1414 | "female": 835 1415 | }, 1416 | "five_plus": { 1417 | "male": 372, 1418 | "female": 310 1419 | } 1420 | } 1421 | }, 1422 | { 1423 | "id": 21, 1424 | "name": "guang_xi", 1425 | "display_name": "广西", 1426 | "town": { 1427 | "one": { 1428 | "male": 2559, 1429 | "female": 2123 1430 | }, 1431 | "two": { 1432 | "male": 3025, 1433 | "female": 2762 1434 | }, 1435 | "three": { 1436 | "male": 1219, 1437 | "female": 921 1438 | }, 1439 | "four": { 1440 | "male": 299, 1441 | "female": 227 1442 | }, 1443 | "five_plus": { 1444 | "male": 106, 1445 | "female": 84 1446 | } 1447 | }, 1448 | "city": { 1449 | "one": { 1450 | "male": 4074, 1451 | "female": 3444 1452 | }, 1453 | "two": { 1454 | "male": 4056, 1455 | "female": 3610 1456 | }, 1457 | "three": { 1458 | "male": 886, 1459 | "female": 600 1460 | }, 1461 | "four": { 1462 | "male": 137, 1463 | "female": 110 1464 | }, 1465 | "five_plus": { 1466 | "male": 37, 1467 | "female": 16 1468 | } 1469 | }, 1470 | "countryside": { 1471 | "one": { 1472 | "male": 3812, 1473 | "female": 3378 1474 | }, 1475 | "two": { 1476 | "male": 4327, 1477 | "female": 3956 1478 | }, 1479 | "three": { 1480 | "male": 2116, 1481 | "female": 1849 1482 | }, 1483 | "four": { 1484 | "male": 679, 1485 | "female": 525 1486 | }, 1487 | "five_plus": { 1488 | "male": 317, 1489 | "female": 237 1490 | } 1491 | } 1492 | }, 1493 | { 1494 | "id": 22, 1495 | "name": "hai_nan", 1496 | "display_name": "海南", 1497 | "town": { 1498 | "one": { 1499 | "male": 547, 1500 | "female": 417 1501 | }, 1502 | "two": { 1503 | "male": 536, 1504 | "female": 438 1505 | }, 1506 | "three": { 1507 | "male": 199, 1508 | "female": 122 1509 | }, 1510 | "four": { 1511 | "male": 42, 1512 | "female": 23 1513 | }, 1514 | "five_plus": { 1515 | "male": 10, 1516 | "female": 7 1517 | } 1518 | }, 1519 | "city": { 1520 | "one": { 1521 | "male": 985, 1522 | "female": 930 1523 | }, 1524 | "two": { 1525 | "male": 1008, 1526 | "female": 825 1527 | }, 1528 | "three": { 1529 | "male": 223, 1530 | "female": 143 1531 | }, 1532 | "four": { 1533 | "male": 25, 1534 | "female": 22 1535 | }, 1536 | "five_plus": { 1537 | "male": 2, 1538 | "female": 2 1539 | } 1540 | }, 1541 | "countryside": { 1542 | "one": { 1543 | "male": 835, 1544 | "female": 724 1545 | }, 1546 | "two": { 1547 | "male": 801, 1548 | "female": 716 1549 | }, 1550 | "three": { 1551 | "male": 346, 1552 | "female": 268 1553 | }, 1554 | "four": { 1555 | "male": 70, 1556 | "female": 44 1557 | }, 1558 | "five_plus": { 1559 | "male": 33, 1560 | "female": 16 1561 | } 1562 | } 1563 | }, 1564 | { 1565 | "id": 23, 1566 | "name": "chong_qing", 1567 | "display_name": "重庆", 1568 | "town": { 1569 | "one": { 1570 | "male": 1218, 1571 | "female": 1122 1572 | }, 1573 | "two": { 1574 | "male": 1146, 1575 | "female": 1162 1576 | }, 1577 | "three": { 1578 | "male": 173, 1579 | "female": 141 1580 | }, 1581 | "four": { 1582 | "male": 16, 1583 | "female": 15 1584 | }, 1585 | "five_plus": { 1586 | "male": 9, 1587 | "female": 5 1588 | } 1589 | }, 1590 | "city": { 1591 | "one": { 1592 | "male": 4571, 1593 | "female": 4114 1594 | }, 1595 | "two": { 1596 | "male": 3054, 1597 | "female": 3008 1598 | }, 1599 | "three": { 1600 | "male": 206, 1601 | "female": 192 1602 | }, 1603 | "four": { 1604 | "male": 21, 1605 | "female": 34 1606 | }, 1607 | "five_plus": { 1608 | "male": 1, 1609 | "female": 6 1610 | } 1611 | }, 1612 | "countryside": { 1613 | "one": { 1614 | "male": 1387, 1615 | "female": 1176 1616 | }, 1617 | "two": { 1618 | "male": 1242, 1619 | "female": 1143 1620 | }, 1621 | "three": { 1622 | "male": 238, 1623 | "female": 220 1624 | }, 1625 | "four": { 1626 | "male": 38, 1627 | "female": 49 1628 | }, 1629 | "five_plus": { 1630 | "male": 9, 1631 | "female": 11 1632 | } 1633 | } 1634 | }, 1635 | { 1636 | "id": 24, 1637 | "name": "si_chuan", 1638 | "display_name": "四川", 1639 | "town": { 1640 | "one": { 1641 | "male": 3846, 1642 | "female": 3355 1643 | }, 1644 | "two": { 1645 | "male": 3415, 1646 | "female": 3376 1647 | }, 1648 | "three": { 1649 | "male": 485, 1650 | "female": 405 1651 | }, 1652 | "four": { 1653 | "male": 86, 1654 | "female": 87 1655 | }, 1656 | "five_plus": { 1657 | "male": 37, 1658 | "female": 28 1659 | } 1660 | }, 1661 | "city": { 1662 | "one": { 1663 | "male": 8746, 1664 | "female": 7496 1665 | }, 1666 | "two": { 1667 | "male": 5702, 1668 | "female": 5268 1669 | }, 1670 | "three": { 1671 | "male": 335, 1672 | "female": 259 1673 | }, 1674 | "four": { 1675 | "male": 36, 1676 | "female": 46 1677 | }, 1678 | "five_plus": { 1679 | "male": 5, 1680 | "female": 1 1681 | } 1682 | }, 1683 | "countryside": { 1684 | "one": { 1685 | "male": 6998, 1686 | "female": 5989 1687 | }, 1688 | "two": { 1689 | "male": 5757, 1690 | "female": 5493 1691 | }, 1692 | "three": { 1693 | "male": 1322, 1694 | "female": 1193 1695 | }, 1696 | "four": { 1697 | "male": 398, 1698 | "female": 387 1699 | }, 1700 | "five_plus": { 1701 | "male": 266, 1702 | "female": 206 1703 | } 1704 | } 1705 | }, 1706 | { 1707 | "id": 25, 1708 | "name": "gui_zhou", 1709 | "display_name": "贵州", 1710 | "town": { 1711 | "one": { 1712 | "male": 2900, 1713 | "female": 2544 1714 | }, 1715 | "two": { 1716 | "male": 3347, 1717 | "female": 2997 1718 | }, 1719 | "three": { 1720 | "male": 1023, 1721 | "female": 832 1722 | }, 1723 | "four": { 1724 | "male": 218, 1725 | "female": 175 1726 | }, 1727 | "five_plus": { 1728 | "male": 66, 1729 | "female": 67 1730 | } 1731 | }, 1732 | "city": { 1733 | "one": { 1734 | "male": 3184, 1735 | "female": 2904 1736 | }, 1737 | "two": { 1738 | "male": 3244, 1739 | "female": 2856 1740 | }, 1741 | "three": { 1742 | "male": 679, 1743 | "female": 480 1744 | }, 1745 | "four": { 1746 | "male": 122, 1747 | "female": 116 1748 | }, 1749 | "five_plus": { 1750 | "male": 42, 1751 | "female": 21 1752 | } 1753 | }, 1754 | "countryside": { 1755 | "one": { 1756 | "male": 4010, 1757 | "female": 3468 1758 | }, 1759 | "two": { 1760 | "male": 4320, 1761 | "female": 4075 1762 | }, 1763 | "three": { 1764 | "male": 2026, 1765 | "female": 1702 1766 | }, 1767 | "four": { 1768 | "male": 612, 1769 | "female": 478 1770 | }, 1771 | "five_plus": { 1772 | "male": 257, 1773 | "female": 219 1774 | } 1775 | } 1776 | }, 1777 | { 1778 | "id": 26, 1779 | "name": "yun_nan", 1780 | "display_name": "云南", 1781 | "town": { 1782 | "one": { 1783 | "male": 2976, 1784 | "female": 2806 1785 | }, 1786 | "two": { 1787 | "male": 3130, 1788 | "female": 3145 1789 | }, 1790 | "three": { 1791 | "male": 659, 1792 | "female": 615 1793 | }, 1794 | "four": { 1795 | "male": 115, 1796 | "female": 130 1797 | }, 1798 | "five_plus": { 1799 | "male": 47, 1800 | "female": 26 1801 | } 1802 | }, 1803 | "city": { 1804 | "one": { 1805 | "male": 3613, 1806 | "female": 3395 1807 | }, 1808 | "two": { 1809 | "male": 3111, 1810 | "female": 2938 1811 | }, 1812 | "three": { 1813 | "male": 376, 1814 | "female": 331 1815 | }, 1816 | "four": { 1817 | "male": 45, 1818 | "female": 54 1819 | }, 1820 | "five_plus": { 1821 | "male": 13, 1822 | "female": 8 1823 | } 1824 | }, 1825 | "countryside": { 1826 | "one": { 1827 | "male": 5532, 1828 | "female": 4822 1829 | }, 1830 | "two": { 1831 | "male": 5372, 1832 | "female": 5162 1833 | }, 1834 | "three": { 1835 | "male": 1756, 1836 | "female": 1564 1837 | }, 1838 | "four": { 1839 | "male": 439, 1840 | "female": 373 1841 | }, 1842 | "five_plus": { 1843 | "male": 169, 1844 | "female": 136 1845 | } 1846 | } 1847 | }, 1848 | { 1849 | "id": 27, 1850 | "name": "xi_zang", 1851 | "display_name": "西藏", 1852 | "town": { 1853 | "one": { 1854 | "male": 150, 1855 | "female": 136 1856 | }, 1857 | "two": { 1858 | "male": 122, 1859 | "female": 103 1860 | }, 1861 | "three": { 1862 | "male": 52, 1863 | "female": 37 1864 | }, 1865 | "four": { 1866 | "male": 15, 1867 | "female": 21 1868 | }, 1869 | "five_plus": { 1870 | "male": 7, 1871 | "female": 14 1872 | } 1873 | }, 1874 | "city": { 1875 | "one": { 1876 | "male": 155, 1877 | "female": 127 1878 | }, 1879 | "two": { 1880 | "male": 110, 1881 | "female": 102 1882 | }, 1883 | "three": { 1884 | "male": 15, 1885 | "female": 12 1886 | }, 1887 | "four": { 1888 | "male": 2, 1889 | "female": 6 1890 | }, 1891 | "five_plus": { 1892 | "male": 6, 1893 | "female": 1 1894 | } 1895 | }, 1896 | "countryside": { 1897 | "one": { 1898 | "male": 614, 1899 | "female": 553 1900 | }, 1901 | "two": { 1902 | "male": 610, 1903 | "female": 603 1904 | }, 1905 | "three": { 1906 | "male": 368, 1907 | "female": 410 1908 | }, 1909 | "four": { 1910 | "male": 171, 1911 | "female": 215 1912 | }, 1913 | "five_plus": { 1914 | "male": 174, 1915 | "female": 202 1916 | } 1917 | } 1918 | }, 1919 | { 1920 | "id": 28, 1921 | "name": "shan_xi_1", 1922 | "display_name": "陕西", 1923 | "town": { 1924 | "one": { 1925 | "male": 1797, 1926 | "female": 1526 1927 | }, 1928 | "two": { 1929 | "male": 1901, 1930 | "female": 1840 1931 | }, 1932 | "three": { 1933 | "male": 279, 1934 | "female": 172 1935 | }, 1936 | "four": { 1937 | "male": 15, 1938 | "female": 21 1939 | }, 1940 | "five_plus": { 1941 | "male": 4, 1942 | "female": 5 1943 | } 1944 | }, 1945 | "city": { 1946 | "one": { 1947 | "male": 3843, 1948 | "female": 3569 1949 | }, 1950 | "two": { 1951 | "male": 2993, 1952 | "female": 2853 1953 | }, 1954 | "three": { 1955 | "male": 242, 1956 | "female": 165 1957 | }, 1958 | "four": { 1959 | "male": 14, 1960 | "female": 22 1961 | }, 1962 | "five_plus": { 1963 | "male": 4, 1964 | "female": 5 1965 | } 1966 | }, 1967 | "countryside": { 1968 | "one": { 1969 | "male": 2416, 1970 | "female": 2112 1971 | }, 1972 | "two": { 1973 | "male": 2348, 1974 | "female": 2404 1975 | }, 1976 | "three": { 1977 | "male": 351, 1978 | "female": 264 1979 | }, 1980 | "four": { 1981 | "male": 54, 1982 | "female": 26 1983 | }, 1984 | "five_plus": { 1985 | "male": 4, 1986 | "female": 5 1987 | } 1988 | } 1989 | }, 1990 | { 1991 | "id": 29, 1992 | "name": "gan_su", 1993 | "display_name": "甘肃", 1994 | "town": { 1995 | "one": { 1996 | "male": 1511, 1997 | "female": 1288 1998 | }, 1999 | "two": { 2000 | "male": 1703, 2001 | "female": 1644 2002 | }, 2003 | "three": { 2004 | "male": 327, 2005 | "female": 253 2006 | }, 2007 | "four": { 2008 | "male": 65, 2009 | "female": 37 2010 | }, 2011 | "five_plus": { 2012 | "male": 22, 2013 | "female": 15 2014 | } 2015 | }, 2016 | "city": { 2017 | "one": { 2018 | "male": 2116, 2019 | "female": 1941 2020 | }, 2021 | "two": { 2022 | "male": 1678, 2023 | "female": 1564 2024 | }, 2025 | "three": { 2026 | "male": 196, 2027 | "female": 132 2028 | }, 2029 | "four": { 2030 | "male": 18, 2031 | "female": 19 2032 | }, 2033 | "five_plus": { 2034 | "male": 3, 2035 | "female": 2 2036 | } 2037 | }, 2038 | "countryside": { 2039 | "one": { 2040 | "male": 2383, 2041 | "female": 2191 2042 | }, 2043 | "two": { 2044 | "male": 2397, 2045 | "female": 2364 2046 | }, 2047 | "three": { 2048 | "male": 706, 2049 | "female": 681 2050 | }, 2051 | "four": { 2052 | "male": 199, 2053 | "female": 176 2054 | }, 2055 | "five_plus": { 2056 | "male": 88, 2057 | "female": 64 2058 | } 2059 | } 2060 | }, 2061 | { 2062 | "id": 30, 2063 | "name": "qing_hai", 2064 | "display_name": "青海", 2065 | "town": { 2066 | "one": { 2067 | "male": 366, 2068 | "female": 332 2069 | }, 2070 | "two": { 2071 | "male": 314, 2072 | "female": 298 2073 | }, 2074 | "three": { 2075 | "male": 79, 2076 | "female": 78 2077 | }, 2078 | "four": { 2079 | "male": 22, 2080 | "female": 25 2081 | }, 2082 | "five_plus": { 2083 | "male": 19, 2084 | "female": 10 2085 | } 2086 | }, 2087 | "city": { 2088 | "one": { 2089 | "male": 618, 2090 | "female": 557 2091 | }, 2092 | "two": { 2093 | "male": 429, 2094 | "female": 393 2095 | }, 2096 | "three": { 2097 | "male": 58, 2098 | "female": 57 2099 | }, 2100 | "four": { 2101 | "male": 12, 2102 | "female": 12 2103 | }, 2104 | "five_plus": { 2105 | "male": 3, 2106 | "female": 2 2107 | } 2108 | }, 2109 | "countryside": { 2110 | "one": { 2111 | "male": 609, 2112 | "female": 479 2113 | }, 2114 | "two": { 2115 | "male": 487, 2116 | "female": 467 2117 | }, 2118 | "three": { 2119 | "male": 213, 2120 | "female": 203 2121 | }, 2122 | "four": { 2123 | "male": 91, 2124 | "female": 75 2125 | }, 2126 | "five_plus": { 2127 | "male": 53, 2128 | "female": 61 2129 | } 2130 | } 2131 | }, 2132 | { 2133 | "id": 31, 2134 | "name": "ning_xia", 2135 | "display_name": "宁夏", 2136 | "town": { 2137 | "one": { 2138 | "male": 447, 2139 | "female": 475 2140 | }, 2141 | "two": { 2142 | "male": 440, 2143 | "female": 429 2144 | }, 2145 | "three": { 2146 | "male": 156, 2147 | "female": 121 2148 | }, 2149 | "four": { 2150 | "male": 34, 2151 | "female": 32 2152 | }, 2153 | "five_plus": { 2154 | "male": 12, 2155 | "female": 4 2156 | } 2157 | }, 2158 | "city": { 2159 | "one": { 2160 | "male": 913, 2161 | "female": 820 2162 | }, 2163 | "two": { 2164 | "male": 771, 2165 | "female": 780 2166 | }, 2167 | "three": { 2168 | "male": 109, 2169 | "female": 94 2170 | }, 2171 | "four": { 2172 | "male": 10, 2173 | "female": 11 2174 | }, 2175 | "five_plus": { 2176 | "male": 2, 2177 | "female": 4 2178 | } 2179 | }, 2180 | "countryside": { 2181 | "one": { 2182 | "male": 555, 2183 | "female": 536 2184 | }, 2185 | "two": { 2186 | "male": 527, 2187 | "female": 524 2188 | }, 2189 | "three": { 2190 | "male": 313, 2191 | "female": 265 2192 | }, 2193 | "four": { 2194 | "male": 135, 2195 | "female": 93 2196 | }, 2197 | "five_plus": { 2198 | "male": 50, 2199 | "female": 46 2200 | } 2201 | } 2202 | }, 2203 | { 2204 | "id": 32, 2205 | "name": "xin_jiang", 2206 | "display_name": "新疆", 2207 | "town": { 2208 | "one": { 2209 | "male": 1265, 2210 | "female": 1082 2211 | }, 2212 | "two": { 2213 | "male": 922, 2214 | "female": 876 2215 | }, 2216 | "three": { 2217 | "male": 110, 2218 | "female": 95 2219 | }, 2220 | "four": { 2221 | "male": 11, 2222 | "female": 18 2223 | }, 2224 | "five_plus": { 2225 | "male": 2, 2226 | "female": 2 2227 | } 2228 | }, 2229 | "city": { 2230 | "one": { 2231 | "male": 2319, 2232 | "female": 2234 2233 | }, 2234 | "two": { 2235 | "male": 1489, 2236 | "female": 1438 2237 | }, 2238 | "three": { 2239 | "male": 131, 2240 | "female": 102 2241 | }, 2242 | "four": { 2243 | "male": 15, 2244 | "female": 37 2245 | }, 2246 | "five_plus": { 2247 | "male": 0, 2248 | "female": 5 2249 | } 2250 | }, 2251 | "countryside": { 2252 | "one": { 2253 | "male": 1798, 2254 | "female": 1643 2255 | }, 2256 | "two": { 2257 | "male": 1277, 2258 | "female": 1247 2259 | }, 2260 | "three": { 2261 | "male": 404, 2262 | "female": 376 2263 | }, 2264 | "four": { 2265 | "male": 31, 2266 | "female": 66 2267 | }, 2268 | "five_plus": { 2269 | "male": 8, 2270 | "female": 17 2271 | } 2272 | } 2273 | }, 2274 | { 2275 | "id": 33, 2276 | "name": "ao_men", 2277 | "display_name": "澳门", 2278 | "town": { 2279 | "one": { 2280 | "male": 0, 2281 | "female": 0 2282 | }, 2283 | "two": { 2284 | "male": 0, 2285 | "female": 0 2286 | }, 2287 | "three": { 2288 | "male": 0, 2289 | "female": 0 2290 | }, 2291 | "four": { 2292 | "male": 0, 2293 | "female": 0 2294 | }, 2295 | "five_plus": { 2296 | "male": 0, 2297 | "female": 0 2298 | } 2299 | }, 2300 | "city": { 2301 | "one": { 2302 | "male": 1905, 2303 | "female": 1807 2304 | }, 2305 | "two": { 2306 | "male": 0, 2307 | "female": 0 2308 | }, 2309 | "three": { 2310 | "male": 0, 2311 | "female": 0 2312 | }, 2313 | "four": { 2314 | "male": 0, 2315 | "female": 0 2316 | }, 2317 | "five_plus": { 2318 | "male": 0, 2319 | "female": 5 2320 | } 2321 | }, 2322 | "countryside": { 2323 | "one": { 2324 | "male": 0, 2325 | "female": 0 2326 | }, 2327 | "two": { 2328 | "male": 0, 2329 | "female": 0 2330 | }, 2331 | "three": { 2332 | "male": 0, 2333 | "female": 0 2334 | }, 2335 | "four": { 2336 | "male": 0, 2337 | "female": 0 2338 | }, 2339 | "five_plus": { 2340 | "male": 0, 2341 | "female": 0 2342 | } 2343 | } 2344 | }, 2345 | { 2346 | "id": 34, 2347 | "name": "xiang_gang", 2348 | "display_name": "香港", 2349 | "town": { 2350 | "one": { 2351 | "male": 0, 2352 | "female": 0 2353 | }, 2354 | "two": { 2355 | "male": 0, 2356 | "female": 0 2357 | }, 2358 | "three": { 2359 | "male": 0, 2360 | "female": 0 2361 | }, 2362 | "four": { 2363 | "male": 0, 2364 | "female": 0 2365 | }, 2366 | "five_plus": { 2367 | "male": 0, 2368 | "female": 0 2369 | } 2370 | }, 2371 | "city": { 2372 | "one": { 2373 | "male": 17400, 2374 | "female": 15800 2375 | }, 2376 | "two": { 2377 | "male": 0, 2378 | "female": 0 2379 | }, 2380 | "three": { 2381 | "male": 0, 2382 | "female": 0 2383 | }, 2384 | "four": { 2385 | "male": 0, 2386 | "female": 0 2387 | }, 2388 | "five_plus": { 2389 | "male": 0, 2390 | "female": 5 2391 | } 2392 | }, 2393 | "countryside": { 2394 | "one": { 2395 | "male": 0, 2396 | "female": 0 2397 | }, 2398 | "two": { 2399 | "male": 0, 2400 | "female": 0 2401 | }, 2402 | "three": { 2403 | "male": 0, 2404 | "female": 0 2405 | }, 2406 | "four": { 2407 | "male": 0, 2408 | "female": 0 2409 | }, 2410 | "five_plus": { 2411 | "male": 0, 2412 | "female": 0 2413 | } 2414 | } 2415 | }, 2416 | { 2417 | "id": 35, 2418 | "name": "tai_wan", 2419 | "display_name": "台湾", 2420 | "town": { 2421 | "one": { 2422 | "male": 0, 2423 | "female": 0 2424 | }, 2425 | "two": { 2426 | "male": 0, 2427 | "female": 0 2428 | }, 2429 | "three": { 2430 | "male": 0, 2431 | "female": 0 2432 | }, 2433 | "four": { 2434 | "male": 0, 2435 | "female": 0 2436 | }, 2437 | "five_plus": { 2438 | "male": 0, 2439 | "female": 0 2440 | } 2441 | }, 2442 | "city": { 2443 | "one": { 2444 | "male": 71208, 2445 | "female": 66205 2446 | }, 2447 | "two": { 2448 | "male": 0, 2449 | "female": 0 2450 | }, 2451 | "three": { 2452 | "male": 0, 2453 | "female": 0 2454 | }, 2455 | "four": { 2456 | "male": 0, 2457 | "female": 0 2458 | }, 2459 | "five_plus": { 2460 | "male": 0, 2461 | "female": 5 2462 | } 2463 | }, 2464 | "countryside": { 2465 | "one": { 2466 | "male": 0, 2467 | "female": 0 2468 | }, 2469 | "two": { 2470 | "male": 0, 2471 | "female": 0 2472 | }, 2473 | "three": { 2474 | "male": 0, 2475 | "female": 0 2476 | }, 2477 | "four": { 2478 | "male": 0, 2479 | "female": 0 2480 | }, 2481 | "five_plus": { 2482 | "male": 0, 2483 | "female": 0 2484 | } 2485 | } 2486 | } 2487 | ] 2488 | --------------------------------------------------------------------------------