ln.whyme.uno/
├── index.html
├── package.json
├── tsconfig.json
├── vite.config.ts
├── postcss.config.cjs
├── tailwind.config.cjs
├── src/
│ ├── main.tsx
│ ├── App.tsx
│ ├── index.css
│ └── vite-env.d.ts
{
"name": "ln.whyme.uno",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"chart.js": "^4.4.1",
"react": "^18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.31",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3",
"vite": "^5.0.8"
}
}
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
base: "./", // ⚡ 保证打包后资源路径正确,适合 Cloudflare 上传
})
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
module.exports = {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
}
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>对数感觉模型 - ln.whyme.uno</title>
</head>
<body class="bg-gray-100">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
import React, { useState } from "react";
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
} from "chart.js";
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip);
export default function App() {
const [start, setStart] = useState(4);
const [end, setEnd] = useState(80);
const midpoint = Math.sqrt(start * end);
const ages = Array.from({ length: end - start + 1 }, (_, i) => start + i);
const lnValues = ages.map((age) => Math.log(age));
const data = {
labels: ages,
datasets: [
{
label: "主观时间曲线 ln(age)",
data: lnValues,
borderColor: "rgba(75,192,192,1)",
borderWidth: 2,
tension: 0.2,
pointRadius: 0,
},
{
label: "主观中点",
data: ages.map((age) => (age === Math.round(midpoint) ? Math.log(age) : null)),
borderColor: "red",
backgroundColor: "red",
pointRadius: 6,
type: "scatter" as const,
},
],
};
return (
<div className="min-h-screen flex flex-col items-center justify-start p-6">
<h1 className="text-3xl font-bold mb-4">对数感觉模型</h1>
<p className="text-gray-700 mb-6">
我们对时间的感知是对数型的。输入起点和终点年龄,看看你的一生“主观中点”在哪。
</p>
<div className="flex gap-4 mb-6">
<div>
<label className="block text-sm">起点年龄</label>
<input
type="number"
value={start}
onChange={(e) => setStart(Number(e.target.value))}
className="border rounded p-1 w-20"
/>
</div>
<div>
<label className="block text-sm">终点年龄</label>
<input
type="number"
value={end}
onChange={(e) => setEnd(Number(e.target.value))}
className="border rounded p-1 w-20"
/>
</div>
</div>
<div className="w-full max-w-3xl bg-white rounded-xl shadow p-4">
<Line data={data} />
</div>
<p className="mt-4 text-lg font-medium">
你的主观中点大约在 <span className="text-red-600">{midpoint.toFixed(1)} 岁</span>
</p>
</div>
);
}
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
font-family: sans-serif;
}
/// <reference types="vite/client" />
-
把上面的文件保存好(或者我可以帮你打包成 zip 给你)。
-
在根目录运行:
npm install npm run build生成
dist/文件夹。 -
登录 Cloudflare → Pages → Create project → Upload assets 上传
dist/文件夹里的内容。 -
部署完成后,绑定域名
ln.whyme.uno