feat:
1. local msg/file share by webrtc fix: 1. meta clean goroutine walk error 2. clean interval to args(--clean)
This commit is contained in:
220
frontend/src/page/share/component/panel-left.tsx
Normal file
220
frontend/src/page/share/component/panel-left.tsx
Normal file
@@ -0,0 +1,220 @@
|
||||
import {createUseStyles} from "react-jss";
|
||||
import {UButton} from "../../../component/button/u-button.tsx";
|
||||
import React, {useState} from "react";
|
||||
import {useStore} from "../../../store/share.ts";
|
||||
import {message} from "../../../hook/message/u-message.tsx";
|
||||
import {useFileUpload} from "../../../api/upload.ts";
|
||||
|
||||
const useUploadStyle = 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': {}
|
||||
}
|
||||
})
|
||||
|
||||
const useShowStyle = createUseStyles({
|
||||
container: {
|
||||
backgroundColor: "#e3f2fd",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative", // 为关闭按钮提供定位基准
|
||||
},
|
||||
title: {
|
||||
color: "#2c9678",
|
||||
marginTop: 0,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
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",
|
||||
position: "relative",
|
||||
},
|
||||
closeButton: {
|
||||
position: "absolute",
|
||||
top: "10px",
|
||||
right: "10px",
|
||||
background: "transparent",
|
||||
color: "white",
|
||||
border: "none",
|
||||
borderRadius: "50%",
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
cursor: "pointer",
|
||||
"&:hover": {
|
||||
// background: "#cc0000",
|
||||
boxShadow: "20px 20px 60px #fff, -20px -20px 60px #fff",
|
||||
// boxShadow: "20px 20px 60px #eee",
|
||||
},
|
||||
},
|
||||
codeWrapper: {
|
||||
backgroundColor: "rgba(255,255,255,0.8)",
|
||||
padding: "0 15px",
|
||||
borderRadius: "8px",
|
||||
margin: "15px 0",
|
||||
overflowX: "auto",
|
||||
},
|
||||
pre: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
color: 'black',
|
||||
alignItems: 'center',
|
||||
height: '24px',
|
||||
"& > code": {
|
||||
marginLeft: "0",
|
||||
}
|
||||
},
|
||||
copyButton: {
|
||||
marginLeft: 'auto',
|
||||
background: "#2c9678",
|
||||
color: "white",
|
||||
border: "none",
|
||||
padding: "8px 16px",
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
transition: "background 0.3s",
|
||||
"&:hover": {
|
||||
background: "#1f6d5a",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const PanelLeft = () => {
|
||||
const [code, set_code] = useState("")
|
||||
|
||||
if (code) {
|
||||
return <PanelLeftShow code={code} set_code={set_code} />
|
||||
}
|
||||
|
||||
return <PanelLeftUpload set_code={set_code}/>
|
||||
};
|
||||
|
||||
const PanelLeftUpload: React.FC<{ set_code: (code:string) => void }> = ({set_code}) => {
|
||||
const style = useUploadStyle()
|
||||
const {file, setFile} = useStore()
|
||||
const {uploadFile, progress, loading} = useFileUpload();
|
||||
|
||||
function onFileSelect() {
|
||||
// @ts-ignore
|
||||
document.querySelector('#real-file-input').click();
|
||||
}
|
||||
|
||||
function onFileChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
setFile(e.currentTarget.files ? e.currentTarget.files[0] : null)
|
||||
}
|
||||
|
||||
async function onFileUpload() {
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
|
||||
const code = await uploadFile(file)
|
||||
set_code(code)
|
||||
}
|
||||
|
||||
function onFileClean() {
|
||||
setFile(null)
|
||||
}
|
||||
|
||||
return <div className={style.container}>
|
||||
<div className={style.form}>
|
||||
<h2 className={style.title}>上传文件</h2>
|
||||
{
|
||||
!file && !loading &&
|
||||
<UButton onClick={onFileSelect}>选择文件</UButton>
|
||||
}
|
||||
{
|
||||
file && !loading &&
|
||||
<UButton onClick={onFileUpload}>上传文件</UButton>
|
||||
}
|
||||
{
|
||||
loading &&
|
||||
<UButton process={progress} loading={loading}>上传中</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>
|
||||
}
|
||||
|
||||
const PanelLeftShow: React.FC<{ code: string; set_code: (code: string) => void }> = ({ code, set_code }) => {
|
||||
const classes = useShowStyle();
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
message.success("复制成功!");
|
||||
} catch (err) {
|
||||
message.warning("复制失败,请手动选择文本复制");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.container}>
|
||||
|
||||
<div className={classes.form}>
|
||||
<button
|
||||
className={classes.closeButton}
|
||||
onClick={() => set_code('')}
|
||||
aria-label="关闭"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
<h2 className={classes.title}>
|
||||
上传成功!
|
||||
</h2>
|
||||
|
||||
<div className={classes.codeWrapper}>
|
||||
<pre className={classes.pre}>
|
||||
<code>{code}</code>
|
||||
<button className={classes.copyButton} onClick={handleCopy}>
|
||||
一键复制
|
||||
</button>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
30
frontend/src/page/share/component/panel-mid.tsx
Normal file
30
frontend/src/page/share/component/panel-mid.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import {createUseStyles} from "react-jss";
|
||||
|
||||
const useStyle = createUseStyles({
|
||||
container: {
|
||||
backgroundColor: 'lightgray',
|
||||
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>
|
||||
};
|
||||
67
frontend/src/page/share/component/panel-right.tsx
Normal file
67
frontend/src/page/share/component/panel-right.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
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 = `/ushare/${code}`
|
||||
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>
|
||||
};
|
||||
26
frontend/src/page/share/share.tsx
Normal file
26
frontend/src/page/share/share.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
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";
|
||||
|
||||
const useStyle = createUseStyles({
|
||||
"@global": {
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
},
|
||||
container: {
|
||||
margin: 0,
|
||||
height: "100vh",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "40% 20% 40%",
|
||||
},
|
||||
})
|
||||
|
||||
export const FileSharing = () => {
|
||||
const style = useStyle()
|
||||
return <div className={style.container}>
|
||||
<PanelLeft />
|
||||
<PanelMid/>
|
||||
<PanelRight/>
|
||||
</div>
|
||||
};
|
||||
Reference in New Issue
Block a user