123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- import { useState } from "react";
- import styles from "../styles/Builder.module.css";
- interface IMap {
- [key: string]: boolean;
- }
- const pluginList = ["Sort", "Filter", "Comment", "Alternating Colors"];
- // TODO 选项匹配插件名称
- // TODO 代码包含一个完整的运行案例,直接运行,或者npm i 后运行,README.md引导
- // TODO 下载代码压缩文件
- // TODO 下载完成之后,服务器新复制的项目清除掉
- // TODO 测试并发能力
- function Builder() {
- const pluginMap: IMap = {};
- const [msgList, setMsgList] = useState<string[]>([]);
- const [progressValue, setProgressValue] = useState<number>(0);
- const [buildId, setBuildId] = useState<string>("");
- // const
- const check = (e: React.ChangeEvent<HTMLInputElement>) => {
- pluginMap[e.target.value] = e.target.checked;
- };
- const build = () => {
- const buildList = [];
- for (const plugin in pluginMap) {
- if (pluginMap[plugin] === true) {
- buildList.push(plugin);
- }
- }
- postData("/api/build", { buildList }).then((data) => {
- setBuildId(data.buildId);
- startBuild(data.buildId);
- });
- };
- const download = () => {
- fetch("/api/download?buildId=" + buildId, {
- method: "GET", // *GET, POST, PUT, DELETE, etc.
- mode: "cors", // no-cors, *cors, same-origin
- cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
- credentials: "same-origin", // include, *same-origin, omit
- headers: {
- "Content-Type": "application/octet-stream",
- // 'Content-Type': 'application/x-www-form-urlencoded',
- },
- redirect: "follow", // manual, *follow, error
- referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
- }).then ((res) => res.blob()).then((blob)=>{
- downloadFile(blob,'luckysheet-custom-build.zip')
- })
- };
- /**
- * reference: https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
- * @param url
- * @param data
- * @returns
- */
- async function postData(url = "", data = {}) {
- // Default options are marked with *
- const response = await fetch(url, {
- method: "POST", // *GET, POST, PUT, DELETE, etc.
- mode: "cors", // no-cors, *cors, same-origin
- cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
- credentials: "same-origin", // include, *same-origin, omit
- headers: {
- "Content-Type": "application/json",
- // 'Content-Type': 'application/x-www-form-urlencoded',
- },
- redirect: "follow", // manual, *follow, error
- referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
- body: JSON.stringify(data), // body data type must match "Content-Type" header
- });
- return response.json(); // parses JSON response into native JavaScript objects
- }
- async function getFile(url = "") {
- // Default options are marked with *
- const response = await fetch(url, {
- method: "GET", // *GET, POST, PUT, DELETE, etc.
- mode: "cors", // no-cors, *cors, same-origin
- cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
- credentials: "same-origin", // include, *same-origin, omit
- headers: {
- "Content-Type": "application/octet-stream",
- // 'Content-Type': 'application/x-www-form-urlencoded',
- },
- redirect: "follow", // manual, *follow, error
- referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
- });
- return response; // parses JSON response into native JavaScript objects
- }
- function startBuild(buildId: string) {
- // when "Start" button pressed
- if (!window.EventSource) {
- // IE or an old browser
- alert("The browser doesn't support EventSource.");
- return;
- }
- const eventSource = new EventSource("/api/progress?buildId=" + buildId);
- let retryCount = 0;
- eventSource.onopen = function (e) {
- console.log("Event: open");
- };
- eventSource.onerror = function (this: EventSource) {
- console.log(`Event: readyState=${this.readyState})`);
- if (this.readyState == EventSource.CLOSED) {
- console.log("Event: Connection was closed.");
- } else if (this.readyState == EventSource.CONNECTING) {
- eventSource.close();
- console.log("Event: Connection was closed.");
- // if (++retryCount === 5) {
- // eventSource.close();
- // }
- } else {
- console.log("Event: Error has occured.");
- }
- };
- eventSource.addEventListener("progress", function (e) {
- const dataList = e.data.split("-");
- const msg = dataList[0];
- const progress = dataList[1];
- setMsgList((msgList) => [...msgList, msg]);
- setProgressValue(progress * 100);
- if (msg === "Success" || msg === "Fail") {
- eventSource.close();
- }
- });
- }
- /**
- * 下载文件
- * @param content 文件流
- * @param fileName 文件名称
- */
- function downloadFile(blob: any, fileName: string) {
- // const blob = new Blob([content], {
- // type: 'application/octet-stream'
- // });
- const a = document.createElement("a");
- const url = window.URL.createObjectURL(blob);
- a.href = url;
- a.download = fileName;
- document.body.appendChild(a); // we need to append the element to the dom -> otherwise it will not work in firefox
- a.click();
- window.URL.revokeObjectURL(url);
- a.remove(); //afterwards we remove the element again
- }
-
- return (
- <div className={styles.container}>
- <h2>Luckysheet Custom Builder</h2>
- <div className="container">
- <h3>Choose plugins</h3>
- <div>
- {pluginList.map((plugin) => {
- return (
- <p key={plugin}>
- <input
- type="checkbox"
- id={plugin}
- value={plugin}
- onChange={check}
- ></input>
- <label htmlFor={plugin}>{plugin}</label>
- </p>
- );
- })}
- </div>
- <div className={styles.operate}>
- <button onClick={build}>build</button>
- </div>
- <div>
- {progressValue === 0 ? (
- ""
- ) : (
- <div>
- <progress
- id="progress-bar"
- max="100"
- value={progressValue}
- ></progress>
- <span>{progressValue}%</span>
- </div>
- )}
- <div>
- <h3>Build Log</h3>
- <ul>
- {msgList.map((msg, i) => (
- <li key={i}>{msg}</li>
- ))}
- </ul>
- </div>
- <div>
- {/* <button onClick={download}>download</button> */}
- {progressValue === 100 ? (
- <button onClick={download}>download</button>
- ) : (
- ""
- )}
- </div>
- </div>
- </div>
- </div>
- );
- }
- export default Builder;
|