diff --git a/config/webpack.common.js b/config/webpack.common.js index 907e2b4..823093f 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -123,6 +123,10 @@ const resolve = (plugins = []) => { let res = { extensions: [ ".tsx", '.ts', '.js' ], + fallback: { + 'react/jsx-runtime': 'react/jsx-runtime.js', + 'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js', + }, plugins: plugins }; diff --git a/package-lock.json b/package-lock.json index c38adc1..b8eb1b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,8 @@ "express": "^4.17.3", "gl-matrix": "^3.4.3", "react": "^17.0.2", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", "react-dom": "^17.0.2", "uuid": "^8.3.2" }, @@ -726,6 +728,17 @@ "node": ">=4" } }, + "node_modules/@babel/runtime": { + "version": "7.17.9", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", @@ -1023,6 +1036,21 @@ "node": ">=10" } }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "node_modules/@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-0.14.0.tgz", @@ -1195,7 +1223,7 @@ "version": "17.0.21", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true + "devOptional": true }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -2817,6 +2845,16 @@ "node": ">=8" } }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmmirror.com/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "node_modules/dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -3317,8 +3355,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.2.11", @@ -3945,6 +3982,14 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -6164,6 +6209,43 @@ "node": ">=0.10.0" } }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmmirror.com/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmmirror.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dependencies": { + "dnd-core": "^16.0.1" + } + }, "node_modules/react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -6177,6 +6259,11 @@ "react": "17.0.2" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -6305,6 +6392,19 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, "node_modules/regexp.prototype.flags": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", @@ -8767,6 +8867,14 @@ } } }, + "@babel/runtime": { + "version": "7.17.9", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "@discoveryjs/json-ext": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", @@ -9013,6 +9121,21 @@ "rimraf": "^3.0.2" } }, + "@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-0.14.0.tgz", @@ -9176,7 +9299,7 @@ "version": "17.0.21", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true + "devOptional": true }, "@types/normalize-package-data": { "version": "2.4.1", @@ -10488,6 +10611,16 @@ "path-type": "^4.0.0" } }, + "dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmmirror.com/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "requires": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -10905,8 +11038,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { "version": "3.2.11", @@ -11384,6 +11516,14 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -13051,6 +13191,26 @@ "object-assign": "^4.1.1" } }, + "react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmmirror.com/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "requires": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + } + }, + "react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmmirror.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "requires": { + "dnd-core": "^16.0.1" + } + }, "react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -13061,6 +13221,11 @@ "scheduler": "^0.20.2" } }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -13163,6 +13328,19 @@ "strip-indent": "^3.0.0" } }, + "redux": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, "regexp.prototype.flags": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", diff --git a/package.json b/package.json index 9ccdc33..67b5b24 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,8 @@ "express": "^4.17.3", "gl-matrix": "^3.4.3", "react": "^17.0.2", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", "react-dom": "^17.0.2", "uuid": "^8.3.2" } diff --git a/source/Component/ConfirmPopup/ConfirmPopup.tsx b/source/Component/ConfirmPopup/ConfirmPopup.tsx index 98ca844..b27e56a 100644 --- a/source/Component/ConfirmPopup/ConfirmPopup.tsx +++ b/source/Component/ConfirmPopup/ConfirmPopup.tsx @@ -9,6 +9,7 @@ interface IConfirmPopupProps { titleI18N?: AllI18nKeys; titleI18NOption?: Record; infoI18n?: AllI18nKeys; + infoI18nOption?: Record; yesI18n?: AllI18nKeys; noI18n?: AllI18nKeys; renderInfo?: () => ReactNode; @@ -64,8 +65,10 @@ class ConfirmPopup extends Popup { this.props.renderInfo ? this.props.renderInfo() : this.props.infoI18n ? - : - null + : null } } diff --git a/source/Component/LoadFile/LoadFile.scss b/source/Component/LoadFile/LoadFile.scss index a1c769a..06eaaf0 100644 --- a/source/Component/LoadFile/LoadFile.scss +++ b/source/Component/LoadFile/LoadFile.scss @@ -1,10 +1,16 @@ @import "../Theme/Theme.scss"; +div.load-file-app-root { + width: 100%; + height: 100%; +} + div.load-file-layer-root { position: fixed; z-index: 1000; width: 100%; height: 100%; + pointer-events: none; box-sizing: border-box; padding: 20px; @@ -19,7 +25,6 @@ div.load-file-layer-root { border-radius: 3px; div { - pointer-events: none; user-select: none; text-align: center; width: 100%; diff --git a/source/Component/LoadFile/LoadFile.tsx b/source/Component/LoadFile/LoadFile.tsx index 981e304..fdb7ed4 100644 --- a/source/Component/LoadFile/LoadFile.tsx +++ b/source/Component/LoadFile/LoadFile.tsx @@ -1,69 +1,142 @@ +import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import { Localization } from "@Component/Localization/Localization"; import { FontLevel, Theme } from "@Component/Theme/Theme"; +import { Status, useStatus, IMixinStatusProps } from "@Context/Status"; import { Icon } from "@fluentui/react"; -import { Component, ReactNode } from "react"; +import { FunctionComponent } from "react"; +import { useDrop } from 'react-dnd' +import { NativeTypes } from "react-dnd-html5-backend" import "./LoadFile.scss"; -interface ILoadFileState { - show: boolean; -} +const DragFileMask: FunctionComponent = () => { -class LoadFile extends Component<{}, ILoadFileState> { - - public state: Readonly = { - show: false - }; - - private renderMask() { - return -
-
- -
-
- -
-
- -
+ return +
+
+
- ; - } - - private offDragTimer: NodeJS.Timeout | undefined; - - private handleWindowsDragOnFiles = () => { - clearTimeout(this.offDragTimer as number | undefined); - this.setState({ - show: true - }); - } - - private handleWindowsDragOffFiles = () => { - clearTimeout(this.offDragTimer as number | undefined); - this.offDragTimer = setTimeout(() => { - this.setState({ - show: false - }); - }); - } - - public componentDidMount() { - window.addEventListener("dragenter", this.handleWindowsDragOnFiles); - } - - public componentWillUnmount() { - window.removeEventListener("dragenter", this.handleWindowsDragOnFiles); - } - - public render(): ReactNode { - return this.state.show ? this.renderMask() : null; - } +
+ +
+
+ +
+
+
; } +async function fileChecker(status: Status, file?: File) { + + if (!status) return undefined; + + return new Promise((r, j) => { + + // 检查文件存在性 + if (!file) { + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Load.Save.Error.Empty", + titleI18N: "Popup.Load.Save.Title", + yesI18n: "Popup.Load.Save.confirm" + }); + return j(); + } + + // 检测拓展名 + let extendName = (file.name.match(/\.(\w+)$/) ?? [])[1]; + if (extendName !== "ltss") { + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Load.Save.Error.Type", + infoI18nOption: { ext: extendName }, + titleI18N: "Popup.Load.Save.Title", + yesI18n: "Popup.Load.Save.confirm" + }); + return j(); + } + + // 文件读取 + let fileReader = new FileReader(); + fileReader.readAsText(file); + fileReader.onload = () => { + + const loadFunc = () => { + + // 进行转换 + let errorMessage = status.archive.load(status.model, fileReader.result as string, file.name); + if (errorMessage) { + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Load.Save.Error.Parse", + infoI18nOption: { why: errorMessage }, + titleI18N: "Popup.Load.Save.Title", + yesI18n: "Popup.Load.Save.confirm" + }); + j(); + } + + else { + r(undefined); + } + } + + // 如果保存进行提示 + if (!status.archive.isSaved) { + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Load.Save.Overwrite.Info", + titleI18N: "Popup.Load.Save.Title", + yesI18n: "Popup.Load.Save.Overwrite", + noI18n: "Popup.Action.No", + red: "yes", + yes: () => { + loadFunc(); + }, + no: () => { + j(); + } + }); + } + + else { + loadFunc(); + } + } + fileReader.onerror = () => { + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Load.Save.Error.Parse", + infoI18nOption: { why: "Unknown error" }, + titleI18N: "Popup.Load.Save.Title", + yesI18n: "Popup.Load.Save.confirm" + }); + j(); + } + }); +} + +const LoadFileView: FunctionComponent = (props) => { + + const [{ isOver }, drop] = useDrop(() => ({ + accept: NativeTypes.FILE, + drop: (item: { files: File[] }) => { + if (props.status) { + fileChecker(props.status, item.files[0]).catch((e) => undefined); + } + }, + collect: (monitor) => ({ + isOver: monitor.isOver() + }) + })); + + return <> + { + isOver ? : null + } +
+ {props.children} +
+ +} + +const LoadFile = useStatus(LoadFileView); + export { LoadFile }; \ No newline at end of file diff --git a/source/Context/Status.tsx b/source/Context/Status.tsx index 0d702ca..0a7d1d8 100644 --- a/source/Context/Status.tsx +++ b/source/Context/Status.tsx @@ -161,6 +161,11 @@ class Status extends Emitter { this.emit("labelChange"); this.emit("behaviorChange"); + // 清除焦点对象 + this.setBehaviorObject(); + this.setFocusObject(new Set()); + this.setLabelObject(); + // 映射 this.emit("fileLoad"); }); diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index e8e756b..bde9d05 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -65,6 +65,13 @@ const EN_US = { "Popup.Delete.Behavior.Confirm": "Are you sure you want to delete this behavior? The behavior is deleted and cannot be recalled.", "Popup.Restore.Behavior.Confirm": "Are you sure you want to reset all parameters of this behavior? This operation cannot be recalled.", "Popup.Setting.Title": "Preferences setting", + "Popup.Load.Save.Title": "Load save", + "Popup.Load.Save.confirm": "Got it", + "Popup.Load.Save.Overwrite": "Overwrite and continue", + "Popup.Load.Save.Overwrite.Info": "The current workspace will be overwritten after the archive is loaded, and all unsaved progress will be lost. Are you sure you want to continue?", + "Popup.Load.Save.Error.Empty": "File information acquisition error. The file has been lost or moved.", + "Popup.Load.Save.Error.Type": "The file with extension name \"{ext}\" cannot be loaded temporarily", + "Popup.Load.Save.Error.Parse": "Archive parsing error, detailed reason: \n{why}", "Popup.Add.Behavior.Title": "Add behavior", "Popup.Add.Behavior.Action.Add": "Add all select behavior", "Popup.Add.Behavior.Select.Counter": "Selected {count} behavior", diff --git a/source/Localization/ZH-CN.ts b/source/Localization/ZH-CN.ts index 6b24c28..fb9de81 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -65,6 +65,13 @@ const ZH_CN = { "Popup.Delete.Behavior.Confirm": "你确定要删除这个行为吗?行为被删除将无法撤回。", "Popup.Restore.Behavior.Confirm": "你确定要重置此行为的全部参数吗?此操作无法撤回。", "Popup.Setting.Title": "首选项设置", + "Popup.Load.Save.Title": "加载存档", + "Popup.Load.Save.confirm": "我知道了", + "Popup.Load.Save.Overwrite": "覆盖并继续", + "Popup.Load.Save.Overwrite.Info": "存档加载后将覆盖当前工作区,未保存的进度将全部丢失,确定要继续吗?", + "Popup.Load.Save.Error.Empty": "文件信息获取错误,文件已丢失或已被移动", + "Popup.Load.Save.Error.Type": "暂时无法加载拓展名为 \"{ext}\" 的文件", + "Popup.Load.Save.Error.Parse": "存档解析错误,详细原因: \n{why}", "Popup.Add.Behavior.Title": "添加行为", "Popup.Add.Behavior.Action.Add": "添加全部选中行为", "Popup.Add.Behavior.Select.Counter": "已选择 {count} 个行为", diff --git a/source/Model/Archive.ts b/source/Model/Archive.ts index ed044d1..8c54b59 100644 --- a/source/Model/Archive.ts +++ b/source/Model/Archive.ts @@ -100,7 +100,7 @@ class Archive extends Emitter { // 解析为 JSON 对象 const archive: IArchiveObject = JSON.parse(data); - console.log(archive); + // console.log(archive); // 实例化全部对象 const objectPool: CtrlObject[] = []; @@ -254,7 +254,7 @@ class Archive extends Emitter { * 加载文件为模型 * @return Model */ - public load(model: Model, data: string): string | undefined { + public load(model: Model, data: string, name: string, url?: string): string | undefined { try { this.loadArchiveIntoModel(model, data); @@ -262,8 +262,11 @@ class Archive extends Emitter { return e as string; } - this.isSaved = true; this.emit("fileLoad", this); + this.fileName = name; + this.isSaved = true; + this.isNewFile = false; + this.emit("fileSave", this); }; public constructor() { diff --git a/source/Page/SimulatorWeb/SimulatorWeb.tsx b/source/Page/SimulatorWeb/SimulatorWeb.tsx index 53271a4..255b201 100644 --- a/source/Page/SimulatorWeb/SimulatorWeb.tsx +++ b/source/Page/SimulatorWeb/SimulatorWeb.tsx @@ -1,4 +1,6 @@ import { Component, ReactNode } from "react"; +import { DndProvider } from 'react-dnd' +import { HTML5Backend } from 'react-dnd-html5-backend' import { SettingProvider, Setting, Platform } from "@Context/Setting"; import { Theme, BackgroundLevel, FontLevel } from "@Component/Theme/Theme"; import { StatusProvider, Status } from "@Context/Status"; @@ -7,12 +9,10 @@ import { initializeIcons } from '@fluentui/font-icons-mdl2'; import { RootContainer } from "@Component/Container/RootContainer"; import { LayoutDirection } from "@Context/Layout"; import { LoadFile } from "@Component/LoadFile/LoadFile"; -import { AllBehaviors, getBehaviorById } from "@Behavior/Behavior"; import { CommandBar } from "@Component/CommandBar/CommandBar"; import { HeaderBar } from "@Component/HeaderBar/HeaderBar"; import { Popup } from "@Component/Popup/Popup"; import { Entry } from "../Entry/Entry"; -import { Group } from "@Model/Group"; import "./SimulatorWeb.scss"; initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/"); @@ -42,118 +42,6 @@ class SimulatorWeb extends Component { this.status.bindRenderer(classicRender); this.status.setting = this.setting; - const randomPosition = (group: Group) => { - group.individuals.forEach((individual) => { - individual.position[0] = (Math.random() - .5) * 2; - individual.position[1] = (Math.random() - .5) * 2; - individual.position[2] = (Math.random() - .5) * 2; - }) - }; - - // 测试代码 - if (false) { - let group = this.status.newGroup(); - let range = this.status.newRange(); - range.color = [.1, .5, .9]; - group.new(100); - group.color = [.8, .1, .6]; - randomPosition(group); - this.status.model.update(0); - this.status.newLabel().name = "New Label"; - this.status.newLabel().name = "Test Label 01"; - let template = this.status.model.addBehavior(AllBehaviors[0]); - template.name = "Template"; template.color = [150, 20, 220]; - let dynamic = this.status.model.addBehavior(AllBehaviors[1]); - dynamic.name = "Dynamic"; dynamic.color = [250, 200, 80]; - let brownian = this.status.model.addBehavior(AllBehaviors[2]); - brownian.name = "Brownian"; brownian.color = [200, 80, 250]; - let boundary = this.status.model.addBehavior(AllBehaviors[3]); - boundary.name = "Boundary"; boundary.color = [80, 200, 250]; - boundary.parameter.range.picker = this.status.model.allRangeLabel; - group.addBehavior(template); - group.addBehavior(dynamic); - group.addBehavior(brownian); - group.addBehavior(boundary); - } - - // 鱼群模型测试 - if (false) { - let fish1 = this.status.newGroup(); - let fish2 = this.status.newGroup(); - let shark = this.status.newGroup(); - let range = this.status.newRange(); - - range.displayName = "Experimental site"; - range.color = [.8, .1, .6]; - - fish1.new(100); - fish1.displayName = "Fish A"; - fish1.color = [.1, .5, .9]; - randomPosition(fish1); - - fish2.new(50); - fish2.displayName = "Fish B"; - fish2.color = [.3, .2, .9]; - randomPosition(fish2); - - shark.new(3); - shark.displayName = "Shark"; - shark.color = [.8, .2, .3]; - shark.renderParameter.size = 100; - shark.renderParameter.shape = "5"; - randomPosition(shark); - - this.status.model.update(0); - let fishLabel = this.status.newLabel(); - fishLabel.name = "Fish"; - fish1.addLabel(fishLabel); - fish2.addLabel(fishLabel); - - let template = this.status.model.addBehavior(getBehaviorById("Template")); - template.name = "Template"; template.color = [150, 20, 220]; - - let dynamicFish = this.status.model.addBehavior(getBehaviorById("PhysicsDynamics")); - dynamicFish.name = "Dynamic Fish"; dynamicFish.color = [250, 200, 80]; - - let dynamicShark = this.status.model.addBehavior(getBehaviorById("PhysicsDynamics")); - dynamicShark.name = "Dynamic Shark"; dynamicShark.color = [250, 200, 80]; - dynamicShark.parameter.maxAcceleration = 8.5; - dynamicShark.parameter.maxVelocity = 15.8; - dynamicShark.parameter.resistance = 3.6; - - let brownian = this.status.model.addBehavior(getBehaviorById("Brownian")); - brownian.name = "Brownian"; brownian.color = [200, 80, 250]; - - let boundary = this.status.model.addBehavior(getBehaviorById("BoundaryConstraint")); - boundary.name = "Boundary"; boundary.color = [80, 200, 250]; - boundary.parameter.range.picker = this.status.model.allRangeLabel; - - let tracking = this.status.model.addBehavior(getBehaviorById("Tracking")); - tracking.name = "Tracking"; tracking.color = [80, 200, 250]; - tracking.parameter.target.picker = fishLabel; - - let attacking = this.status.model.addBehavior(getBehaviorById("ContactAttacking")); - attacking.name = "Contact Attacking"; attacking.color = [120, 100, 250]; - attacking.parameter.target.picker = fishLabel; - - fish1.addBehavior(dynamicFish); - fish1.addBehavior(brownian); - fish1.addBehavior(boundary); - - fish2.addBehavior(dynamicFish); - fish2.addBehavior(brownian); - fish2.addBehavior(boundary); - - shark.addBehavior(dynamicShark); - shark.addBehavior(boundary); - shark.addBehavior(tracking); - shark.addBehavior(attacking); - - setTimeout(() => { - this.status.model.updateBehaviorParameter(); - }, 200) - } - (window as any).LT = { status: this.status, setting: this.setting @@ -193,7 +81,9 @@ class SimulatorWeb extends Component { public render(): ReactNode { return - {this.renderContent()} + + {this.renderContent()} + } @@ -204,15 +94,16 @@ class SimulatorWeb extends Component { backgroundLevel={BackgroundLevel.Level5} fontLevel={FontLevel.Level3} > - - -
- - -
+ + +
+ + +
+
} }