Skip to content

Commit 178e77d

Browse files
authored
✨ 实现批量脚本拖拽导入 / 批量脚本本地导入 (#396)
1 parent 7a0f77e commit 178e77d

3 files changed

Lines changed: 120 additions & 62 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"pako": "^2.1.0",
3535
"react": "^18.2.0",
3636
"react-dom": "^18.2.0",
37+
"react-dropzone": "^14.3.8",
3738
"react-i18next": "^15.4.1",
3839
"react-icons": "^5.3.0",
3940
"react-joyride": "^2.9.3",

pnpm-lock.yaml

Lines changed: 34 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/pages/components/layout/MainLayout.tsx

Lines changed: 85 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,61 @@ import { useAppDispatch, useAppSelector } from "@App/pages/store/hooks";
2828
import { selectThemeMode, setDarkMode } from "@App/pages/store/features/config";
2929
import { RiFileCodeLine, RiImportLine, RiPlayListAddLine, RiTerminalBoxLine, RiTimerLine } from "react-icons/ri";
3030
import { scriptClient } from "@App/pages/store/features/script";
31+
import { useDropzone } from "react-dropzone";
32+
33+
const readFile = (file: File): Promise<string> => {
34+
return new Promise((resolve) => {
35+
// 实例化 FileReader对象
36+
const reader = new FileReader();
37+
reader.onload = async (processEvent) => {
38+
// 创建blob url
39+
const blob = new Blob([processEvent.target!.result!], {
40+
type: "application/javascript",
41+
});
42+
const url = URL.createObjectURL(blob);
43+
resolve(url);
44+
};
45+
// 调用readerAsText方法读取文本
46+
reader.readAsText(file);
47+
});
48+
};
49+
50+
const uploadFiles = async (files: File[]) => {
51+
// const filterFiles = files.filter((f) => f.name.endsWith(".js"));
52+
const urls = await Promise.all(
53+
files.map((file) => {
54+
return readFile(file);
55+
})
56+
);
57+
importByUrls(urls);
58+
};
59+
60+
const importByUrls = async (urls: string[]) => {
61+
const stat = await scriptClient.importByUrls(urls);
62+
stat &&
63+
Modal.info({
64+
title: "脚本导入结果",
65+
content: (
66+
<Space direction="vertical" style={{ width: "100%" }}>
67+
<div style={{ textAlign: "center" }}>
68+
<Space size="small" style={{ fontSize: 18 }}>
69+
<IconCheckCircle style={{ color: "green" }} />
70+
{stat.success}
71+
{""}
72+
<IconCloseCircle style={{ color: "red" }} />
73+
{stat.fail}
74+
</Space>
75+
</div>
76+
{stat.msg.length > 0 && (
77+
<>
78+
<b>失败信息:</b>
79+
{stat.msg}
80+
</>
81+
)}
82+
</Space>
83+
),
84+
});
85+
};
3186

3287
const MainLayout: React.FC<{
3388
children: ReactNode;
@@ -39,6 +94,13 @@ const MainLayout: React.FC<{
3994
const importRef = useRef<RefTextAreaType>(null);
4095
const [importVisible, setImportVisible] = useState(false);
4196
const { t } = useTranslation();
97+
const { getRootProps, getInputProps, isDragActive } = useDropzone({
98+
accept: { "application/javascript": [".js"] },
99+
onDrop: (acceptedFiles) => {
100+
console.log(acceptedFiles);
101+
uploadFiles(acceptedFiles);
102+
},
103+
});
42104

43105
return (
44106
<ConfigProvider
@@ -59,35 +121,8 @@ const MainLayout: React.FC<{
59121
visible={importVisible}
60122
onOk={async () => {
61123
const urls = importRef.current!.dom.value.split("\n").filter((v) => v);
62-
try {
63-
const stat = await scriptClient.importByUrls(urls);
64-
stat &&
65-
Modal.info({
66-
title: "链接导入结果",
67-
content: (
68-
<Space direction="vertical" style={{ width: "100%" }}>
69-
<div style={{ textAlign: "center" }}>
70-
<Space size="small" style={{ fontSize: 18 }}>
71-
<IconCheckCircle style={{ color: "green" }} />
72-
{stat.success}
73-
{""}
74-
<IconCloseCircle style={{ color: "red" }} />
75-
{stat.fail}
76-
</Space>
77-
</div>
78-
{stat.msg.length > 0 && (
79-
<>
80-
<b>失败信息:</b>
81-
{stat.msg}
82-
</>
83-
)}
84-
</Space>
85-
),
86-
});
87-
setImportVisible(false);
88-
} catch (e) {
89-
Message.error(`${t("import_link_failure")}: ${e}`);
90-
}
124+
importByUrls(urls);
125+
setImportVisible(false);
91126
}}
92127
onCancel={() => {
93128
setImportVisible(false);
@@ -129,37 +164,9 @@ const MainLayout: React.FC<{
129164
<Menu.Item
130165
key="import_local"
131166
onClick={() => {
132-
const el = document.getElementById("import-local");
133-
el!.onchange = (e: Event) => {
134-
try {
135-
// 获取文件
136-
// @ts-ignore
137-
const file = e.target.files[0];
138-
// 实例化 FileReader对象
139-
const reader = new FileReader();
140-
reader.onload = async (processEvent) => {
141-
// 创建blob url
142-
const blob = new Blob(
143-
// @ts-ignore
144-
[processEvent.target!.result],
145-
{
146-
type: "application/javascript",
147-
}
148-
);
149-
const url = URL.createObjectURL(blob);
150-
await scriptClient.importByUrl(url);
151-
Message.success(t("import_local_success"));
152-
};
153-
// 调用readerAsText方法读取文本
154-
reader.readAsText(file);
155-
} catch (error) {
156-
Message.error(`${t("import_local_failure")}: ${e}`);
157-
}
158-
};
159-
el!.click();
167+
document.getElementById("import-local")?.click();
160168
}}
161169
>
162-
<input id="import-local" type="file" style={{ display: "none" }} accept=".js" />
163170
<RiImportLine /> {t("import_by_local")}
164171
</Menu.Item>
165172
<Menu.Item
@@ -230,7 +237,27 @@ const MainLayout: React.FC<{
230237
style={{
231238
background: "var(--color-fill-2)",
232239
}}
240+
{...getRootProps({ onClick: (e) => e.stopPropagation() })}
233241
>
242+
<input id="import-local" {...getInputProps({ style: { display: "none" } })} />
243+
<div
244+
style={{
245+
position: "absolute",
246+
zIndex: 100,
247+
display: isDragActive ? "flex" : "none",
248+
justifyContent: "center",
249+
alignItems: "center",
250+
inset: 0,
251+
margin: "auto",
252+
color: "grey",
253+
fontSize: 36,
254+
width: "100%",
255+
height: "100%",
256+
backdropFilter: "blur(4px)",
257+
}}
258+
>
259+
拖拽脚本到此处上传
260+
</div>
234261
{children}
235262
</Layout>
236263
</Layout>

0 commit comments

Comments
 (0)