init: 0.0.4

This commit is contained in:
loveuer
2025-04-27 16:38:51 +08:00
commit fb96934abe
24 changed files with 3297 additions and 0 deletions

24
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

54
frontend/README.md Normal file
View File

@ -0,0 +1,54 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default tseslint.config({
extends: [
// Remove ...tseslint.configs.recommended and replace with this
...tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
...tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
...tseslint.configs.stylisticTypeChecked,
],
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default tseslint.config({
plugins: {
// Add the react-x and react-dom plugins
'react-x': reactX,
'react-dom': reactDom,
},
rules: {
// other rules...
// Enable its recommended typescript rules
...reactX.configs['recommended-typescript'].rules,
...reactDom.configs.recommended.rules,
},
})
```

28
frontend/eslint.config.js Normal file
View File

@ -0,0 +1,28 @@
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'] },
{
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 },
],
},
},
)

13
frontend/index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

31
frontend/package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwindcss": "^4.1.4"
},
"devDependencies": {
"@eslint/js": "^9.22.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.22.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.26.1",
"vite": "^6.3.1"
}
}

2366
frontend/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

0
frontend/src/App.css Normal file
View File

5
frontend/src/App.tsx Normal file
View File

@ -0,0 +1,5 @@
import {FileSharing} from "./page/share.tsx";
export function App() {
return <FileSharing />;
}

28
frontend/src/index.css Normal file
View File

@ -0,0 +1,28 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
padding: 0;
}

10
frontend/src/main.tsx Normal file
View File

@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import {App} from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

View File

@ -0,0 +1,15 @@
@import "tailwindcss";
.mid-left {
background-color: #e3f2fd; /* 柔和的浅蓝色 */
}
.mid-right {
background-color: #e8f5e9; /* 柔和的浅绿色 */
}
.share-left {
background-color: #e3f2fd; /* 柔和的浅蓝色 */
}
.share-right {
background-color: #e8f5e9;
}

130
frontend/src/page/share.tsx Normal file
View File

@ -0,0 +1,130 @@
import "./share.css";
// FileSharing.tsx
import { useRef, useState } from 'react';
type FileItemProps = {
fileName: string;
onClear: () => void;
};
const FileItem = ({ fileName, onClear }: FileItemProps) => (
<div className="flex items-center gap-2 my-2">
<button
className="w-5 h-5 rounded-full flex items-center justify-center text-white hover:bg-red-400 transition-colors"
onClick={onClear}
>
×
</button>
<span className="text-gray-700">{fileName}</span>
</div>
);
const MiddlePanel = () => (
<div className="relative bg-gray-200 overflow-hidden">
<div
className="mid-left absolute w-full h-full bg-blue-100"
style={{ clipPath: 'polygon(0 0, 100% 0, 0 100%)' }}
/>
<div
className="mid-right absolute w-full h-full bg-green-100"
style={{ clipPath: 'polygon(100% 0, 100% 100%, 0 100%)' }}
/>
</div>
);
const UploadPanel = () => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleFileSelect = () => {
if (!selectedFile && fileInputRef.current) {
fileInputRef.current.click();
} else {
// Handle upload logic
if (selectedFile) {
const formData = new FormData();
formData.append('file', selectedFile);
// Replace with actual API call
fetch('https://your-upload-api.com/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
alert(`Upload success! Code: ${data.code}`);
setSelectedFile(null);
})
.catch(() => alert('Upload failed'));
}
}
};
return (
<div className="share-left flex flex-col items-center justify-center p-5 bg-blue-50 h-full">
<div className="bg-blue-100 rounded-xl p-6 w-4/5 shadow-inner">
<h2 className="text-xl font-semibold mb-4 text-cyan-900"></h2>
<input
type="file"
ref={fileInputRef}
className="hidden"
onChange={(e) => e.target.files?.[0] && setSelectedFile(e.target.files[0])}
/>
<button
onClick={handleFileSelect}
className="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 transition-colors"
>
{selectedFile ? '确认上传' : '选择文件'}
</button>
{selectedFile && (
<FileItem
fileName={selectedFile.name}
onClear={() => setSelectedFile(null)}
/>
)}
</div>
</div>
);
};
const DownloadPanel = () => {
const [downloadCode, setDownloadCode] = useState('');
const handleDownload = () => {
if (!downloadCode) {
alert('请输入下载码');
return;
}
// Replace with actual download logic
window.location.href = `https://your-download-api.com/download?code=${downloadCode}`;
};
return (
<div className="share-right flex flex-col items-center justify-center p-5 bg-green-50 h-full">
<div className="bg-green-100 rounded-xl p-6 w-4/5 shadow-inner">
<h2 className="text-xl font-semibold mb-4 text-teal-900"></h2>
<input
type="text"
placeholder="输入下载码"
className="w-full px-3 py-2 border-2 border-gray-200 rounded-lg mb-4"
value={downloadCode}
onChange={(e) => setDownloadCode(e.target.value)}
/>
<button
onClick={handleDownload}
className="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 transition-colors w-full"
>
</button>
</div>
</div>
);
};
export const FileSharing = () => (
<div className="h-screen grid grid-cols-[40%_20%_40%]">
<UploadPanel />
<MiddlePanel />
<DownloadPanel />
</div>
);

1
frontend/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

7
frontend/tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"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,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

8
frontend/vite.config.ts Normal file
View File

@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
})