Skip to content

Repository files navigation

📂 项目目录结构(最小模板)

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

📄 文件内容

package.json

{
  "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"
  }
}

vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  base: "./", // ⚡ 保证打包后资源路径正确,适合 Cloudflare 上传
})

tsconfig.json

{
  "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"]
}

postcss.config.cjs

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

tailwind.config.cjs

module.exports = {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
}

index.html

<!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>

src/main.tsx

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>,
)

src/App.tsx

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>
  );
}

src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  font-family: sans-serif;
}

src/vite-env.d.ts

/// <reference types="vite/client" />

🔧 使用步骤

  1. 把上面的文件保存好(或者我可以帮你打包成 zip 给你)。

  2. 在根目录运行:

    npm install
    npm run build
    

    生成 dist/ 文件夹。

  3. 登录 Cloudflare → Pages → Create project → Upload assets 上传 dist/ 文件夹里的内容。

  4. 部署完成后,绑定域名 ln.whyme.uno

About

对数感觉模型

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages