diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 0000000..5c4100f
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,12 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(npx tsc --noEmit)",
+ "Bash(pnpm lint)",
+ "Bash(pnpm add -D typescript-eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin)",
+ "Bash(git add .)"
+ ],
+ "deny": [],
+ "ask": []
+ }
+}
diff --git a/eslint.config.js b/eslint.config.js
deleted file mode 100644
index cee1e2c..0000000
--- a/eslint.config.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import js from '@eslint/js'
-import globals from 'globals'
-import reactHooks from 'eslint-plugin-react-hooks'
-import reactRefresh from 'eslint-plugin-react-refresh'
-import { defineConfig, globalIgnores } from 'eslint/config'
-
-export default defineConfig([
- globalIgnores(['dist']),
- {
- files: ['**/*.{js,jsx}'],
- extends: [
- js.configs.recommended,
- reactHooks.configs['recommended-latest'],
- reactRefresh.configs.vite,
- ],
- languageOptions: {
- ecmaVersion: 2020,
- globals: globals.browser,
- parserOptions: {
- ecmaVersion: 'latest',
- ecmaFeatures: { jsx: true },
- sourceType: 'module',
- },
- },
- rules: {
- 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
- },
- },
-])
diff --git a/eslint.config.ts b/eslint.config.ts
new file mode 100644
index 0000000..ef1cf6c
--- /dev/null
+++ b/eslint.config.ts
@@ -0,0 +1,32 @@
+import js from '@eslint/js';
+import globals from 'globals';
+import reactHooks from 'eslint-plugin-react-hooks';
+import reactRefresh from 'eslint-plugin-react-refresh';
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config(
+ { ignores: ['dist', 'node_modules'] },
+ {
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
+ rules: {
+ ...reactHooks.configs.recommended.rules,
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ { varsIgnorePattern: '^_', argsIgnorePattern: '^_' },
+ ],
+ },
+ }
+);
diff --git a/index.html b/index.html
index d7eb0c0..2bb1305 100644
--- a/index.html
+++ b/index.html
@@ -1,13 +1,13 @@
-
+
- my-drag-app
+ 传感器及检测技术 - 实验课件系统
-
+
diff --git a/package.json b/package.json
index 8ace6cc..e7589ea 100644
--- a/package.json
+++ b/package.json
@@ -10,8 +10,9 @@
},
"scripts": {
"dev": "vite",
- "build": "vite build",
+ "build": "tsc && vite build",
"lint": "eslint .",
+ "typecheck": "tsc --noEmit",
"preview": "vite preview"
},
"dependencies": {
@@ -28,8 +29,11 @@
"devDependencies": {
"@eslint/js": "^9.36.0",
"@tailwindcss/postcss": "^4.1.14",
+ "@types/node": "^24.8.0",
"@types/react": "^19.1.16",
"@types/react-dom": "^19.1.9",
+ "@typescript-eslint/eslint-plugin": "^8.46.1",
+ "@typescript-eslint/parser": "^8.46.1",
"@vitejs/plugin-react": "^5.0.4",
"autoprefixer": "^10.4.21",
"eslint": "^9.36.0",
@@ -38,6 +42,8 @@
"globals": "^16.4.0",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.14",
+ "typescript": "^5.9.3",
+ "typescript-eslint": "^8.46.1",
"vite": "^7.1.7"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 03fbe0a..14f85e7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -42,15 +42,24 @@ importers:
'@tailwindcss/postcss':
specifier: ^4.1.14
version: 4.1.14
+ '@types/node':
+ specifier: ^24.8.0
+ version: 24.8.0
'@types/react':
specifier: ^19.1.16
version: 19.2.2
'@types/react-dom':
specifier: ^19.1.9
version: 19.2.2(@types/react@19.2.2)
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.46.1
+ version: 8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser':
+ specifier: ^8.46.1
+ version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
'@vitejs/plugin-react':
specifier: ^5.0.4
- version: 5.0.4(vite@7.1.10(jiti@2.6.1)(lightningcss@1.30.1))
+ version: 5.0.4(vite@7.1.10(@types/node@24.8.0)(jiti@2.6.1)(lightningcss@1.30.1))
autoprefixer:
specifier: ^10.4.21
version: 10.4.21(postcss@8.5.6)
@@ -72,9 +81,15 @@ importers:
tailwindcss:
specifier: ^4.1.14
version: 4.1.14
+ typescript:
+ specifier: ^5.9.3
+ version: 5.9.3
+ typescript-eslint:
+ specifier: ^8.46.1
+ version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
vite:
specifier: ^7.1.7
- version: 7.1.10(jiti@2.6.1)(lightningcss@1.30.1)
+ version: 7.1.10(@types/node@24.8.0)(jiti@2.6.1)(lightningcss@1.30.1)
packages:
@@ -417,6 +432,18 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
'@rolldown/pluginutils@1.0.0-beta.38':
resolution: {integrity: sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==}
@@ -651,6 +678,9 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ '@types/node@24.8.0':
+ resolution: {integrity: sha512-5x08bUtU8hfboMTrJ7mEO4CpepS9yBwAqcL52y86SWNmbPX8LVbNs3EP4cNrIZgdjk2NAlP2ahNihozpoZIxSg==}
+
'@types/react-dom@19.2.2':
resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==}
peerDependencies:
@@ -659,6 +689,65 @@ packages:
'@types/react@19.2.2':
resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==}
+ '@typescript-eslint/eslint-plugin@8.46.1':
+ resolution: {integrity: sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.46.1
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/parser@8.46.1':
+ resolution: {integrity: sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/project-service@8.46.1':
+ resolution: {integrity: sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/scope-manager@8.46.1':
+ resolution: {integrity: sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.46.1':
+ resolution: {integrity: sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/type-utils@8.46.1':
+ resolution: {integrity: sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/types@8.46.1':
+ resolution: {integrity: sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.46.1':
+ resolution: {integrity: sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/utils@8.46.1':
+ resolution: {integrity: sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/visitor-keys@8.46.1':
+ resolution: {integrity: sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@vitejs/plugin-react@5.0.4':
resolution: {integrity: sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -708,6 +797,13 @@ packages:
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
browserslist@4.26.3:
resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -880,12 +976,19 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+ fastq@1.19.1:
+ resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
@@ -899,6 +1002,10 @@ packages:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
@@ -946,6 +1053,10 @@ packages:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
glob-parent@6.0.2:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
@@ -965,6 +1076,9 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -985,6 +1099,10 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
@@ -1001,6 +1119,10 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -1131,6 +1253,14 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@@ -1142,6 +1272,10 @@ packages:
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -1195,6 +1329,10 @@ packages:
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
@@ -1217,6 +1355,9 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
react-dom@19.2.0:
resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==}
peerDependencies:
@@ -1251,11 +1392,18 @@ packages:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
rollup@4.52.4:
resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
@@ -1263,6 +1411,11 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
+ semver@7.7.3:
+ resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
+ engines: {node: '>=10'}
+ hasBin: true
+
set-cookie-parser@2.7.1:
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
@@ -1301,6 +1454,16 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ ts-api-utils@2.1.0:
+ resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -1308,6 +1471,21 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
+ typescript-eslint@8.46.1:
+ resolution: {integrity: sha512-VHgijW803JafdSsDO8I761r3SHrgk4T00IdyQ+/UsthtgPRsBWQLqoSxOolxTpxRKi1kGXK0bSz4CoAc9ObqJA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@7.14.0:
+ resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==}
+
update-browserslist-db@1.1.3:
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
hasBin: true
@@ -1676,6 +1854,18 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
'@rolldown/pluginutils@1.0.0-beta.38': {}
'@rollup/rollup-android-arm-eabi@4.52.4':
@@ -1841,6 +2031,10 @@ snapshots:
'@types/json-schema@7.0.15': {}
+ '@types/node@24.8.0':
+ dependencies:
+ undici-types: 7.14.0
+
'@types/react-dom@19.2.2(@types/react@19.2.2)':
dependencies:
'@types/react': 19.2.2
@@ -1849,7 +2043,100 @@ snapshots:
dependencies:
csstype: 3.1.3
- '@vitejs/plugin-react@5.0.4(vite@7.1.10(jiti@2.6.1)(lightningcss@1.30.1))':
+ '@typescript-eslint/eslint-plugin@8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/scope-manager': 8.46.1
+ '@typescript-eslint/type-utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.46.1
+ eslint: 9.37.0(jiti@2.6.1)
+ graphemer: 1.4.0
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.1.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.46.1
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.46.1
+ debug: 4.4.3
+ eslint: 9.37.0(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.46.1(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/types': 8.46.1
+ debug: 4.4.3
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.46.1':
+ dependencies:
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/visitor-keys': 8.46.1
+
+ '@typescript-eslint/tsconfig-utils@8.46.1(typescript@5.9.3)':
+ dependencies:
+ typescript: 5.9.3
+
+ '@typescript-eslint/type-utils@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ debug: 4.4.3
+ eslint: 9.37.0(jiti@2.6.1)
+ ts-api-utils: 2.1.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.46.1': {}
+
+ '@typescript-eslint/typescript-estree@8.46.1(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/visitor-keys': 8.46.1
+ debug: 4.4.3
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.3
+ ts-api-utils: 2.1.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1))
+ '@typescript-eslint/scope-manager': 8.46.1
+ '@typescript-eslint/types': 8.46.1
+ '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3)
+ eslint: 9.37.0(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.46.1':
+ dependencies:
+ '@typescript-eslint/types': 8.46.1
+ eslint-visitor-keys: 4.2.1
+
+ '@vitejs/plugin-react@5.0.4(vite@7.1.10(@types/node@24.8.0)(jiti@2.6.1)(lightningcss@1.30.1))':
dependencies:
'@babel/core': 7.28.4
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
@@ -1857,7 +2144,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.38
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
- vite: 7.1.10(jiti@2.6.1)(lightningcss@1.30.1)
+ vite: 7.1.10(@types/node@24.8.0)(jiti@2.6.1)(lightningcss@1.30.1)
transitivePeerDependencies:
- supports-color
@@ -1909,6 +2196,14 @@ snapshots:
balanced-match: 1.0.2
concat-map: 0.0.1
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
browserslist@4.26.3:
dependencies:
baseline-browser-mapping: 2.8.16
@@ -2109,10 +2404,22 @@ snapshots:
fast-deep-equal@3.1.3: {}
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
fast-json-stable-stringify@2.1.0: {}
fast-levenshtein@2.0.6: {}
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
@@ -2121,6 +2428,10 @@ snapshots:
dependencies:
flat-cache: 4.0.1
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
@@ -2170,6 +2481,10 @@ snapshots:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
glob-parent@6.0.2:
dependencies:
is-glob: 4.0.3
@@ -2182,6 +2497,8 @@ snapshots:
graceful-fs@4.2.11: {}
+ graphemer@1.4.0: {}
+
has-flag@4.0.0: {}
has-symbols@1.1.0: {}
@@ -2196,6 +2513,8 @@ snapshots:
ignore@5.3.2: {}
+ ignore@7.0.5: {}
+
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
@@ -2209,6 +2528,8 @@ snapshots:
dependencies:
is-extglob: 2.1.1
+ is-number@7.0.0: {}
+
isexe@2.0.0: {}
jiti@2.6.1: {}
@@ -2303,6 +2624,13 @@ snapshots:
math-intrinsics@1.1.0: {}
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
mime-db@1.52.0: {}
mime-types@2.1.35:
@@ -2313,6 +2641,10 @@ snapshots:
dependencies:
brace-expansion: 1.1.12
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
minipass@7.1.2: {}
minizlib@3.1.0:
@@ -2356,6 +2688,8 @@ snapshots:
picocolors@1.1.1: {}
+ picomatch@2.3.1: {}
+
picomatch@4.0.3: {}
postcss-value-parser@4.2.0: {}
@@ -2372,6 +2706,8 @@ snapshots:
punycode@2.3.1: {}
+ queue-microtask@1.2.3: {}
+
react-dom@19.2.0(react@19.2.0):
dependencies:
react: 19.2.0
@@ -2397,6 +2733,8 @@ snapshots:
resolve-from@4.0.0: {}
+ reusify@1.1.0: {}
+
rollup@4.52.4:
dependencies:
'@types/estree': 1.0.8
@@ -2425,10 +2763,16 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.52.4
fsevents: 2.3.3
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
scheduler@0.27.0: {}
semver@6.3.1: {}
+ semver@7.7.3: {}
+
set-cookie-parser@2.7.1: {}
shebang-command@2.0.0:
@@ -2462,12 +2806,35 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ ts-api-utils@2.1.0(typescript@5.9.3):
+ dependencies:
+ typescript: 5.9.3
+
tslib@2.8.1: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
+ typescript-eslint@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)
+ eslint: 9.37.0(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@5.9.3: {}
+
+ undici-types@7.14.0: {}
+
update-browserslist-db@1.1.3(browserslist@4.26.3):
dependencies:
browserslist: 4.26.3
@@ -2478,7 +2845,7 @@ snapshots:
dependencies:
punycode: 2.3.1
- vite@7.1.10(jiti@2.6.1)(lightningcss@1.30.1):
+ vite@7.1.10(@types/node@24.8.0)(jiti@2.6.1)(lightningcss@1.30.1):
dependencies:
esbuild: 0.25.11
fdir: 6.5.0(picomatch@4.0.3)
@@ -2487,6 +2854,7 @@ snapshots:
rollup: 4.52.4
tinyglobby: 0.2.15
optionalDependencies:
+ '@types/node': 24.8.0
fsevents: 2.3.3
jiti: 2.6.1
lightningcss: 1.30.1
diff --git a/postcss.config.js b/postcss.config.ts
similarity index 100%
rename from postcss.config.js
rename to postcss.config.ts
diff --git a/src/App.jsx b/src/App.tsx
similarity index 78%
rename from src/App.jsx
rename to src/App.tsx
index aa9a4e2..5c690b2 100644
--- a/src/App.jsx
+++ b/src/App.tsx
@@ -1,5 +1,5 @@
import { RouterProvider } from 'react-router-dom';
-import router from './router.jsx';
+import router from './router/router';
function App() {
return ;
diff --git a/src/components/DraggableItem.jsx b/src/components/DraggableItem.tsx
similarity index 83%
rename from src/components/DraggableItem.jsx
rename to src/components/DraggableItem.tsx
index c82bc6a..f08c299 100644
--- a/src/components/DraggableItem.jsx
+++ b/src/components/DraggableItem.tsx
@@ -1,8 +1,14 @@
+import { ReactNode } from 'react';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { GripVertical } from 'lucide-react';
-export function DraggableItem({ id, children }) {
+interface DraggableItemProps {
+ id: string;
+ children: ReactNode;
+}
+
+export function DraggableItem({ id, children }: DraggableItemProps) {
const {
attributes,
listeners,
diff --git a/src/data/experiments.js b/src/data/experiments.ts
similarity index 73%
rename from src/data/experiments.js
rename to src/data/experiments.ts
index 906b14b..65d2423 100644
--- a/src/data/experiments.js
+++ b/src/data/experiments.ts
@@ -1,6 +1,17 @@
+import { LucideIcon } from 'lucide-react';
import { Thermometer, Gauge, Radio, Zap, Droplets } from 'lucide-react';
-export const defaultExperiments = [
+/**
+ * 实验项目接口
+ */
+export interface Experiment {
+ id: string;
+ name: string;
+ icon: LucideIcon;
+ description: string;
+}
+
+export const defaultExperiments: Experiment[] = [
{
id: '1',
name: '温度传感器实验',
@@ -38,17 +49,17 @@ const STORAGE_KEY = 'sensor-lab-experiments';
/**
* 从 localStorage 加载实验数据
- * @returns {Array} 返回保存的实验顺序,如果没有保存则返回 null
+ * @returns 返回保存的实验顺序,如果没有保存则返回 null
*/
-export const loadExperimentsFromStorage = () => {
+export const loadExperimentsFromStorage = (): Experiment[] | null => {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
- const storedIds = JSON.parse(stored);
+ const storedIds = JSON.parse(stored) as string[];
// 根据保存的 ID 顺序重新排列实验数据
const orderedExperiments = storedIds
.map((id) => defaultExperiments.find((exp) => exp.id === id))
- .filter(Boolean); // 过滤掉可能不存在的项目
+ .filter((exp): exp is Experiment => exp !== undefined);
// 如果顺序数量匹配,返回排序后的数据
if (orderedExperiments.length === defaultExperiments.length) {
@@ -63,9 +74,9 @@ export const loadExperimentsFromStorage = () => {
/**
* 保存实验数据到 localStorage
- * @param {Array} experiments - 实验数据数组
+ * @param experiments - 实验数据数组
*/
-export const saveExperimentsToStorage = (experiments) => {
+export const saveExperimentsToStorage = (experiments: Experiment[]): void => {
try {
const ids = experiments.map((exp) => exp.id);
localStorage.setItem(STORAGE_KEY, JSON.stringify(ids));
diff --git a/src/main.jsx b/src/main.jsx
deleted file mode 100644
index b9a1a6d..0000000
--- a/src/main.jsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './index.css'
-import App from './App.jsx'
-
-createRoot(document.getElementById('root')).render(
-
-
- ,
-)
diff --git a/src/main.tsx b/src/main.tsx
new file mode 100644
index 0000000..5b03395
--- /dev/null
+++ b/src/main.tsx
@@ -0,0 +1,15 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App';
+
+const rootElement = document.getElementById('root');
+if (!rootElement) {
+ throw new Error('Root element not found');
+}
+
+createRoot(rootElement).render(
+
+
+
+);
diff --git a/src/pages/ExperimentDetail.jsx b/src/pages/ExperimentDetail.tsx
similarity index 100%
rename from src/pages/ExperimentDetail.jsx
rename to src/pages/ExperimentDetail.tsx
diff --git a/src/pages/Home.jsx b/src/pages/Home.tsx
similarity index 96%
rename from src/pages/Home.jsx
rename to src/pages/Home.tsx
index 090a29e..fa77c25 100644
--- a/src/pages/Home.jsx
+++ b/src/pages/Home.tsx
@@ -7,6 +7,7 @@ import {
PointerSensor,
useSensor,
useSensors,
+ DragEndEvent,
} from '@dnd-kit/core';
import {
arrayMove,
@@ -41,7 +42,7 @@ export default function Home() {
})
);
- function handleDragEnd(event) {
+ function handleDragEnd(event: DragEndEvent) {
const { active, over } = event;
if (over && active.id !== over.id) {
@@ -53,7 +54,7 @@ export default function Home() {
}
}
- function handleExperimentClick(id) {
+ function handleExperimentClick(id: string) {
navigate(`/experiment/${id}`);
}
diff --git a/src/pages/Layout.jsx b/src/pages/Layout.tsx
similarity index 100%
rename from src/pages/Layout.jsx
rename to src/pages/Layout.tsx
diff --git a/src/pages/NotFound.jsx b/src/pages/NotFound.tsx
similarity index 100%
rename from src/pages/NotFound.jsx
rename to src/pages/NotFound.tsx
diff --git a/src/router.jsx b/src/router/router.tsx
similarity index 74%
rename from src/router.jsx
rename to src/router/router.tsx
index a94110f..b645c86 100644
--- a/src/router.jsx
+++ b/src/router/router.tsx
@@ -1,8 +1,8 @@
import { createBrowserRouter } from 'react-router-dom';
-import Layout from './pages/Layout';
-import Home from './pages/Home';
-import ExperimentDetail from './pages/ExperimentDetail';
-import NotFound from './pages/NotFound';
+import Layout from '../pages/Layout';
+import Home from '../pages/Home';
+import ExperimentDetail from '../pages/ExperimentDetail';
+import NotFound from '../pages/NotFound';
/**
* 应用路由配置
diff --git a/tailwind.config.js b/tailwind.config.ts
similarity index 100%
rename from tailwind.config.js
rename to tailwind.config.ts
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..4a5655b
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,35 @@
+{
+ "compilerOptions": {
+ // 基础选项
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ // 模块解析选项
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "resolveJsonModule": true,
+ "noEmit": true,
+
+ // JSX 支持
+ "jsx": "react-jsx",
+
+ // 类型检查
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+
+ // 其他选项
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "include": ["src"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..c953b46
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true
+ },
+ "include": ["vite.config.ts", "eslint.config.ts"]
+}
diff --git a/vite.config.js b/vite.config.ts
similarity index 100%
rename from vite.config.js
rename to vite.config.ts