init: 0.0.6

next: upload
This commit is contained in:
loveuer
2025-04-28 14:59:22 +08:00
parent 4801b3de08
commit 0cfd617922
19 changed files with 713 additions and 355 deletions

View File

View File

@ -0,0 +1,33 @@
import React, {ReactNode} from 'react';
import {createUseStyles} from "react-jss";
const useStyle = createUseStyles({
ubutton: {
backgroundColor: "#4CAF50",
color: "white",
padding: "10px 20px",
border: "none",
borderRadius: "5px",
cursor: "pointer",
transition: "background-color 0.3s",
"&:hover": {
backgroundColor: "#45a049",
},
"&:disabled": {
backgroundColor: "#a5d6a7",
cursor: "not-allowed",
},
}
})
type Props = {
onClick: () => void;
children: ReactNode;
disabled?: boolean;
style?: React.CSSProperties | undefined;
};
export const UButton: React.FC<Props> = ({onClick, children, style,disabled = false}) => {
const classes= useStyle()
return <button style={style} className={classes.ubutton} disabled={disabled} onClick={onClick}>
{children}
</button>
}

View File

@ -0,0 +1,85 @@
import {createUseStyles} from "react-jss";
import {UButton} from "../../component/button/u-button.tsx";
import React from "react";
import {useStore} from "../../store/share.ts";
const useStyle = createUseStyles({
container: {
backgroundColor: "#e3f2fd",
display: "flex",
justifyContent: "center",
alignItems: "center",
},
form: {
backgroundColor: "#C8E6C9",
boxShadow: "inset 0 0 15px rgba(56, 142, 60, 0.15)",
padding: "30px",
borderRadius: "15px",
width: "70%",
margin: "20px 60px 20px 0",
/*todo margin 不用 px*/
},
title: {
color: "#2c9678"
},
file: {
display: 'none',
},
preview: {
marginTop: '10px',
display: 'flex',
},
name: {
color: "#2c9678",
marginLeft: '10px'
},
clean: {
borderRadius: '50%',
cursor: 'pointer',
'&:hover': {}
}
})
export const PanelLeft = () => {
const style = useStyle()
const {file, setFile} = useStore()
function onFileSelect() {
// @ts-ignore
document.querySelector('#real-file-input').click();
}
function onFileChange(e: React.ChangeEvent<HTMLInputElement>) {
console.log('[D] onFileChange: e =', e)
setFile(e.currentTarget.files ? e.currentTarget.files[0] : null)
}
function onFileUpload() {
console.log('[D] onFileUpload: upload file =', file)
}
function onFileClean() {
setFile(null)
}
return <div className={style.container}>
<div className={style.form}>
<h2 className={style.title}></h2>
{
!file &&
<UButton onClick={onFileSelect}></UButton>
}
{
file &&
<UButton onClick={onFileUpload}></UButton>
}
<input type="file" className={style.file} id="real-file-input" onChange={onFileChange}/>
{
file &&
<div className={style.preview}>
<div className={style.clean} onClick={onFileClean}>×</div>
<div className={style.name}>{file.name}</div>
</div>
}
</div>
</div>
};

View File

@ -0,0 +1,29 @@
import {createUseStyles} from "react-jss";
const useStyle = createUseStyles({
container: {
position: "relative",
overflow: "hidden",
},
left: {
backgroundColor: "#e3f2fd",
position: "absolute",
width: "100%",
height: "100%",
clipPath: 'polygon(0 0, 100% 0, 0 100%)'
},
right: {
backgroundColor: "#e8f5e9",
position: "absolute",
width: "100%",
height: "100%",
clipPath: 'polygon(100% 0, 100% 100%, 0 100%)'
},
})
export const PanelMid = () => {
const style = useStyle()
return <div className={style.container}>
<div className={style.left}></div>
<div className={style.right}></div>
</div>
};

View File

@ -0,0 +1,68 @@
import {createUseStyles} from "react-jss";
import {UButton} from "../../component/button/u-button.tsx";
import {useStore} from "../../store/share.ts";
const useStyle = createUseStyles({
container: {
backgroundColor: "#e8f5e9",
display: "flex",
justifyContent: "center",
alignItems: "center",
},
form: {
backgroundColor: "#BBDEFB",
boxShadow: "inset 0 0 15px rgba(33, 150, 243, 0.15)",
padding: "30px",
borderRadius: "15px",
width: "70%",
margin: "20px 0 20px 60px",
/*todo margin 不用 px*/
},
title: {
color: '#1661ab', // 靛青
},
code: {
padding: '11px',
margin: '20px 0',
width: '200px',
border: '2px solid #ddd',
borderRadius: '5px',
'&:active': {
border: '2px solid #1661ab',
}
}
})
export const PanelRight = () => {
const style = useStyle()
const {code, setCode} = useStore()
function onCodeChange(e: React.ChangeEvent<HTMLInputElement>) {
setCode(e.currentTarget.value)
}
async function onFetchFile() {
const url = `/api/share/fetch?code=${code}`
console.log('[D] onFetchFile: url =', url)
const link = document.createElement('a');
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
return <div className={style.container}>
<div className={style.form}>
<h2 className={style.title}></h2>
<div>
<input
type="text"
className={style.code}
placeholder="输入下载码"
value={code}
onChange={onCodeChange}
/>
<UButton style={{marginLeft: '10px'}} onClick={onFetchFile}></UButton>
</div>
</div>
</div>
};

View File

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

View File

@ -1,130 +1,26 @@
import "./share.css";
// FileSharing.tsx
import { useRef, useState } from 'react';
import {createUseStyles} from 'react-jss'
import {PanelLeft} from "./component/panel-left.tsx";
import {PanelRight} from "./component/panel-right.tsx";
import {PanelMid} from "./component/panel-mid.tsx";
type FileItemProps = {
fileName: string;
onClear: () => void;
};
const useStyle = createUseStyles({
"@global": {
margin: 0,
padding: 0,
},
container: {
margin: 0,
height: "100vh",
display: "grid",
gridTemplateColumns: "40% 20% 40%",
},
})
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>
export const FileSharing = () => {
const style = useStyle()
return <div className={style.container}>
<PanelLeft />
<PanelMid/>
<PanelRight/>
</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>
);
};

View File

@ -0,0 +1,23 @@
import {create} from 'zustand'
type Store = {
file: File | null,
setFile: (f: File | null) => void
code: string,
setCode: (value:string) => void
}
export const useStore = create<Store>()((set) => ({
file: null,
setFile: (f: File | null = null) => {
set(state => {
return {...state, file: f}
})
},
code: '',
setCode: (value:string= '') => {
set(state => {
return {...state, code: value}
})
}
}))