From a341a4558b4553dfafe361422fc4797ffbca11e1 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Wed, 3 Apr 2019 19:47:26 +0800 Subject: [PATCH] init --- .gitignore | 21 + .travis.yml | 8 + LICENSE | 21 + README.md | 10 + config/env.js | 93 + config/jest/cssTransform.js | 14 + config/jest/fileTransform.js | 30 + config/paths.js | 57 + config/webpack.config.dev.js | 463 + config/webpack.config.prod.js | 714 + config/webpackDevServer.config.js | 105 + package-lock.json | 16277 ++++++++++++++++++++ package.json | 126 + public/explore/result.html | 47 + public/favicon.ico | Bin 0 -> 3870 bytes public/fileShare.html | 60 + public/folderShare.html | 60 + public/home/download.html | 44 + public/home/home.html | 94 + public/index.html | 65 + public/lock.html | 60 + public/manifest.json | 15 + public/markdown.html | 60 + public/member/login.html | 69 + public/member/setting.html | 54 + public/myShare.html | 60 + public/profile.html | 60 + public/profile/profile.html | 79 + public/setting.html | 60 + public/share/share_dir.html | 93 + public/share/share_home.html | 68 + public/share/share_lock.html | 74 + public/share/share_single.html | 93 + public/video.html | 60 + public/viewer/markdown.html | 74 + public/viewer/video.html | 70 + scripts/build.js | 189 + scripts/start.js | 116 + scripts/test.js | 53 + src/App.js | 52 + src/App.test.js | 9 + src/actions/index.js | 193 + src/component/Download.js | 355 + src/component/FileManager/ContextMenu.js | 341 + src/component/FileManager/Explorer.js | 271 + src/component/FileManager/FileIcon.js | 345 + src/component/FileManager/FileManager.js | 30 + src/component/FileManager/Folder.js | 130 + src/component/FileManager/ImgPreview.js | 131 + src/component/FileManager/Modals.js | 720 + src/component/FileManager/Navigator.js | 464 + src/component/FileManager/ObjectIcon.js | 215 + src/component/FileManager/PathSelector.js | 157 + src/component/FileManager/SmallIcon.js | 257 + src/component/FileManager/TableRow.js | 232 + src/component/LockedFile.js | 140 + src/component/Login/EmailActivication.js | 113 + src/component/Login/LoginForm.js | 216 + src/component/Login/RegisterForm.js | 258 + src/component/Login/ResetPwd.js | 200 + src/component/Login/ResetPwdForm.js | 191 + src/component/Login/TwoStep.js | 160 + src/component/MyShare.js | 270 + src/component/Navbar.js | 678 + src/component/Profile.js | 253 + src/component/Search.js | 120 + src/component/SearchBar.js | 180 + src/component/SharedFile.js | 178 + src/component/SideDrawer.js | 98 + src/component/Snackbar.js | 150 + src/component/StorageBar.js | 153 + src/component/Upload/FileList.js | 253 + src/component/Uploader.js | 163 + src/component/UserAvatar.js | 189 + src/component/UserInfo.js | 89 + src/component/UserSetting.js | 775 + src/component/Viewer/markdown.js | 159 + src/component/Viewer/video.js | 43 + src/config.js | 44 + src/index.js | 75 + src/loader/index.js | 131 + src/loader/utils.js | 104 + src/pages/download.app.js | 48 + src/pages/download.js | 66 + src/pages/fileShare.app.js | 48 + src/pages/fileShare.js | 66 + src/pages/folderShare.app.js | 51 + src/pages/folderShare.js | 67 + src/pages/lock.app.js | 48 + src/pages/lock.js | 66 + src/pages/login.app.js | 59 + src/pages/login.js | 66 + src/pages/markdown.app.js | 51 + src/pages/markdown.js | 66 + src/pages/myShare.app.js | 48 + src/pages/myShare.js | 66 + src/pages/profile.app.js | 48 + src/pages/profile.js | 66 + src/pages/search.app.js | 48 + src/pages/search.js | 66 + src/pages/setting.app.js | 48 + src/pages/setting.js | 66 + src/pages/video.app.js | 51 + src/pages/video.js | 66 + src/reducers/explorer.js | 67 + src/reducers/index.js | 350 + src/reducers/navigator.js | 12 + src/reducers/view.js | 38 + src/serviceWorker.js | 131 + src/untils/index.js | 69 + 110 files changed, 30943 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 config/env.js create mode 100644 config/jest/cssTransform.js create mode 100644 config/jest/fileTransform.js create mode 100644 config/paths.js create mode 100644 config/webpack.config.dev.js create mode 100644 config/webpack.config.prod.js create mode 100644 config/webpackDevServer.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/explore/result.html create mode 100644 public/favicon.ico create mode 100644 public/fileShare.html create mode 100644 public/folderShare.html create mode 100644 public/home/download.html create mode 100644 public/home/home.html create mode 100644 public/index.html create mode 100644 public/lock.html create mode 100644 public/manifest.json create mode 100644 public/markdown.html create mode 100644 public/member/login.html create mode 100644 public/member/setting.html create mode 100644 public/myShare.html create mode 100644 public/profile.html create mode 100644 public/profile/profile.html create mode 100644 public/setting.html create mode 100644 public/share/share_dir.html create mode 100644 public/share/share_home.html create mode 100644 public/share/share_lock.html create mode 100644 public/share/share_single.html create mode 100644 public/video.html create mode 100644 public/viewer/markdown.html create mode 100644 public/viewer/video.html create mode 100644 scripts/build.js create mode 100644 scripts/start.js create mode 100644 scripts/test.js create mode 100644 src/App.js create mode 100644 src/App.test.js create mode 100644 src/actions/index.js create mode 100644 src/component/Download.js create mode 100644 src/component/FileManager/ContextMenu.js create mode 100644 src/component/FileManager/Explorer.js create mode 100644 src/component/FileManager/FileIcon.js create mode 100644 src/component/FileManager/FileManager.js create mode 100644 src/component/FileManager/Folder.js create mode 100644 src/component/FileManager/ImgPreview.js create mode 100644 src/component/FileManager/Modals.js create mode 100644 src/component/FileManager/Navigator.js create mode 100644 src/component/FileManager/ObjectIcon.js create mode 100644 src/component/FileManager/PathSelector.js create mode 100644 src/component/FileManager/SmallIcon.js create mode 100644 src/component/FileManager/TableRow.js create mode 100644 src/component/LockedFile.js create mode 100644 src/component/Login/EmailActivication.js create mode 100644 src/component/Login/LoginForm.js create mode 100644 src/component/Login/RegisterForm.js create mode 100644 src/component/Login/ResetPwd.js create mode 100644 src/component/Login/ResetPwdForm.js create mode 100644 src/component/Login/TwoStep.js create mode 100644 src/component/MyShare.js create mode 100644 src/component/Navbar.js create mode 100644 src/component/Profile.js create mode 100644 src/component/Search.js create mode 100644 src/component/SearchBar.js create mode 100644 src/component/SharedFile.js create mode 100644 src/component/SideDrawer.js create mode 100644 src/component/Snackbar.js create mode 100644 src/component/StorageBar.js create mode 100644 src/component/Upload/FileList.js create mode 100644 src/component/Uploader.js create mode 100644 src/component/UserAvatar.js create mode 100644 src/component/UserInfo.js create mode 100644 src/component/UserSetting.js create mode 100644 src/component/Viewer/markdown.js create mode 100644 src/component/Viewer/video.js create mode 100644 src/config.js create mode 100644 src/index.js create mode 100644 src/loader/index.js create mode 100644 src/loader/utils.js create mode 100644 src/pages/download.app.js create mode 100644 src/pages/download.js create mode 100644 src/pages/fileShare.app.js create mode 100644 src/pages/fileShare.js create mode 100644 src/pages/folderShare.app.js create mode 100644 src/pages/folderShare.js create mode 100644 src/pages/lock.app.js create mode 100644 src/pages/lock.js create mode 100644 src/pages/login.app.js create mode 100644 src/pages/login.js create mode 100644 src/pages/markdown.app.js create mode 100644 src/pages/markdown.js create mode 100644 src/pages/myShare.app.js create mode 100644 src/pages/myShare.js create mode 100644 src/pages/profile.app.js create mode 100644 src/pages/profile.js create mode 100644 src/pages/search.app.js create mode 100644 src/pages/search.js create mode 100644 src/pages/setting.app.js create mode 100644 src/pages/setting.js create mode 100644 src/pages/video.app.js create mode 100644 src/pages/video.js create mode 100644 src/reducers/explorer.js create mode 100644 src/reducers/index.js create mode 100644 src/reducers/navigator.js create mode 100644 src/reducers/view.js create mode 100644 src/serviceWorker.js create mode 100644 src/untils/index.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f491785 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3c10708 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: + - '10.13.0' +before_script: npm install +script: npm run-script build +after_success: + - zip -r -q -o pack.zip ./build + - curl -F "token=$TOKEN" -F "commit=$TRAVIS_COMMIT" -F "source=frontend" -F "filename=@pack.zip" -H "Expect:" http://cloudreve.org/deploy.php \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..80102fa --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 AaronLiu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d6171b8 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +cloudreve-frontend [![Build Status](https://travis-ci.org/HFO4/cloudreve-frontend.svg?branch=master)](https://travis-ci.org/HFO4/cloudreve-frontend) +========================= + +Build: + +``` +npm install +npm run-script build +``` + diff --git a/config/env.js b/config/env.js new file mode 100644 index 0000000..b0344c5 --- /dev/null +++ b/config/env.js @@ -0,0 +1,93 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const paths = require('./paths'); + +// Make sure that including paths.js after env.js will read .env variables. +delete require.cache[require.resolve('./paths')]; + +const NODE_ENV = process.env.NODE_ENV; +if (!NODE_ENV) { + throw new Error( + 'The NODE_ENV environment variable is required but was not specified.' + ); +} + +// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use +var dotenvFiles = [ + `${paths.dotenv}.${NODE_ENV}.local`, + `${paths.dotenv}.${NODE_ENV}`, + // Don't include `.env.local` for `test` environment + // since normally you expect tests to produce the same + // results for everyone + NODE_ENV !== 'test' && `${paths.dotenv}.local`, + paths.dotenv, +].filter(Boolean); + +// Load environment variables from .env* files. Suppress warnings using silent +// if this file is missing. dotenv will never modify any environment variables +// that have already been set. Variable expansion is supported in .env files. +// https://github.com/motdotla/dotenv +// https://github.com/motdotla/dotenv-expand +dotenvFiles.forEach(dotenvFile => { + if (fs.existsSync(dotenvFile)) { + require('dotenv-expand')( + require('dotenv').config({ + path: dotenvFile, + }) + ); + } +}); + +// We support resolving modules according to `NODE_PATH`. +// This lets you use absolute paths in imports inside large monorepos: +// https://github.com/facebook/create-react-app/issues/253. +// It works similar to `NODE_PATH` in Node itself: +// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders +// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. +// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. +// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 +// We also resolve them to make sure all tools using them work consistently. +const appDirectory = fs.realpathSync(process.cwd()); +process.env.NODE_PATH = (process.env.NODE_PATH || '') + .split(path.delimiter) + .filter(folder => folder && !path.isAbsolute(folder)) + .map(folder => path.resolve(appDirectory, folder)) + .join(path.delimiter); + +// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be +// injected into the application via DefinePlugin in Webpack configuration. +const REACT_APP = /^REACT_APP_/i; + +function getClientEnvironment(publicUrl) { + const raw = Object.keys(process.env) + .filter(key => REACT_APP.test(key)) + .reduce( + (env, key) => { + env[key] = process.env[key]; + return env; + }, + { + // Useful for determining whether we’re running in production mode. + // Most importantly, it switches React into the correct mode. + NODE_ENV: process.env.NODE_ENV || 'development', + // Useful for resolving the correct path to static assets in `public`. + // For example, . + // This should only be used as an escape hatch. Normally you would put + // images into the `src` and `import` them in code to get their paths. + PUBLIC_URL: publicUrl, + } + ); + // Stringify all values so we can feed into Webpack DefinePlugin + const stringified = { + 'process.env': Object.keys(raw).reduce((env, key) => { + env[key] = JSON.stringify(raw[key]); + return env; + }, {}), + }; + + return { raw, stringified }; +} + +module.exports = getClientEnvironment; diff --git a/config/jest/cssTransform.js b/config/jest/cssTransform.js new file mode 100644 index 0000000..8f65114 --- /dev/null +++ b/config/jest/cssTransform.js @@ -0,0 +1,14 @@ +'use strict'; + +// This is a custom Jest transformer turning style imports into empty objects. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process() { + return 'module.exports = {};'; + }, + getCacheKey() { + // The output is always the same. + return 'cssTransform'; + }, +}; diff --git a/config/jest/fileTransform.js b/config/jest/fileTransform.js new file mode 100644 index 0000000..07010e3 --- /dev/null +++ b/config/jest/fileTransform.js @@ -0,0 +1,30 @@ +'use strict'; + +const path = require('path'); + +// This is a custom Jest transformer turning file imports into filenames. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process(src, filename) { + const assetFilename = JSON.stringify(path.basename(filename)); + + if (filename.match(/\.svg$/)) { + return `module.exports = { + __esModule: true, + default: ${assetFilename}, + ReactComponent: (props) => ({ + $$typeof: Symbol.for('react.element'), + type: 'svg', + ref: null, + key: null, + props: Object.assign({}, props, { + children: ${assetFilename} + }) + }), + };`; + } + + return `module.exports = ${assetFilename};`; + }, +}; diff --git a/config/paths.js b/config/paths.js new file mode 100644 index 0000000..bb13e80 --- /dev/null +++ b/config/paths.js @@ -0,0 +1,57 @@ +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const url = require('url'); + +// Make sure any symlinks in the project folder are resolved: +// https://github.com/facebook/create-react-app/issues/637 +const appDirectory = fs.realpathSync(process.cwd()); +const resolveApp = relativePath => path.resolve(appDirectory, relativePath); + +const envPublicUrl = process.env.PUBLIC_URL; + +function ensureSlash(inputPath, needsSlash) { + const hasSlash = inputPath.endsWith('/'); + if (hasSlash && !needsSlash) { + return inputPath.substr(0, inputPath.length - 1); + } else if (!hasSlash && needsSlash) { + return `${inputPath}/`; + } else { + return inputPath; + } +} + +const getPublicUrl = appPackageJson => + envPublicUrl || require(appPackageJson).homepage; + +// We use `PUBLIC_URL` environment variable or "homepage" field to infer +// "public path" at which the app is served. +// Webpack needs to know it to put the right + + + + + +
+ + + \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 zcma);c{J4h9>;%nil|2-o+rCuEF-(I%-F}ijC~o(k~HKAkr0)!FCj~d>`RtpD?8b; zXOC1OD!V*IsqUwzbMF1)-gEDD=A573Z-&G7^LoAC9|WO7Xc0Cx1g^Zu0u_SjAPB3vGa^W|sj)80f#V0@M_CAZTIO(t--xg= z!sii`1giyH7EKL_+Wi0ab<)&E_0KD!3Rp2^HNB*K2@PHCs4PWSA32*-^7d{9nH2_E zmC{C*N*)(vEF1_aMamw2A{ZH5aIDqiabnFdJ|y0%aS|64E$`s2ccV~3lR!u<){eS` z#^Mx6o(iP1Ix%4dv`t@!&Za-K@mTm#vadc{0aWDV*_%EiGK7qMC_(`exc>-$Gb9~W!w_^{*pYRm~G zBN{nA;cm^w$VWg1O^^<6vY`1XCD|s_zv*g*5&V#wv&s#h$xlUilPe4U@I&UXZbL z0)%9Uj&@yd03n;!7do+bfixH^FeZ-Ema}s;DQX2gY+7g0s(9;`8GyvPY1*vxiF&|w z>!vA~GA<~JUqH}d;DfBSi^IT*#lrzXl$fNpq0_T1tA+`A$1?(gLb?e#0>UELvljtQ zK+*74m0jn&)5yk8mLBv;=@}c{t0ztT<v;Avck$S6D`Z)^c0(jiwKhQsn|LDRY&w(Fmi91I7H6S;b0XM{e zXp0~(T@k_r-!jkLwd1_Vre^v$G4|kh4}=Gi?$AaJ)3I+^m|Zyj#*?Kp@w(lQdJZf4 z#|IJW5z+S^e9@(6hW6N~{pj8|NO*>1)E=%?nNUAkmv~OY&ZV;m-%?pQ_11)hAr0oAwILrlsGawpxx4D43J&K=n+p3WLnlDsQ$b(9+4 z?mO^hmV^F8MV{4Lx>(Q=aHhQ1){0d*(e&s%G=i5rq3;t{JC zmgbn5Nkl)t@fPH$v;af26lyhH!k+#}_&aBK4baYPbZy$5aFx4}ka&qxl z$=Rh$W;U)>-=S-0=?7FH9dUAd2(q#4TCAHky!$^~;Dz^j|8_wuKc*YzfdAht@Q&ror?91Dm!N03=4=O!a)I*0q~p0g$Fm$pmr$ zb;wD;STDIi$@M%y1>p&_>%?UP($15gou_ue1u0!4(%81;qcIW8NyxFEvXpiJ|H4wz z*mFT(qVx1FKufG11hByuX%lPk4t#WZ{>8ka2efjY`~;AL6vWyQKpJun2nRiZYDij$ zP>4jQXPaP$UC$yIVgGa)jDV;F0l^n(V=HMRB5)20V7&r$jmk{UUIe zVjKroK}JAbD>B`2cwNQ&GDLx8{pg`7hbA~grk|W6LgiZ`8y`{Iq0i>t!3p2}MS6S+ zO_ruKyAElt)rdS>CtF7j{&6rP-#c=7evGMt7B6`7HG|-(WL`bDUAjyn+k$mx$CH;q2Dz4x;cPP$hW=`pFfLO)!jaCL@V2+F)So3}vg|%O*^T1j>C2lx zsURO-zIJC$^$g2byVbRIo^w>UxK}74^TqUiRR#7s_X$e)$6iYG1(PcW7un-va-S&u zHk9-6Zn&>T==A)lM^D~bk{&rFzCi35>UR!ZjQkdSiNX*-;l4z9j*7|q`TBl~Au`5& z+c)*8?#-tgUR$Zd%Q3bs96w6k7q@#tUn`5rj+r@_sAVVLqco|6O{ILX&U-&-cbVa3 zY?ngHR@%l{;`ri%H*0EhBWrGjv!LE4db?HEWb5mu*t@{kv|XwK8?npOshmzf=vZA@ zVSN9sL~!sn?r(AK)Q7Jk2(|M67Uy3I{eRy z_l&Y@A>;vjkWN5I2xvFFTLX0i+`{qz7C_@bo`ZUzDugfq4+>a3?1v%)O+YTd6@Ul7 zAfLfm=nhZ`)P~&v90$&UcF+yXm9sq!qCx3^9gzIcO|Y(js^Fj)Rvq>nQAHI92ap=P z10A4@prk+AGWCb`2)dQYFuR$|H6iDE8p}9a?#nV2}LBCoCf(Xi2@szia7#gY>b|l!-U`c}@ zLdhvQjc!BdLJvYvzzzngnw51yRYCqh4}$oRCy-z|v3Hc*d|?^Wj=l~18*E~*cR_kU z{XsxM1i{V*4GujHQ3DBpl2w4FgFR48Nma@HPgnyKoIEY-MqmMeY=I<%oG~l!f<+FN z1ZY^;10j4M4#HYXP zw5eJpA_y(>uLQ~OucgxDLuf}fVs272FaMxhn4xnDGIyLXnw>Xsd^J8XhcWIwIoQ9} z%FoSJTAGW(SRGwJwb=@pY7r$uQRK3Zd~XbxU)ts!4XsJrCycrWSI?e!IqwqIR8+Jh zlRjZ`UO1I!BtJR_2~7AbkbSm%XQqxEPkz6BTGWx8e}nQ=w7bZ|eVP4?*Tb!$(R)iC z9)&%bS*u(lXqzitAN)Oo=&Ytn>%Hzjc<5liuPi>zC_nw;Z0AE3Y$Jao_Q90R-gl~5 z_xAb2J%eArrC1CN4G$}-zVvCqF1;H;abAu6G*+PDHSYFx@Tdbfox*uEd3}BUyYY-l zTfEsOqsi#f9^FoLO;ChK<554qkri&Av~SIM*{fEYRE?vH7pTAOmu2pz3X?Wn*!ROX ztd54huAk&mFBemMooL33RV-*1f0Q3_(7hl$<#*|WF9P!;r;4_+X~k~uKEqdzZ$5Al zV63XN@)j$FN#cCD;ek1R#l zv%pGrhB~KWgoCj%GT?%{@@o(AJGt*PG#l3i>lhmb_twKH^EYvacVY-6bsCl5*^~L0 zonm@lk2UvvTKr2RS%}T>^~EYqdL1q4nD%0n&Xqr^cK^`J5W;lRRB^R-O8b&HENO||mo0xaD+S=I8RTlIfVgqN@SXDr2&-)we--K7w= zJVU8?Z+7k9dy;s;^gDkQa`0nz6N{T?(A&Iz)2!DEecLyRa&FI!id#5Z7B*O2=PsR0 zEvc|8{NS^)!d)MDX(97Xw}m&kEO@5jqRaDZ!+%`wYOI<23q|&js`&o4xvjP7D_xv@ z5hEwpsp{HezI9!~6O{~)lLR@oF7?J7i>1|5a~UuoN=q&6N}EJPV_GD`&M*v8Y`^2j zKII*d_@Fi$+i*YEW+Hbzn{iQk~yP z>7N{S4)r*!NwQ`(qcN#8SRQsNK6>{)X12nbF`*7#ecO7I)Q$uZsV+xS4E7aUn+U(K baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/public/fileShare.html b/public/fileShare.html new file mode 100644 index 0000000..6db3386 --- /dev/null +++ b/public/fileShare.html @@ -0,0 +1,60 @@ + + + + + + + + + + React App + + + + + + +
+ + + + diff --git a/public/folderShare.html b/public/folderShare.html new file mode 100644 index 0000000..6db3386 --- /dev/null +++ b/public/folderShare.html @@ -0,0 +1,60 @@ + + + + + + + + + + React App + + + + + + +
+ + + + diff --git a/public/home/download.html b/public/home/download.html new file mode 100644 index 0000000..6b745d7 --- /dev/null +++ b/public/home/download.html @@ -0,0 +1,44 @@ + + + + + + + + + + 离线下载管理- {$options.siteName} + + + + + + +
+ + \ No newline at end of file diff --git a/public/home/home.html b/public/home/home.html new file mode 100644 index 0000000..6f19cd4 --- /dev/null +++ b/public/home/home.html @@ -0,0 +1,94 @@ + + + + + + + + + + + + 我的文件 - {$options.siteName} + + + + + + +
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..52c9e4b --- /dev/null +++ b/public/index.html @@ -0,0 +1,65 @@ + + + + + + + + + + 我的文件 - {$options.siteName} + + + + + +
+ + + \ No newline at end of file diff --git a/public/lock.html b/public/lock.html new file mode 100644 index 0000000..6db3386 --- /dev/null +++ b/public/lock.html @@ -0,0 +1,60 @@ + + + + + + + + + + React App + + + + + + +
+ + + + diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..1f2f141 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/public/markdown.html b/public/markdown.html new file mode 100644 index 0000000..6db3386 --- /dev/null +++ b/public/markdown.html @@ -0,0 +1,60 @@ + + + + + + + + + + React App + + + + + + +
+ + + + diff --git a/public/member/login.html b/public/member/login.html new file mode 100644 index 0000000..258af4e --- /dev/null +++ b/public/member/login.html @@ -0,0 +1,69 @@ + + + + + + + + + + + + {eq name="pageId" value="resetPwdForm"}找回密码{/eq}{eq name="pageId" value="resetPwd"}找回密码{/eq}{eq name="pageId" value="emailActivate"}激活成功{/eq}{eq name="pageId" value="login"}登录{/eq}{eq name="pageId" value="register"}注册{/eq}{eq name="pageId" value="TwoStep"}二步验证{/eq} - {$options.siteName} + + + + + + +
+ + + + \ No newline at end of file diff --git a/public/member/setting.html b/public/member/setting.html new file mode 100644 index 0000000..9a5df13 --- /dev/null +++ b/public/member/setting.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + 用户设置- {$options.siteName} + + + + + + +
+ + + + \ No newline at end of file diff --git a/public/myShare.html b/public/myShare.html new file mode 100644 index 0000000..6db3386 --- /dev/null +++ b/public/myShare.html @@ -0,0 +1,60 @@ + + + + + + + + + + React App + + + + + + +
+ + + + diff --git a/public/profile.html b/public/profile.html new file mode 100644 index 0000000..6db3386 --- /dev/null +++ b/public/profile.html @@ -0,0 +1,60 @@ + + + + + + + + + + React App + + + + + + +
+ + + + diff --git a/public/profile/profile.html b/public/profile/profile.html new file mode 100644 index 0000000..f33db95 --- /dev/null +++ b/public/profile/profile.html @@ -0,0 +1,79 @@ + + + + + + + + + + + + 用户主页 - {$options.siteName} + + + + + + +
+ + + + \ No newline at end of file diff --git a/public/setting.html b/public/setting.html new file mode 100644 index 0000000..6db3386 --- /dev/null +++ b/public/setting.html @@ -0,0 +1,60 @@ + + + + + + + + + + React App + + + + + + +
+ + + + diff --git a/public/share/share_dir.html b/public/share/share_dir.html new file mode 100644 index 0000000..6f71723 --- /dev/null +++ b/public/share/share_dir.html @@ -0,0 +1,93 @@ + + + + + + + + + + + + {$dirData.folder_name} - {$options.siteName} + + + + + + +
+ + + + \ No newline at end of file diff --git a/public/share/share_home.html b/public/share/share_home.html new file mode 100644 index 0000000..531bdba --- /dev/null +++ b/public/share/share_home.html @@ -0,0 +1,68 @@ + + + + + + + + + + + + 我的分享- {$options.siteName} + + + + + + +
+ + + + \ No newline at end of file diff --git a/public/share/share_lock.html b/public/share/share_lock.html new file mode 100644 index 0000000..cbb143a --- /dev/null +++ b/public/share/share_lock.html @@ -0,0 +1,74 @@ + + + + + + + + + + + + 私密分享 - {$options.siteName} + + + + + + +
+ + + + \ No newline at end of file diff --git a/public/share/share_single.html b/public/share/share_single.html new file mode 100644 index 0000000..2eec9d0 --- /dev/null +++ b/public/share/share_single.html @@ -0,0 +1,93 @@ + + + + + + + + + + + + {$fileData.orign_name} - {$options.siteName} + + + + + + +
+ + + + \ No newline at end of file diff --git a/public/video.html b/public/video.html new file mode 100644 index 0000000..6db3386 --- /dev/null +++ b/public/video.html @@ -0,0 +1,60 @@ + + + + + + + + + + React App + + + + + + +
+ + + + diff --git a/public/viewer/markdown.html b/public/viewer/markdown.html new file mode 100644 index 0000000..1552146 --- /dev/null +++ b/public/viewer/markdown.html @@ -0,0 +1,74 @@ + + + + + + + + + + + + {$fileName} - {$options.siteName} + + + + + +
+ + + + + diff --git a/public/viewer/video.html b/public/viewer/video.html new file mode 100644 index 0000000..3d7546d --- /dev/null +++ b/public/viewer/video.html @@ -0,0 +1,70 @@ + + + + + + + + + + + {$fileName} - {$options.siteName} + + + + + +
+ + + diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..f800b3c --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,189 @@ +'use strict'; + +// Do this as the first thing so that any code reading it knows the right env. +process.env.BABEL_ENV = 'production'; +process.env.NODE_ENV = 'production'; + +// Makes the script crash on unhandled rejections instead of silently +// ignoring them. In the future, promise rejections that are not handled will +// terminate the Node.js process with a non-zero exit code. +process.on('unhandledRejection', err => { + throw err; +}); + +// Ensure environment variables are read. +require('../config/env'); + + +const path = require('path'); +const chalk = require('chalk'); +const fs = require('fs-extra'); +const webpack = require('webpack'); +const bfj = require('bfj'); +const config = require('../config/webpack.config.prod'); +const paths = require('../config/paths'); +const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); +const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); +const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); +const printBuildError = require('react-dev-utils/printBuildError'); + +const measureFileSizesBeforeBuild = + FileSizeReporter.measureFileSizesBeforeBuild; +const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; +const useYarn = fs.existsSync(paths.yarnLockFile); + +// These sizes are pretty large. We'll warn for bundles exceeding them. +const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; +const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; + +const isInteractive = process.stdout.isTTY; + +// Warn and crash if required files are missing +if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { + process.exit(1); +} + +// Process CLI arguments +const argv = process.argv.slice(2); +const writeStatsJson = argv.indexOf('--stats') !== -1; + +// We require that you explictly set browsers and do not fall back to +// browserslist defaults. +const { checkBrowsers } = require('react-dev-utils/browsersHelper'); +checkBrowsers(paths.appPath, isInteractive) + .then(() => { + // First, read the current file sizes in build directory. + // This lets us display how much they changed later. + return measureFileSizesBeforeBuild(paths.appBuild); + }) + .then(previousFileSizes => { + // Remove all content but keep the directory so that + // if you're in it, you don't end up in Trash + fs.emptyDirSync(paths.appBuild); + // Merge with the public folder + copyPublicFolder(); + // Start the webpack build + return build(previousFileSizes); + }) + .then( + ({ stats, previousFileSizes, warnings }) => { + if (warnings.length) { + console.log(chalk.yellow('Compiled with warnings.\n')); + console.log(warnings.join('\n\n')); + console.log( + '\nSearch for the ' + + chalk.underline(chalk.yellow('keywords')) + + ' to learn more about each warning.' + ); + console.log( + 'To ignore, add ' + + chalk.cyan('// eslint-disable-next-line') + + ' to the line before.\n' + ); + } else { + console.log(chalk.green('Compiled successfully.\n')); + } + + console.log('File sizes after gzip:\n'); + printFileSizesAfterBuild( + stats, + previousFileSizes, + paths.appBuild, + WARN_AFTER_BUNDLE_GZIP_SIZE, + WARN_AFTER_CHUNK_GZIP_SIZE + ); + console.log(); + + const appPackage = require(paths.appPackageJson); + const publicUrl = paths.publicUrl; + const publicPath = config.output.publicPath; + const buildFolder = path.relative(process.cwd(), paths.appBuild); + printHostingInstructions( + appPackage, + publicUrl, + publicPath, + buildFolder, + useYarn + ); + }, + err => { + console.log(chalk.red('Failed to compile.\n')); + printBuildError(err); + process.exit(1); + } + ) + .catch(err => { + if (err && err.message) { + console.log(err.message); + } + process.exit(1); + }); + +// Create the production build and print the deployment instructions. +function build(previousFileSizes) { + console.log('Creating an optimized production build...'); + + let compiler = webpack(config); + return new Promise((resolve, reject) => { + compiler.run((err, stats) => { + let messages; + if (err) { + if (!err.message) { + return reject(err); + } + messages = formatWebpackMessages({ + errors: [err.message], + warnings: [], + }); + } else { + messages = formatWebpackMessages( + stats.toJson({ all: false, warnings: true, errors: true }) + ); + } + if (messages.errors.length) { + // Only keep the first error. Others are often indicative + // of the same problem, but confuse the reader with noise. + if (messages.errors.length > 1) { + messages.errors.length = 1; + } + return reject(new Error(messages.errors.join('\n\n'))); + } + if ( + process.env.CI && + (typeof process.env.CI !== 'string' || + process.env.CI.toLowerCase() !== 'false') && + messages.warnings.length + ) { + console.log( + chalk.yellow( + '\nTreating warnings as errors because process.env.CI = true.\n' + + 'Most CI servers set it automatically.\n' + ) + ); + return reject(new Error(messages.warnings.join('\n\n'))); + } + + const resolveArgs = { + stats, + previousFileSizes, + warnings: messages.warnings, + }; + if (writeStatsJson) { + return bfj + .write(paths.appBuild + '/bundle-stats.json', stats.toJson()) + .then(() => resolve(resolveArgs)) + .catch(error => reject(new Error(error))); + } + + return resolve(resolveArgs); + }); + }); +} + +function copyPublicFolder() { + fs.copySync(paths.appPublic, paths.appBuild, { + dereference: true, + filter: file => file !== paths.appHtml, + }); +} diff --git a/scripts/start.js b/scripts/start.js new file mode 100644 index 0000000..71ed604 --- /dev/null +++ b/scripts/start.js @@ -0,0 +1,116 @@ +'use strict'; + +// Do this as the first thing so that any code reading it knows the right env. +process.env.BABEL_ENV = 'development'; +process.env.NODE_ENV = 'development'; + +// Makes the script crash on unhandled rejections instead of silently +// ignoring them. In the future, promise rejections that are not handled will +// terminate the Node.js process with a non-zero exit code. +process.on('unhandledRejection', err => { + throw err; +}); + +// Ensure environment variables are read. +require('../config/env'); + + +const fs = require('fs'); +const chalk = require('chalk'); +const webpack = require('webpack'); +const WebpackDevServer = require('webpack-dev-server'); +const clearConsole = require('react-dev-utils/clearConsole'); +const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +const { + choosePort, + createCompiler, + prepareProxy, + prepareUrls, +} = require('react-dev-utils/WebpackDevServerUtils'); +const openBrowser = require('react-dev-utils/openBrowser'); +const paths = require('../config/paths'); +const config = require('../config/webpack.config.dev'); +const createDevServerConfig = require('../config/webpackDevServer.config'); + +const useYarn = fs.existsSync(paths.yarnLockFile); +const isInteractive = process.stdout.isTTY; + +// Warn and crash if required files are missing +if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { + process.exit(1); +} + +// Tools like Cloud9 rely on this. +const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; +const HOST = process.env.HOST || '0.0.0.0'; + +if (process.env.HOST) { + console.log( + chalk.cyan( + `Attempting to bind to HOST environment variable: ${chalk.yellow( + chalk.bold(process.env.HOST) + )}` + ) + ); + console.log( + `If this was unintentional, check that you haven't mistakenly set it in your shell.` + ); + console.log( + `Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}` + ); + console.log(); +} + +// We require that you explictly set browsers and do not fall back to +// browserslist defaults. +const { checkBrowsers } = require('react-dev-utils/browsersHelper'); +checkBrowsers(paths.appPath, isInteractive) + .then(() => { + // We attempt to use the default port but if it is busy, we offer the user to + // run on a different port. `choosePort()` Promise resolves to the next free port. + return choosePort(HOST, DEFAULT_PORT); + }) + .then(port => { + if (port == null) { + // We have not found a port. + return; + } + const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; + const appName = require(paths.appPackageJson).name; + const urls = prepareUrls(protocol, HOST, port); + // Create a webpack compiler that is configured with custom messages. + const compiler = createCompiler(webpack, config, appName, urls, useYarn); + // Load proxy config + const proxySetting = require(paths.appPackageJson).proxy; + const proxyConfig = prepareProxy(proxySetting, paths.appPublic); + // Serve webpack assets generated by the compiler over a web server. + const serverConfig = createDevServerConfig( + proxyConfig, + urls.lanUrlForConfig + ); + const devServer = new WebpackDevServer(compiler, serverConfig); + // Launch WebpackDevServer. + devServer.listen(port, HOST, err => { + if (err) { + return console.log(err); + } + if (isInteractive) { + clearConsole(); + } + console.log(chalk.cyan('Starting the development server...\n')); + openBrowser(urls.localUrlForBrowser); + }); + + ['SIGINT', 'SIGTERM'].forEach(function(sig) { + process.on(sig, function() { + devServer.close(); + process.exit(); + }); + }); + }) + .catch(err => { + if (err && err.message) { + console.log(err.message); + } + process.exit(1); + }); diff --git a/scripts/test.js b/scripts/test.js new file mode 100644 index 0000000..0b6e8cb --- /dev/null +++ b/scripts/test.js @@ -0,0 +1,53 @@ +'use strict'; + +// Do this as the first thing so that any code reading it knows the right env. +process.env.BABEL_ENV = 'test'; +process.env.NODE_ENV = 'test'; +process.env.PUBLIC_URL = ''; + +// Makes the script crash on unhandled rejections instead of silently +// ignoring them. In the future, promise rejections that are not handled will +// terminate the Node.js process with a non-zero exit code. +process.on('unhandledRejection', err => { + throw err; +}); + +// Ensure environment variables are read. +require('../config/env'); + + +const jest = require('jest'); +const execSync = require('child_process').execSync; +let argv = process.argv.slice(2); + +function isInGitRepository() { + try { + execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); + return true; + } catch (e) { + return false; + } +} + +function isInMercurialRepository() { + try { + execSync('hg --cwd . root', { stdio: 'ignore' }); + return true; + } catch (e) { + return false; + } +} + +// Watch unless on CI, in coverage mode, or explicitly running all tests +if ( + !process.env.CI && + argv.indexOf('--coverage') === -1 && + argv.indexOf('--watchAll') === -1 +) { + // https://github.com/facebook/create-react-app/issues/5210 + const hasSourceControl = isInGitRepository() || isInMercurialRepository(); + argv.push(hasSourceControl ? '--watch' : '--watchAll'); +} + + +jest.run(argv); diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..28e5c07 --- /dev/null +++ b/src/App.js @@ -0,0 +1,52 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; + +import { withStyles } from '@material-ui/core/styles'; + +import Navbar from "./component/Navbar.js" +import FileManager from "./component/FileManager/FileManager.js" +import AlertBar from "./component/Snackbar" + +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; + +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class App extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +App.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(App); diff --git a/src/App.test.js b/src/App.test.js new file mode 100644 index 0000000..a754b20 --- /dev/null +++ b/src/App.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/actions/index.js b/src/actions/index.js new file mode 100644 index 0000000..44d2dae --- /dev/null +++ b/src/actions/index.js @@ -0,0 +1,193 @@ +export const navitateTo = path => { + return { + type: 'NAVIGATOR_TO', + path:path, + } +} + +export const navitateUp = () => { + return { + type: 'NAVIGATOR_UP', + } +} + +export const drawerToggleAction = open => { + return { + type: 'DRAWER_TOGGLE', + open:open, + } +} + +export const changeViewMethod = method => { + return { + type: 'CHANGE_VIEW_METHOD', + method:method, + } +} + +export const changeSortMethod = method => { + return { + type: 'CHANGE_SORT_METHOD', + method:method, + } +} + +export const updateFileList = list => { + return { + type: 'UPDATE_FILE_LIST', + list:list, + } +} + +export const changeContextMenu = (type,open) => { + return { + type: 'CHANGE_CONTEXT_MENU', + menuType:type, + open:open, + } +} + +export const addSelectedTarget = targets => { + return { + type: 'ADD_SELECTED_TARGET', + targets:targets, + } +} + +export const setSelectedTarget = targets => { + return { + type: 'SET_SELECTED_TARGET', + targets:targets, + } +} + +export const removeSelectedTarget = id => { + return { + type: 'RMOVE_SELECTED_TARGET', + id:id, + } +} + +export const setNavigatorLoadingStatus = status => { + return { + type: 'SET_NAVIGATOR_LOADING_STATUE', + status:status, + } +} + +export const setNavigatorError = (status,msg) => { + return { + type: 'SET_NAVIGATOR_ERROR', + status:status, + msg:msg, + } +} + +export const openCreateFolderDialog = () => { + return { + type: 'OPEN_CREATE_FOLDER_DIALOG', + } +} + +export const openRenameDialog = () => { + return { + type: 'OPEN_RENAME_DIALOG', + } +} + +export const openMoveDialog = () => { + return { + type: 'OPEN_MOVE_DIALOG', + } +} + +export const openRemoveDialog = () => { + return { + type: 'OPEN_REMOVE_DIALOG', + } +} + +export const openShareDialog = () => { + return { + type: 'OPEN_SHARE_DIALOG', + } +} + +export const openMusicDialog = () => { + return { + type: 'OPEN_MUSIC_DIALOG', + } +} + +export const openRemoteDownloadDialog = ()=>{ + return { + type:'OPEN_REMOTE_DOWNLOAD_DIALOG', + } +} + +export const openTorrentDownloadDialog = ()=>{ + return { + type:'OPEN_TORRENT_DOWNLOAD_DIALOG', + } +} + +export const openGetSourceDialog = ()=>{ + return { + type:'OPEN_GET_SOURCE_DIALOG', + } +} + +export const closeAllModals = () => { + return { + type: 'CLOSE_ALL_MODALS', + } +} + +export const toggleSnackbar = (vertical,horizontal,msg,color) => { + return { + type: 'TOGGLE_SNACKBAR', + vertical:vertical, + horizontal:horizontal, + msg:msg, + color:color, + } +} + +export const setModalsLoading = (status) => { + return { + type: 'SET_MODALS_LOADING', + status:status, + } +} + +export const refreshFileList = () => { + return { + type: 'REFRESH_FILE_LIST', + } +} + +export const searchMyFile = (keywords) => { + return { + type: 'SEARCH_MY_FILE', + keywords: keywords, + } +} + +export const showImgPreivew = (first) => { + return { + type: 'SHOW_IMG_PREIVEW', + first: first, + } +} + +export const refreshStorage = () => { + return { + type: 'REFRESH_STORAGE', + } +} + +export const saveFile = () => { + return { + type: 'SAVE_FILE', + } +} \ No newline at end of file diff --git a/src/component/Download.js b/src/component/Download.js new file mode 100644 index 0000000..eb59b33 --- /dev/null +++ b/src/component/Download.js @@ -0,0 +1,355 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Card from '@material-ui/core/Card'; +import LinearProgress from '@material-ui/core/LinearProgress'; +import ImageIcon from '@material-ui/icons/PhotoSizeSelectActual' +import VideoIcon from '@material-ui/icons/Videocam' +import AudioIcon from '@material-ui/icons/Audiotrack' +import PdfIcon from "@material-ui/icons/PictureAsPdf" +import RefreshIcon from "@material-ui/icons/Refresh" +import DeleteIcon from "@material-ui/icons/Delete" +import FileShowIcon from "@material-ui/icons/InsertDriveFile" +import {FileWordBox,FilePowerpointBox,FileExcelBox,ScriptText,MagnetOn,ZipBox,WindowRestore,Android} from 'mdi-material-ui' +import CardContent from '@material-ui/core/CardContent'; +import { toggleSnackbar,}from "../actions/index" +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import axios from 'axios' +import IconButton from '@material-ui/core/IconButton'; +import {sizeToString} from '../untils/index' +import {mediaType} from "../config" + +const styles = theme => ({ + card: { + marginTop:"20px", + display: "flex", + justifyContent: "space-between", + }, + actions: { + display: 'flex', + }, + title:{ + marginTop:"20px", + }, + layout: { + width: 'auto', + marginTop:'30px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 700, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + shareTitle:{ + maxWidth:"200px", + }, + avatarFile:{ + backgroundColor: theme.palette.primary.light, + }, + avatarFolder:{ + backgroundColor: theme.palette.secondary.light, + }, + gird:{ + marginTop:"30px", + }, + iconContainer:{ + width:"90px", + height:"90px", + padding: "29px", + marginTop: "6px", + paddingLeft: "35px", + [theme.breakpoints.down("md")]: { + display:"none" + } + }, + content:{ + width:"100%", + minWidth: 0, + }, + contentSide:{ + minWidth: 0, + paddingTop: "24px", + paddingRight: "28px", + [theme.breakpoints.down("md")]: { + display:"none" + } + }, + iconImgBig:{ + color:"#d32f2f", + fontSize: "30px", + }, + iconVideoBig:{ + color:"#d50000", + fontSize: "30px", + }, + iconAudioBig:{ + color:"#651fff", + fontSize: "30px", + }, + iconPdfBig:{ + color:"#f44336", + fontSize: "30px", + }, + iconWordBig:{ + color:"#538ce5", + fontSize: "30px", + }, + iconPptBig:{ + color:"rgb(239, 99, 63)", + fontSize: "30px", + }, + iconExcelBig:{ + color:"#4caf50", + fontSize: "30px", + }, + iconTextBig:{ + color:"#607d8b", + fontSize: "30px", + }, + iconFileBig:{ + color:"#424242", + fontSize: "30px", + }, + iconTorrentBig:{ + color:"#5c6bc0", + fontSize: "30px", + }, + iconZipBig:{ + color:"#f9a825", + fontSize: "30px", + }, + iconAndroidBig:{ + color:"#8bc34a", + fontSize: "30px", + }, + iconExeBig:{ + color:"#1a237e", + fontSize: "30px", + }, + hide:{ + display:"none", + }, + loadingAnimation:{ + borderRadius: "6px 6px 0 0", + }, + shareFix:{ + marginLeft: "20px", + }, + loadMore:{ + textAlign:"center", + marginTop:"20px", + marginBottom:"20px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +const getIcon = (classes,name)=>{ + let iconBig; + let fileType =name.split(".").pop().toLowerCase(); + if (mediaType["image"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["video"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["audio"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["pdf"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["word"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["ppt"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["excel"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["text"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["torrent"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["zip"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["excute"].indexOf(fileType)!==-1){ + iconBig = (); + }else if(mediaType["android"].indexOf(fileType)!==-1){ + iconBig = (); + }else{ + iconBig = (); + } + return iconBig; +} + +class DownloadCompoment extends Component { + + page=0; + + state={ + downloading:[], + loading:false, + finishedList:[], + continue:true, + } + + componentDidMount = ()=>{ + this.loadDownloading(); + } + + loadDownloading = ()=>{ + this.setState({ + loading:true, + }); + axios.get('/RemoteDownload/FlushUser').then( (response)=> { + axios.post('/RemoteDownload/ListDownloading').then((response)=> { + this.setState({ + downloading:response.data, + loading:false, + }); + + }) + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right","加载失败","error"); + this.setState({ + loading:false, + }); + }); + } + + loadMore = ()=>{ + this.setState({ + loading:true, + }); + axios.get('/RemoteDownload/ListFinished?page='+(++this.page)).then( (response)=> { + this.setState({ + finishedList:response.data, + loading:false, + continue:response.data.length<10?false:true, + }); + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right","加载失败","error"); + this.setState({ + loading:false, + }); + }); + } + + cancelDownload = id=>{ + axios.post('/RemoteDownload/Cancel',{ + id:id, + }).then( (response)=> { + if(response.data.error!==0){ + this.props.toggleSnackbar("top","right",response.message,"error"); + }else{ + this.setState({ + downloading:this.state.downloading.filter(value=>{ + return value.id!==id; + }) + }); + this.props.toggleSnackbar("top","right","取消成功","success"); + } + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message,"error"); + }); + } + + render() { + const { classes } = this.props; + + + return ( +
+ 进行中 + + + {this.state.downloading.map(value=>{ + value.percent = !value.hasOwnProperty("completedLength")?0:(value.completedLength/value.totalLength) + return ( + +
+ {getIcon(classes,value.fileName)} +
+ + + {value.fileName} + + + + {value.hasOwnProperty("completedLength")&& + {value.percent.toFixed(2)}% - {value.completedLength==="0"?"0Bytes":sizeToString(value.completedLength)}/{value.totalLength==="0"?"0Bytes":sizeToString(value.totalLength)} - {value.downloadSpeed==="0"?"0B/s":sizeToString(value.downloadSpeed)+"/s"} + } + {!value.hasOwnProperty("completedLength")&& + - + } + + + this.cancelDownload(value.id)}> + + +
+ ) + })} + 已完成 +
+ {this.state.finishedList.map(value=>{ + + return ( + + {(JSON.stringify(value.fileName) !== '[]')&& +
+ {getIcon(classes,value.fileName)} +
} + + + + {value.fileName} + + + {(()=>{switch (value.status) { + case "canceled": + return (
已取消
); + case "error": + return (
错误:{value.msg}
); + case "success": + return (
成功
); + default: + break; + } + })()} +
+
+
+ ) + })} + +
+
+ ); + } + +} + +const Download = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(DownloadCompoment)) + +export default Download diff --git a/src/component/FileManager/ContextMenu.js b/src/component/FileManager/ContextMenu.js new file mode 100644 index 0000000..9dca351 --- /dev/null +++ b/src/component/FileManager/ContextMenu.js @@ -0,0 +1,341 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' +import { + changeContextMenu, + setNavigatorLoadingStatus, + navitateTo, + openCreateFolderDialog, + openRenameDialog, + openMoveDialog, + openRemoveDialog, + openShareDialog, + showImgPreivew, + openMusicDialog, + toggleSnackbar, + openRemoteDownloadDialog, + openTorrentDownloadDialog, + openGetSourceDialog, + } from "../../actions/index" +import {isPreviewable,isTorrent} from "../../config" +import {allowSharePreview} from "../../untils/index" +import { withStyles } from '@material-ui/core/styles'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; +import MenuList from '@material-ui/core/MenuList'; +import MenuItem from '@material-ui/core/MenuItem'; +import Divider from '@material-ui/core/Divider'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import UploadIcon from '@material-ui/icons/CloudUpload' +import DownloadIcon from '@material-ui/icons/CloudDownload' +import NewFolderIcon from '@material-ui/icons/CreateNewFolder' +import OpenFolderIcon from '@material-ui/icons/FolderOpen' +import ShareIcon from '@material-ui/icons/Share' +import RenameIcon from '@material-ui/icons/BorderColor' +import MoveIcon from '@material-ui/icons/Input' +import LinkIcon from '@material-ui/icons/InsertLink' +import DeleteIcon from '@material-ui/icons/Delete' +import OpenIcon from '@material-ui/icons/OpenInNew' +import {MagnetOn} from 'mdi-material-ui' + +const styles = theme => ({ + propover:{ + minWidth:"200px!important", + } +}) + +const mapStateToProps = state => { + return { + menuType:state.viewUpdate.contextType, + menuOpen:state.viewUpdate.contextOpen, + isMultiple:state.explorer.selectProps.isMultiple, + withFolder:state.explorer.selectProps.withFolder, + withFile:state.explorer.selectProps.withFile, + path:state.navigator.path, + selected:state.explorer.selected, + } +} + +const mapDispatchToProps = dispatch => { + return { + changeContextMenu: (type,open) => { + dispatch(changeContextMenu(type,open)) + }, + setNavigatorLoadingStatus: status => { + dispatch(setNavigatorLoadingStatus(status)) + }, + navitateTo:path => { + dispatch(navitateTo(path)) + }, + openCreateFolderDialog:()=>{ + dispatch(openCreateFolderDialog()) + }, + openRenameDialog:()=>{ + dispatch(openRenameDialog()) + }, + openMoveDialog:()=>{ + dispatch(openMoveDialog()) + }, + openRemoveDialog:()=>{ + dispatch(openRemoveDialog()) + }, + openShareDialog:()=>{ + dispatch(openShareDialog()) + }, + showImgPreivew:(first)=>{ + dispatch(showImgPreivew(first)) + }, + openMusicDialog:()=>{ + dispatch(openMusicDialog()) + }, + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + openRemoteDownloadDialog:()=>{ + dispatch(openRemoteDownloadDialog()) + }, + openTorrentDownloadDialog:()=>{ + dispatch(openTorrentDownloadDialog()) + }, + openGetSourceDialog:()=>{ + dispatch(openGetSourceDialog()) + } + } +} + +class ContextMenuCompoment extends Component { + + X=0; + Y=0; + + state={ + } + + componentDidMount = ()=>{ + window.document.addEventListener("mousemove",this.setPoint); + } + + setPoint = e=>{ + this.Y=e.clientY; + this.X=e.clientX; + }; + + + openDownload = ()=>{ + if(!allowSharePreview()){ + this.props.toggleSnackbar("top","right","未登录用户无法预览","warning"); + this.props.changeContextMenu("file",false); + return; + } + this.props.changeContextMenu("file",false); + let downloadPath = this.props.selected[0].path === "/" ? this.props.selected[0].path+this.props.selected[0].name:this.props.selected[0].path+"/"+this.props.selected[0].name; + window.open(window.apiURL.download+"?action=download&path="+encodeURIComponent(downloadPath)); + + } + + enterFolder = () => { + this.props.navitateTo(this.props.path==="/"?this.props.path+this.props.selected[0].name:this.props.path+"/"+this.props.selected[0].name); + } + + clickUpload = () => { + this.props.changeContextMenu("empty",false); + let uploadButton = document.getElementsByClassName("uploadForm")[0]; + if (document.body.contains(uploadButton)){ + uploadButton.click(); + }else{ + this.props.toggleSnackbar("top","right","上传组件还未加载完成","warning"); + } + } + + openPreview = ()=>{ + if(!allowSharePreview()){ + this.props.toggleSnackbar("top","right","未登录用户无法预览","warning"); + this.props.changeContextMenu("file",false); + return; + } + this.props.changeContextMenu("file",false); + let previewPath = this.props.selected[0].path === "/" ? this.props.selected[0].path+this.props.selected[0].name:this.props.selected[0].path+"/"+this.props.selected[0].name; + switch(isPreviewable(this.props.selected[0].name)){ + case 'img': + this.props.showImgPreivew(this.props.selected[0]); + return; + case 'msDoc': + window.open(window.apiURL.docPreiview+"/?path="+encodeURIComponent(previewPath)); + return; + case 'audio': + this.props.openMusicDialog(); + return; + case 'open': + window.open(window.apiURL.preview+"/?action=preview&path="+encodeURIComponent(previewPath)); + return; + case 'video': + if(window.isSharePage){ + window.location.href=("/Viewer/Video?share=true&shareKey="+window.shareInfo.shareId+"&path="+encodeURIComponent(previewPath)); + return; + } + window.location.href=("/Viewer/Video?&path="+encodeURIComponent(previewPath)); + return; + case 'edit': + if(window.isSharePage){ + window.location.href=("/Viewer/Markdown?share=true&shareKey="+window.shareInfo.shareId+"&path="+encodeURIComponent(previewPath)); + return; + } + window.location.href=("/Viewer/Markdown?path="+encodeURIComponent(previewPath)); + return; + default: + return; + } + } + + render() { + + const { classes} = this.props; + + return ( +
+ this.props.changeContextMenu(this.props.menuType,false)} + anchorOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + > + {this.props.menuType==="empty"&& + + + + + + 上传文件 + + {window.uploadConfig.allowRemoteDownload==="1"&& + this.props.openRemoteDownloadDialog()}> + + + + 离线下载 + + } + + + this.props.openCreateFolderDialog()}> + + + + 创建文件夹 + + + + } + {this.props.menuType!=="empty"&& + + {(!this.props.isMultiple && this.props.withFolder)&& + + + + + 进入 + + } + {(!this.props.isMultiple&&this.props.withFile&&isPreviewable(this.props.selected[0].name))&& +
+ this.openPreview()}> + + + + 打开 + + +
+ } + + {(!this.props.isMultiple&&this.props.withFile)&& + this.openDownload()}> + + + + 下载 + + } + + {(!this.props.isMultiple&&this.props.withFile&&(window.uploadConfig.allowSource==="1"))&& + this.props.openGetSourceDialog()}> + + + + 获取外链 + + } + + {(!this.props.isMultiple&&window.isHomePage&&(window.uploadConfig.allowTorrentDownload==="1")&&this.props.withFile&&isTorrent(this.props.selected[0].name))&& + this.props.openTorrentDownloadDialog()}> + + + + 创建离线下载任务 + + } + + {(!this.props.isMultiple &&window.isHomePage)&& + this.props.openShareDialog()}> + + + + 分享 + + } + + {(!this.props.isMultiple&&window.isHomePage)&& + this.props.openRenameDialog() }> + + + + 重命名 + + } + {window.isHomePage&&
+ this.props.openMoveDialog() }> + + + + 移动 + + + this.props.openRemoveDialog()}> + + + + 删除 + +
} + + + +
+ } +
+
+ ); + } +} + +ContextMenuCompoment.propTypes = { + classes: PropTypes.object.isRequired, + menuType:PropTypes.string.isRequired, +}; + + +const ContextMenu = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(ContextMenuCompoment)) + +export default ContextMenu \ No newline at end of file diff --git a/src/component/FileManager/Explorer.js b/src/component/FileManager/Explorer.js new file mode 100644 index 0000000..f9aca88 --- /dev/null +++ b/src/component/FileManager/Explorer.js @@ -0,0 +1,271 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' + +import {navitateTo,changeContextMenu,navitateUp} from "../../actions/index" +import ObjectIcon from "./ObjectIcon" +import ContextMenu from "./ContextMenu" +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import { withStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import Paper from '@material-ui/core/Paper' +import EmptyIcon from "@material-ui/icons/Unarchive" +import SadIcon from "@material-ui/icons/SentimentVeryDissatisfied" +import classNames from 'classnames'; +import ImgPreivew from "./ImgPreview" +import Button from '@material-ui/core/Button'; +import UpIcon from '@material-ui/icons/ArrowUpward' + +const styles = theme => ({ + paper: { + padding: theme.spacing.unit * 2, + textAlign: 'center', + color: theme.palette.text.secondary, + margin:"10px", + + }, + root: { + flexGrow: 1, + padding:"10px", + overflowY: "auto", + height: "calc(100vh - 112px)", + [theme.breakpoints.up('sm')]: { + overflowY: "auto", + height: "calc(100vh - 112px)", + }, + [theme.breakpoints.down('sm')]: { + height: "100%", + }, + }, + rootTable:{ + padding:"0px", + backgroundColor:theme.palette.background.paper.white, + [theme.breakpoints.up('sm')]: { + overflowY: "auto", + height: "calc(100vh - 112px)", + }, + [theme.breakpoints.down('sm')]: { + height: "100%", + }, + }, + typeHeader:{ + margin: "10px 25px", + color: "#6b6b6b", + fontWeight: "500", + }, + loading:{ + justifyContent: "center", + display: "flex", + marginTop:"40px", + }, + errorBox:{ + padding: theme.spacing.unit * 4, + }, + errorMsg:{ + marginTop:"10px", + }, + emptyContainer:{ + bottom: "0", + height: "300px", + margin: "50px auto", + width: "300px", + color: theme.palette.explorer.emptyIcon, + textAlign: "center", + paddingTop: "20px", + }, + emptyIcon:{ + fontSize: "160px", + }, + emptyInfoBig:{ + fontSize: "25px", + color:theme.palette.text.disabled, + }, + emptyInfoSmall:{ + color:theme.palette.text.hint, + }, + hideAuto:{ + [theme.breakpoints.down('sm')]: { + display:"none", + } + }, + flexFix:{ + minWidth: 0, + }, + upButton:{ + marginLeft: "20px", + marginTop: "10px", + marginBottom: "10px", + } +}) + +const mapStateToProps = state => { + return { + path: state.navigator.path, + drawerDesktopOpen:state.viewUpdate.open, + viewMethod:state.viewUpdate.explorerViewMethod, + sortMethod:state.viewUpdate.sortMethod, + fileList:state.explorer.fileList, + dirList:state.explorer.dirList, + loading:state.viewUpdate.navigatorLoading, + navigatorError:state.viewUpdate.navigatorError, + navigatorErrorMsg:state.viewUpdate.navigatorErrorMsg, + keywords:state.explorer.keywords, + } +} + +const mapDispatchToProps = dispatch => { + return { + navigateToPath: path => { + dispatch(navitateTo(path)) + }, + + changeContextMenu: (type,open) => { + dispatch(changeContextMenu(type,open)) + }, + navitateUp:()=>{ + dispatch(navitateUp()) + } + } +} + +class ExplorerCompoment extends Component { + + contextMenu = (e) => { + e.preventDefault(); + if(this.props.keywords===null&&!window.isSharePage){ + if(!this.props.loading){ + this.props.changeContextMenu("empty",true); + } + } + + + } + + render() { + + const { classes} = this.props; + + return ( +
+ + + {this.props.navigatorError&& + + + :( 请求时出现错误 + + {this.props.navigatorErrorMsg.message} + + } + + {(this.props.loading && !this.props.navigatorError) && +
+ +
+ } + + {(window.isMobile&&this.props.path!=="/"&&!this.props.loading)&& + + } + + {(this.props.keywords===null&&window.isHomePage&&this.props.dirList.length===0&&this.props.fileList.length===0&&!this.props.loading&&!this.props.navigatorError)&& +
+ +
拖拽文件至此
+
或点击左侧“上传文件”按钮添加文件
+ +
+ } + {((this.props.keywords!==null&&this.props.dirList.length===0&&this.props.fileList.length===0&&!this.props.loading&&!this.props.navigatorError)||(this.props.dirList.length===0&&this.props.fileList.length===0&&!this.props.loading&&!this.props.navigatorError&&window.isSharePage))&& +
+ +
什么都没有找到
+ +
+ } + {(this.props.viewMethod!=="list" &&(this.props.dirList.length!==0||this.props.fileList.length!==0)&&!this.props.loading)&& +
+ + {(this.props.dirList.length!==0 && !this.props.loading)&& +
+ 文件夹 + + {this.props.dirList.map((value,index)=>( + + + + ))} + +
+ } + {(this.props.fileList.length!==0 && !this.props.loading)&& +
+ 文件 + + {this.props.fileList.map((value,index)=>( + + + + ))} + +
+ } +
+ } + + {(this.props.viewMethod==="list" &&(this.props.dirList.length!==0 || this.props.fileList.length!==0)&&!this.props.loading)&& + + + + 名称 + 大小 + 日期 + + + + {this.props.dirList.map((value,index)=>( + + ))} + {this.props.fileList.map((value,index)=>( + + ))} + +
+ } + + +
+ ); + } +} + +ExplorerCompoment.propTypes = { + classes: PropTypes.object.isRequired, + path:PropTypes.string.isRequired, +}; + + +const Explorer = connect( + mapStateToProps, + mapDispatchToProps + )( withStyles(styles)(ExplorerCompoment)) + +export default Explorer \ No newline at end of file diff --git a/src/component/FileManager/FileIcon.js b/src/component/FileManager/FileIcon.js new file mode 100644 index 0000000..8e8936a --- /dev/null +++ b/src/component/FileManager/FileIcon.js @@ -0,0 +1,345 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' +import { withStyles } from '@material-ui/core/styles'; + +import ButtonBase from '@material-ui/core/ButtonBase'; +import Typography from '@material-ui/core/Typography'; +import classNames from 'classnames'; +import {allowSharePreview} from "../../untils/index" +import ImageIcon from '@material-ui/icons/PhotoSizeSelectActual' +import VideoIcon from '@material-ui/icons/Videocam' +import AudioIcon from '@material-ui/icons/Audiotrack' +import PdfIcon from "@material-ui/icons/PictureAsPdf" +import Divider from "@material-ui/core/Divider" +import FileShowIcon from "@material-ui/icons/InsertDriveFile" +import Tooltip from '@material-ui/core/Tooltip'; +import {FileWordBox,FilePowerpointBox,FileExcelBox,ScriptText,MagnetOn,ZipBox,WindowRestore,Android} from 'mdi-material-ui' +import { LazyLoadImage } from 'react-lazy-load-image-component'; +import ContentLoader from 'react-content-loader' +import {mediaType} from "../../config" + +const styles = theme => ({ + container: { + padding: "7px", + }, + + selected: { + "&:hover": { + border: "1px solid #d0d0d0", + }, + backgroundColor: theme.palette.explorer.bgSelected, + + }, + + notSelected: { + "&:hover": { + backgroundColor: "#f9f9f9", + border: "1px solid #d0d0d0", + }, + backgroundColor: theme.palette.background.paper, + }, + + button: { + border: "1px solid #dadce0", + width: "100%", + borderRadius: "6px", + boxSizing: "border-box", + transition: "background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms", + alignItems: "initial", + display: "initial", + }, + folderNameSelected: { + color: theme.palette.primary.dark, + fontWeight: "500", + }, + folderNameNotSelected: { + color: theme.palette.explorer.filename, + }, + folderName: { + marginTop: "15px", + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden", + marginRight: "20px", + }, + preview:{ + overflow: "hidden", + height:"150px", + width:"100%", + borderRadius: "6px 6px 0 0", + }, + previewIcon:{ + overflow: "hidden", + height:"149px", + width:"100%", + borderRadius: "6px 6px 0 0", + backgroundColor:theme.palette.background.paper, + paddingTop:"50px", + }, + picPreview:{ + "height": "auto", + "width": "100%", + }, + fileInfo:{ + height:"50px", + display: "flex", + }, + icon: { + margin: "10px 10px 10px 16px", + height: "30px", + minWidth: "30px", + backgroundColor: theme.palette.background.paper, + borderRadius: "90%", + paddingTop: "2px", + color: theme.palette.explorer.icon, + }, + iconImg : { + color:"#d32f2f", + }, + iconImgBig:{ + color:"#d32f2f", + fontSize: "50px", + }, + iconVideo : { + color:"#d50000", + }, + iconVideoBig:{ + color:"#d50000", + fontSize: "50px", + }, + iconAudio : { + color:"#651fff", + }, + iconAudioBig:{ + color:"#651fff", + fontSize: "50px", + }, + iconPdf : { + color:"#f44336", + }, + iconPdfBig:{ + color:"#f44336", + fontSize: "50px", + }, + iconWord : { + color:"#538ce5", + }, + iconWordBig:{ + color:"#538ce5", + fontSize: "50px", + }, + iconPpt : { + color:"rgb(239, 99, 63)", + }, + iconPptBig:{ + color:"rgb(239, 99, 63)", + fontSize: "50px", + }, + iconExcel : { + color:"#4caf50", + }, + iconExcelBig:{ + color:"#4caf50", + fontSize: "50px", + }, + iconText : { + color:"#607d8b", + }, + iconTextBig:{ + color:"#607d8b", + fontSize: "50px", + }, + iconFile : { + color:"#424242", + }, + iconFileBig:{ + color:"#424242", + fontSize: "50px", + }, + iconTorrent : { + color:"#5c6bc0", + }, + iconTorrentBig:{ + color:"#5c6bc0", + fontSize: "50px", + }, + iconZip : { + color:"#f9a825", + }, + iconZipBig:{ + color:"#f9a825", + fontSize: "50px", + }, + iconAndroid : { + color:"#8bc34a", + }, + iconAndroidBig:{ + color:"#8bc34a", + fontSize: "50px", + }, + iconExe : { + color:"#1a237e", + }, + iconExeBig:{ + color:"#1a237e", + fontSize: "50px", + }, + hide:{ + display:"none", + }, + loadingAnimation:{ + borderRadius: "6px 6px 0 0", + }, + shareFix:{ + marginLeft: "20px", + } +}) + +const mapStateToProps = state => { + return { + path: state.navigator.path, + selected: state.explorer.selected, + } +} + +const mapDispatchToProps = dispatch => { + return { + + } +} + +class FileIconCompoment extends Component { + + static defaultProps = { + share: false, + } + + state={ + loading:false, + } + + render() { + + const { classes } = this.props; + + const isSelected = (this.props.selected.findIndex((value) => { + return value === this.props.file; + })) !== -1; + + let icon,iconBig; + let fileType =this.props.file.name.split(".").pop().toLowerCase(); + if (mediaType["image"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["video"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["audio"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["pdf"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["word"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["ppt"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["excel"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["text"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["torrent"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["zip"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["excute"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else if(mediaType["android"].indexOf(fileType)!==-1){ + icon = (); + iconBig = (); + }else{ + icon = (); + iconBig = (); + } + + return ( +
+ + {(this.props.file.pic!==""&& this.props.file.pic!==" "&&allowSharePreview())&& +
+ this.setState({loading:false})} + beforeLoad = {()=>this.setState({loading:true})} + /> + + + + + + +
+ } + {(this.props.file.pic===""|| this.props.file.pic===" "||!allowSharePreview())&& +
+ {iconBig} +
+ + } + {(this.props.file.pic===""|| this.props.file.pic===" "||!allowSharePreview())&& } +
+ {!this.props.share&&
{icon}
} + + {this.props.file.name} + +
+
+ +
+ ); + } +} + +FileIconCompoment.propTypes = { + classes: PropTypes.object.isRequired, + file: PropTypes.object.isRequired, +}; + + +const FileIcon = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(FileIconCompoment)) + +export default FileIcon \ No newline at end of file diff --git a/src/component/FileManager/FileManager.js b/src/component/FileManager/FileManager.js new file mode 100644 index 0000000..713f9fb --- /dev/null +++ b/src/component/FileManager/FileManager.js @@ -0,0 +1,30 @@ +import React, { Component } from 'react' + +import { withStyles } from '@material-ui/core/styles'; + +import Navigator from "./Navigator" +import Explorer from "./Explorer" +import Modals from "./Modals" + +const styles = theme => ({ + +}) + +class FileManager extends Component { + + render() { + return ( +
+ + + +
+ ); + } + +} + +FileManager.propTypes = { +}; + +export default withStyles(styles)(FileManager); \ No newline at end of file diff --git a/src/component/FileManager/Folder.js b/src/component/FileManager/Folder.js new file mode 100644 index 0000000..96c97e3 --- /dev/null +++ b/src/component/FileManager/Folder.js @@ -0,0 +1,130 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' + +import { withStyles } from '@material-ui/core/styles'; + +import ButtonBase from '@material-ui/core/ButtonBase'; +import Typography from '@material-ui/core/Typography'; +import FolderIcon from '@material-ui/icons/Folder' +import classNames from 'classnames'; +import Tooltip from '@material-ui/core/Tooltip'; + +const styles = theme => ({ + container: { + padding: "7px", + }, + + selected: { + "&:hover": { + border: "1px solid #d0d0d0", + }, + backgroundColor: theme.palette.explorer.bgSelected, + + }, + + notSelected: { + "&:hover": { + backgroundColor: "#f9f9f9", + border: "1px solid #d0d0d0", + }, + backgroundColor: theme.palette.background.paper, + }, + + button: { + height: "50px", + border: "1px solid #dadce0", + width: "100%", + borderRadius: "6px", + boxSizing: "border-box", + transition: "background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms", + display: "flex", + justifyContent: "left", + alignItems: "initial", + }, + icon: { + margin: "10px 10px 10px 16px", + height: "30px", + minWidth: "30px", + backgroundColor: theme.palette.background.paper, + borderRadius: "90%", + paddingTop: "2px", + color: theme.palette.explorer.icon, + }, + folderNameSelected: { + color: theme.palette.primary.dark, + fontWeight: "500", + }, + folderNameNotSelected: { + color: theme.palette.explorer.filename, + }, + folderName: { + marginTop: "15px", + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden", + marginRight: "20px", + } +}) + +const mapStateToProps = state => { + return { + selected: state.explorer.selected, + } +} + +const mapDispatchToProps = dispatch => { + return { + } +} + + +class FolderCompoment extends Component { + + state = { + } + + + render() { + + const { classes } = this.props; + + const isSelected = (this.props.selected.findIndex((value) => { + return value === this.props.folder; + })) !== -1; + + return ( + +
+ + {this.props.folder.name} + +
+ ); + } +} + +FolderCompoment.propTypes = { + classes: PropTypes.object.isRequired, + folder: PropTypes.object.isRequired, +}; + + +const Folder = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(FolderCompoment)) + +export default Folder \ No newline at end of file diff --git a/src/component/FileManager/ImgPreview.js b/src/component/FileManager/ImgPreview.js new file mode 100644 index 0000000..56caf1b --- /dev/null +++ b/src/component/FileManager/ImgPreview.js @@ -0,0 +1,131 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' + +import { withStyles } from '@material-ui/core/styles'; +import { + showImgPreivew, +}from "../../actions/index" +import {imgPreviewSuffix} from "../../config" +import PhotoSwipe from'react-photoswipe'; +import('react-photoswipe/lib/photoswipe.css') + +const styles = theme => ({ +}) + +const mapStateToProps = state => { + return { + first:state.explorer.imgPreview.first, + other:state.explorer.imgPreview.other, + } +} + +const mapDispatchToProps = dispatch => { + return { + showImgPreivew:(first)=>{ + dispatch(showImgPreivew(first)) + } + } +} + +class ImgPreviewCompoment extends Component { + + state = { + first:[], + items:[], + open:false, + loaded:false, + } + + options={ + history: false, + focus: false, + showAnimationDuration: 5, + hideAnimationDuration: 0, + bgOpacity: 0.8, + closeOnScroll: 0, + }; + + componentWillReceiveProps = (nextProps)=>{ + let items = []; + if(nextProps.first!==null){ + if(!this.state.loaded){ + this.setState({ + loaded:true, + }) + } + var firstOne; + // eslint-disable-next-line + nextProps.other.map((value)=>{ + let fileType =value.name.split(".").pop().toLowerCase(); + + if(imgPreviewSuffix.indexOf(fileType)!==-1){ + let newImg = { + h:0, + w:0, + title:value.name, + src:window.apiURL.preview+"?action=preview&path="+encodeURIComponent(value.path==="/"?value.path+value.name:value.path+"/"+value.name), + }; + if((value.path===nextProps.first.path)&&(value.name===nextProps.first.name)){ + firstOne = newImg; + }else{ + items.push(newImg); + } + + }; + }); + items.unshift(firstOne); + this.setState({ + items:items, + open:true, + }); + + } + } + + handleClose=()=>{ + this.props.showImgPreivew(null); + this.setState({ + loaded:true, + open:false, + }); + } + + setSize = (ps,index,item)=>{ + if (item.h < 1 || item.w < 1) { + let img = new Image() + img.onload = () => { + item.w = img.width + item.h = img.height + ps.invalidateCurrItems() + ps.updateSize(true) + } + img.src = item.src + } + } + + render() { + if(this.state.loaded){ + return ( +
+ +
+ ); + }else{ + return (
); + } + + } +} + +ImgPreviewCompoment.propTypes = { + classes: PropTypes.object.isRequired, +}; + + +const ImgPreivew = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(ImgPreviewCompoment)) + +export default ImgPreivew \ No newline at end of file diff --git a/src/component/FileManager/Modals.js b/src/component/FileManager/Modals.js new file mode 100644 index 0000000..a9c5b00 --- /dev/null +++ b/src/component/FileManager/Modals.js @@ -0,0 +1,720 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' +import { + closeAllModals, + toggleSnackbar, + setModalsLoading, + refreshFileList, + refreshStorage, +} from "../../actions/index" +import PathSelector from "./PathSelector" +import { withStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import Checkbox from '@material-ui/core/Checkbox'; +import axios from 'axios' +import FormControl from '@material-ui/core/FormControl'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; + +const styles = theme => ({ + wrapper: { + margin: theme.spacing.unit, + position: 'relative', + }, + buttonProgress: { + color: theme.palette.secondary.light , + position: 'absolute', + top: '50%', + left: '50%', + marginTop: -12, + marginLeft: -12, + }, + contentFix:{ + padding: "10px 24px 0px 24px", + }, + shareUrl:{ + minWidth:"400px", + }, + widthAnimation:{ + + }, +}) + +const mapStateToProps = state => { + return { + path:state.navigator.path, + selected:state.explorer.selected, + modalsStatus:state.viewUpdate.modals, + modalsLoading:state.viewUpdate.modalsLoading, + dirList:state.explorer.dirList, + fileList:state.explorer.fileList, + } +} + +const mapDispatchToProps = dispatch => { + return { + closeAllModals:()=>{ + dispatch(closeAllModals()); + }, + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + setModalsLoading:(status)=>{ + dispatch(setModalsLoading(status)) + }, + refreshFileList:()=>{ + dispatch(refreshFileList()) + }, + refreshStorage:()=>{ + dispatch(refreshStorage()) + } + } +} + +class ModalsCompoment extends Component { + + + state={ + newFolderName: "", + newName:"", + selectedPath:"", + selectedPathName:"", + secretShare:false, + sharePwd:"", + shareUrl:"", + downloadURL:"", + remoteDownloadPathSelect:false, + source:"", + } + + handleInputChange = (e)=>{ + this.setState({ + [e.target.id]:e.target.value, + }); + } + + newNameSuffix = ""; + + componentWillReceiveProps = (nextProps)=>{ + if(this.props.modalsStatus.rename!==nextProps.modalsStatus.rename){ + let name = nextProps.selected[0].name.split("."); + if(name.length>1){ + this.newNameSuffix = name.pop(); + } + this.setState({ + newName:name.join("."), + }); + return; + } + if((this.props.modalsStatus.getSource!==nextProps.modalsStatus.getSource)&&nextProps.modalsStatus.getSource===true){ + axios.post('/File/gerSource', { + action: 'source', + path:(this.props.selected[0].path === "/"?"":this.props.path)+"/"+this.props.selected[0].name, + }) + .then( (response)=> { + this.setState({ + source:response.data.url, + }) + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + }); + } + } + + submitShare = e => { + e.preventDefault(); + this.props.setModalsLoading(true); + axios.post('/File/Share', { + action: 'share', + item: this.props.selected[0].path === "/" ? this.props.selected[0].path+this.props.selected[0].name:this.props.selected[0].path+"/"+this.props.selected[0].name, + shareType:this.state.secretShare?"private":"public", + pwd:this.state.sharePwd + }) + .then( (response)=> { + if(response.data.result!==""){ + this.setState({ + shareUrl:response.data.result, + }); + }else{ + this.props.toggleSnackbar("top","right",response.data.result.error,"warning"); + } + this.props.setModalsLoading(false); + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.props.setModalsLoading(false); + }); + } + + submitRemove = e =>{ + e.preventDefault(); + this.props.setModalsLoading(true); + let dirs=[],items = []; + // eslint-disable-next-line + this.props.selected.map((value)=>{ + if(value.type==="dir"){ + dirs.push(value.path === "/" ? value.path+value.name:value.path+"/"+value.name); + }else{ + items.push(value.path === "/" ? value.path+value.name:value.path+"/"+value.name); + } + }); + axios.post('/File/Delete', { + action: 'delete', + items: items, + dirs:dirs, + newPath:this.state.selectedPath === "//"?"/":this.state.selectedPath, + }) + .then( (response)=> { + if(response.data.result.success){ + this.onClose(); + this.props.refreshFileList(); + }else{ + this.props.toggleSnackbar("top","right",response.data.result.error,"warning"); + } + this.props.setModalsLoading(false); + this.props.refreshStorage(); + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.props.setModalsLoading(false); + + }); + } + + submitMove = e =>{ + e.preventDefault(); + this.props.setModalsLoading(true); + let dirs=[],items = []; + // eslint-disable-next-line + this.props.selected.map((value)=>{ + if(value.type==="dir"){ + dirs.push(value.path === "/" ? value.path+value.name:value.path+"/"+value.name); + }else{ + items.push(value.path === "/" ? value.path+value.name:value.path+"/"+value.name); + } + }); + axios.post('/File/Move', { + action: 'move', + items: items, + dirs:dirs, + newPath:this.state.selectedPath === "//"?"/":this.state.selectedPath, + }) + .then( (response)=> { + if(response.data.result.success){ + this.onClose(); + this.props.refreshFileList(); + }else{ + this.props.toggleSnackbar("top","right",response.data.result.error,"warning"); + } + this.props.setModalsLoading(false); + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.props.setModalsLoading(false); + }); + } + + submitRename = e =>{ + e.preventDefault(); + this.props.setModalsLoading(true); + let newName = this.state.newName+(this.newNameSuffix===""?"":"."+this.newNameSuffix); + if(this.props.dirList.findIndex((value,index)=>{ + return value.name === newName; + })!==-1 || this.props.fileList.findIndex((value,index)=>{ + return value.name === newName; + })!==-1){ + this.props.toggleSnackbar("top","right","新名称与已有文件重复","warning"); + this.props.setModalsLoading(false); + }else{ + axios.post('/File/Rename', { + action: 'rename', + item: (this.props.selected[0].path === "/"?"":this.props.path)+"/"+this.props.selected[0].name, + newItemPath:(this.props.selected[0].path === "/"?"":this.props.path)+"/"+newName, + }) + .then( (response)=> { + if(response.data.result.success){ + this.onClose(); + this.props.refreshFileList(); + }else{ + this.props.toggleSnackbar("top","right",response.data.result.error,"warning"); + } + this.props.setModalsLoading(false); + + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.props.setModalsLoading(false); + + }); + } + } + + submitCreateNewFolder = e =>{ + e.preventDefault(); + this.props.setModalsLoading(true); + if(this.props.dirList.findIndex((value,index)=>{ + return value.name === this.state.newFolderName; + })!==-1){ + this.props.toggleSnackbar("top","right","文件夹名称重复","warning"); + this.props.setModalsLoading(false); + }else{ + axios.post('/File/createFolder', { + action: '"createFolder"', + newPath: (this.props.path === "/"?"":this.props.path)+"/"+this.state.newFolderName, + }) + .then( (response)=> { + if(response.data.result.success){ + this.onClose(); + this.props.refreshFileList(); + }else{ + this.props.toggleSnackbar("top","right",response.data.result.error,"warning"); + } + this.props.setModalsLoading(false); + + }) + .catch((error) =>{ + this.props.setModalsLoading(false); + + this.props.toggleSnackbar("top","right",error.message ,"error"); + }); + } + //this.props.toggleSnackbar(); + } + + submitTorrentDownload = e =>{ + e.preventDefault(); + this.props.setModalsLoading(true); + axios.post('/RemoteDownload/AddTorrent', { + action: "torrentDownload", + id: this.props.selected[0].id, + savePath: this.state.selectedPath, + }) + .then( (response)=> { + if(response.data.result.success){ + this.props.toggleSnackbar("top","right","任务已创建","success"); + this.onClose(); + }else{ + this.props.toggleSnackbar("top","right",response.data.result.error,"warning"); + } + this.props.setModalsLoading(false); + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.props.setModalsLoading(false); + }); + } + + submitDownload = e=>{ + e.preventDefault(); + this.props.setModalsLoading(true); + axios.post('/RemoteDownload/addUrl', { + action: 'remoteDownload', + url: this.state.downloadURL, + path: this.state.selectedPath, + }) + .then( (response)=> { + if(response.data.result.success){ + this.props.toggleSnackbar("top","right","任务已创建","success"); + this.onClose(); + }else{ + this.props.toggleSnackbar("top","right",response.data.result.error,"warning"); + } + this.props.setModalsLoading(false); + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.props.setModalsLoading(false); + }); + } + + setMoveTarget = (folder) =>{ + let path = folder.path === "/" ?folder.path+folder.name:folder.path+"/"+folder.name; + this.setState({ + selectedPath:path, + selectedPathName:folder.name, + }); + } + + remoteDownloadNext = ()=>{ + this.props.closeAllModals(); + this.setState({ + remoteDownloadPathSelect:true, + }); + } + + onClose = ()=>{ + this.setState({ + newFolderName: "", + newName:"", + selectedPath:"", + selectedPathName:"", + secretShare:false, + sharePwd:"", + downloadURL:"", + shareUrl:"", + remoteDownloadPathSelect:false, + source:"", + }); + this.newNameSuffix = ""; + this.props.closeAllModals(); + } + + handleChange = name => event => { + this.setState({ [name]: event.target.checked }); + }; + + render() { + + const { classes} = this.props; + + const previewApi = window.apiURL.preview; + + return ( +
+ + 获取文件外链 + + +
+ + +
+ + + + +
+ + 新建文件夹 + + +
+ this.handleInputChange(e)} + fullWidth + /> + +
+ + +
+ +
+
+ +
+ + 重命名 + + + 输入 {this.props.selected.length===1?this.props.selected[0].name:""} 的新名称: + +
+ this.handleInputChange(e)} + fullWidth + + /> + +
+ + +
+ +
+
+ +
+ + 移动至 + + + {this.state.selectedPath!==""&& + + 移动至 {this.state.selectedPathName} + + } + + +
+ +
+
+ +
+ + 删除对象 + + + + 确定要删除{(this.props.selected.length === 1)&& + {this.props.selected[0].name} + }{(this.props.selected.length > 1)&& + 这{this.props.selected.length}个对象 + }吗? + + + + +
+ +
+
+
+ + 创建分享链接 + + + + 获取用于共享的链接 + + {this.state.shareUrl===""&& +
+ } + label="使用密码保护链接"/> + {this.state.secretShare&& + + } + + } + {this.state.shareUrl!==""&& + + + } + +
+ + + {this.state.shareUrl===""&&
+ +
} +
+ +
+ + 音频播放 + + + + {(this.props.selected.length!==0)&& + + } + + + + + + + + + + 新建离线下载任务 + + + + + + + + + + + + + + 选择存储位置 + + + {this.state.selectedPath!==""&& + + 下载至 {this.state.selectedPathName} + + } + + +
+ +
+
+ +
+ + 选择存储位置 + + + {this.state.selectedPath!==""&& + + 下载至 {this.state.selectedPathName} + + } + + +
+ +
+
+ +
+
+ ); + } +} + +ModalsCompoment.propTypes = { + classes: PropTypes.object.isRequired, +}; + + +const Modals = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(ModalsCompoment)) + +export default Modals \ No newline at end of file diff --git a/src/component/FileManager/Navigator.js b/src/component/FileManager/Navigator.js new file mode 100644 index 0000000..66a4987 --- /dev/null +++ b/src/component/FileManager/Navigator.js @@ -0,0 +1,464 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' + +import { withStyles } from '@material-ui/core/styles'; +import Divider from '@material-ui/core/Divider'; +import Button from '@material-ui/core/Button'; +import RightIcon from '@material-ui/icons/KeyboardArrowRight' +import MoreIcon from '@material-ui/icons/MoreHoriz' +import ViewListIcon from '@material-ui/icons/ViewList' +import ViewModuleIcon from '@material-ui/icons/ViewModule' +import ViewSmallIcon from '@material-ui/icons/ViewComfy' +import TextTotateVerticalIcon from '@material-ui/icons/TextRotateVertical' +import FolderIcon from '@material-ui/icons/Folder' +import ExpandMore from '@material-ui/icons/ExpandMore'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import IconButton from '@material-ui/core/IconButton'; +import ShareIcon from '@material-ui/icons/Share' +import NewFolderIcon from '@material-ui/icons/CreateNewFolder' +import RefreshIcon from '@material-ui/icons/Refresh' +import { + navitateTo, + navitateUp, + changeViewMethod, + changeSortMethod, + setNavigatorError, + updateFileList, + setNavigatorLoadingStatus, + refreshFileList, + setSelectedTarget, + openCreateFolderDialog, + openShareDialog, +} from "../../actions/index" +import axios from 'axios' +import {setCookie,setGetParameter} from "../../untils/index" + +const mapStateToProps = state => { + return { + path: state.navigator.path, + refresh: state.navigator.refresh, + drawerDesktopOpen:state.viewUpdate.open, + viewMethod:state.viewUpdate.explorerViewMethod, + keywords:state.explorer.keywords, + sortMethod:state.viewUpdate.sortMethod, + } +} + +const mapDispatchToProps = dispatch => { + return { + navigateToPath: path => { + dispatch(navitateTo(path)) + }, + navitateUp:()=>{ + dispatch(navitateUp()) + }, + changeView:method=>{ + dispatch(changeViewMethod(method)) + }, + changeSort:method=>{ + dispatch(changeSortMethod(method)) + }, + setNavigatorError:(status,msg)=>{ + dispatch(setNavigatorError(status,msg)) + }, + updateFileList:list=>{ + dispatch(updateFileList(list)) + }, + setNavigatorLoadingStatus:status=>{ + dispatch(setNavigatorLoadingStatus(status)) + }, + refreshFileList:()=>{ + dispatch(refreshFileList()) + }, + setSelectedTarget:(target)=>{ + dispatch(setSelectedTarget(target)) + }, + openCreateFolderDialog:()=>{ + dispatch(openCreateFolderDialog()) + }, + openShareDialog:()=>{ + dispatch(openShareDialog()) + }, + } +} + +const delay = (ms) => new Promise( + (resolve) => setTimeout(resolve, ms) +); + +const sortOptions = [ + "文件名称正序", + "文件名称倒序", + "上传时间正序", + "上传时间到序", + "文件大小正序", + "文件大小倒序", +]; + +const styles = theme => ({ + container:{ + [theme.breakpoints.down('xs')]: { + display:"none", + }, + height:"48px", + overflow:"hidden", + backgroundColor:"#fff", + }, + navigatorContainer:{ + "display": "flex", + "justifyContent": "space-between", + }, + nav:{ + height:"47px", + padding:"5px 15px", + display:"flex", + }, + optionContainer:{ + paddingTop: "6px", + marginRight:"10px", + }, + rightIcon:{ + marginTop: "6px", + verticalAlign: "top", + color:"#868686", + }, + expandMore:{ + color:"#8d8d8d", + }, + sideButton:{ + padding:"8px", + marginRight:"5px", + } +}) + +class NavigatorCompoment extends Component { + + keywords = null; + + state = { + hidden:false, + hiddenFolders:[], + folders:[], + anchorEl: null, + hiddenMode:false, + anchorHidden:null, + anchorSort:null, + selectedIndex:0, + } + + constructor(props) { + super(props); + this.element = React.createRef(); + } + + componentDidMount = ()=>{ + this.renderPath(); + window.onpopstate = (event)=>{ + var url = new URL(window.location.href); + var c = url.searchParams.get("path"); + if(c!==null&&c!==this.props.path){ + this.props.navigateToPath(c); + } + }; + } + + renderPath = (path=null)=>{ + this.setState({ + folders:path!==null?path.substr(1).split("/"):this.props.path.substr(1).split("/"), + }); + var newPath = path!==null?path:this.props.path; + var apiURL = this.keywords===null?window.apiURL.listFile:'/File/SearchFile'; + newPath = this.keywords===null?newPath:this.keywords; + axios.post(apiURL, { + action: 'list', + path: newPath + }) + .then( (response)=> { + this.props.updateFileList(response.data.result); + this.props.setNavigatorLoadingStatus(false); + let pathTemp = (null?path.substr(1).split("/"):this.props.path.substr(1).split("/")).join(","); + setCookie("path_tmp",encodeURIComponent(pathTemp),1); + if(this.keywords===null){ + setGetParameter("path",encodeURIComponent(newPath)); + } + }) + .catch((error) =>{ + this.props.setNavigatorError(true,error); + }); + this.checkOverFlow(); + } + + redresh = (path) => { + this.props.setNavigatorLoadingStatus(true); + this.props.setNavigatorError(false,"error"); + this.renderPath(path); + } + + componentWillReceiveProps = (nextProps)=>{ + if(this.props.keywords!==nextProps.keywords){ + this.keywords=nextProps.keywords + } + if(this.props.path !== nextProps.path){ + this.renderPath(nextProps.path); + } + if(this.props.refresh !== nextProps.refresh){ + this.redresh(nextProps.path); + } + + } + + componentDidUpdate = (prevProps,prevStates)=>{ + if(this.state.folders !== prevStates.folders){ + this.checkOverFlow(); + } + if(this.props.drawerDesktopOpen !== prevProps.drawerDesktopOpen){ + delay(500).then(() => this.checkOverFlow()); + + } + } + + checkOverFlow = ()=>{ + const hasOverflowingChildren = this.element.current.offsetHeight < this.element.current.scrollHeight || + this.element.current.offsetWidth < this.element.current.scrollWidth; + if(hasOverflowingChildren && !this.state.hiddenMode){ + this.setState({hiddenMode:true}); + } + if(!hasOverflowingChildren && this.state.hiddenMode){ + this.setState({hiddenMode:false}); + } + } + + navigateTo=(event,id)=> { + if (id === this.state.folders.length-1){ + //最后一个路径 + this.setState({ anchorEl: event.currentTarget }); + return; + }else if(id===-1 && this.state.folders.length === 1 && this.state.folders[0] === ""){ + this.props.refreshFileList(); + this.handleClose(); + return; + }else if (id === -1){ + this.props.navigateToPath("/"); + this.handleClose(); + return; + }else{ + this.props.navigateToPath("/"+this.state.folders.slice(0,id+1).join("/")); + this.handleClose(); + } + } + + handleClose = () => { + this.setState({ anchorEl: null ,anchorHidden:null,anchorSort:null}); + }; + + showHiddenPath = (e) => { + this.setState({ anchorHidden: e.currentTarget }); + } + + showSortOptions = (e) => { + this.setState({ anchorSort: e.currentTarget }); + } + + performAction = e => { + this.handleClose(); + if(e==="refresh"){ + this.redresh(); + return; + } + let presentPath = this.props.path.split("/"); + let newTarget = [{ + type:"dir", + name:presentPath.pop(), + path:presentPath.length===1?"/":presentPath.join("/"), + }]; + //this.props.navitateUp(); + switch (e) { + case "share": + this.props.setSelectedTarget(newTarget); + this.props.openShareDialog(); + break; + case "newfolder": + this.props.openCreateFolderDialog(); + break; + default: + break; + } + + } + + + toggleViewMethod = () => { + this.props.changeView(this.props.viewMethod==="icon"?"list":(this.props.viewMethod==="list"?"smallIcon":"icon")); + } + + handleMenuItemClick = (e,index) => { + this.setState({ selectedIndex: index, anchorEl: null }); + let optionsTable = { + 0:"namePos", + 1:"nameRev", + 2:"timePos", + 3:"timeRev", + 4:"sizePos", + 5:"sizeRes", + }; + this.props.changeSort(optionsTable[index]); + this.handleClose(); + this.props.refreshFileList(); + } + + + render() { + + const { classes} = this.props; + + let presentFolderMenu = ( + this.performAction("refresh")}> + + 刷新 + + {(this.props.keywords===null&&window.isHomePage)&& +
+ + this.performAction("share")}> + + 分享 + + + this.performAction("newfolder")}> + + 创建文件夹 + + +
+ } + +
); + + return ( +
+
+
+ + + + + {this.state.hiddenMode && + + + + {this.state.folders.slice(0,-1).map((folder,id)=>( + this.navigateTo(e,id)}> + + + + + + ))} + + + + {presentFolderMenu} + + } + {!this.state.hiddenMode && this.state.folders.map((folder,id,folders)=>( + + {folder !=="" && + + + {(id === folders.length-1) && + presentFolderMenu + } + {(id !== folders.length-1) && } + + } + + + ))} + +
+
+ {(this.props.viewMethod === "icon")&& + + + + } + {(this.props.viewMethod === "list")&& + + + + } + + {(this.props.viewMethod === "smallIcon")&& + + + + } + + + + + + {sortOptions.map((option, index) => ( + this.handleMenuItemClick(event, index)} + > + {option} + + ))} + +
+
+ +
+ ); + } + +} + +NavigatorCompoment.propTypes = { + classes: PropTypes.object.isRequired, + path:PropTypes.string.isRequired, +}; + + +const Navigator = connect( + mapStateToProps, + mapDispatchToProps + )( withStyles(styles)(NavigatorCompoment)) + +export default Navigator \ No newline at end of file diff --git a/src/component/FileManager/ObjectIcon.js b/src/component/FileManager/ObjectIcon.js new file mode 100644 index 0000000..8f42e8e --- /dev/null +++ b/src/component/FileManager/ObjectIcon.js @@ -0,0 +1,215 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' +import { + changeContextMenu, + setSelectedTarget, + addSelectedTarget, + removeSelectedTarget, + setNavigatorLoadingStatus, + navitateTo, + showImgPreivew, + openMusicDialog, + toggleSnackbar +} from "../../actions/index" +import { withStyles } from '@material-ui/core/styles'; +import Folder from "./Folder" +import FileIcon from "./FileIcon" +import SmallIcon from "./SmallIcon" +import TableItem from "./TableRow" +import classNames from 'classnames'; +import {isPreviewable} from "../../config" +import {allowSharePreview} from "../../untils/index" +const styles = theme => ({ + container: { + padding: "7px", + }, + fixFlex:{ + minWidth:0, + } +}) + +const mapStateToProps = state => { + return { + path: state.navigator.path, + selected: state.explorer.selected, + viewMethod:state.viewUpdate.explorerViewMethod, + } +} + +const mapDispatchToProps = dispatch => { + return { + ContextMenu: (type, open) => { + dispatch(changeContextMenu(type, open)) + }, + setSelectedTarget: targets => { + dispatch(setSelectedTarget(targets)) + }, + addSelectedTarget: targets => { + dispatch(addSelectedTarget(targets)) + }, + removeSelectedTarget: id => { + dispatch(removeSelectedTarget(id)); + }, + setNavigatorLoadingStatus: status => { + dispatch(setNavigatorLoadingStatus(status)); + }, + navitateTo:path => { + dispatch(navitateTo(path)) + }, + showImgPreivew:(first)=>{ + dispatch(showImgPreivew(first)) + }, + openMusicDialog:()=>{ + dispatch(openMusicDialog()) + }, + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +class ObjectCompoment extends Component { + + state = { + } + + contextMenu = (e) => { + e.preventDefault(); + if ((this.props.selected.findIndex((value) => { + return value === this.props.file; + })) === -1) { + this.props.setSelectedTarget([this.props.file]); + } + this.props.ContextMenu("file", true); + } + + selectFile = (e) => { + let presentIndex = this.props.selected.findIndex((value) => { + return value === this.props.file; + }); + if (presentIndex !== -1 && e.ctrlKey) { + this.props.removeSelectedTarget(presentIndex); + } else { + if (e.ctrlKey) { + this.props.addSelectedTarget(this.props.file); + } else { + this.props.setSelectedTarget([this.props.file]); + } + } + } + + handleClick=(e)=> { + if(window.isMobile){ + this.selectFile(e); + if(this.props.file.type==="dir"){ + this.enterFolder(); + return; + } + }else{ + this.selectFile(e); + } + + } + + handleDoubleClick() { + if(this.props.file.type==="dir"){ + this.enterFolder(); + return; + } + if(!allowSharePreview()){ + this.props.toggleSnackbar("top","right","未登录用户无法预览","warning"); + return; + } + let previewPath = this.props.selected[0].path === "/" ? this.props.selected[0].path+this.props.selected[0].name:this.props.selected[0].path+"/"+this.props.selected[0].name; + switch(isPreviewable(this.props.selected[0].name)){ + case 'img': + this.props.showImgPreivew(this.props.selected[0]); + return; + case 'msDoc': + window.open(window.apiURL.docPreiview+"/?path="+encodeURIComponent(previewPath)); + return; + case 'audio': + this.props.openMusicDialog(); + return; + case 'open': + window.open(window.apiURL.preview+"/?action=preview&path="+encodeURIComponent(previewPath)); + return; + case 'video': + if(window.isSharePage){ + window.location.href=("/Viewer/Video?share=true&shareKey="+window.shareInfo.shareId+"&path="+encodeURIComponent(previewPath)); + return; + } + window.location.href=("/Viewer/Video?path="+encodeURIComponent(previewPath)); + return; + case 'edit': + if(window.isSharePage){ + window.location.href=("/Viewer/Markdown?share=true&shareKey="+window.shareInfo.shareId+"&path="+encodeURIComponent(previewPath)); + return; + } + window.location.href=("/Viewer/Markdown?path="+encodeURIComponent(previewPath)); + return; + default: + window.open(window.apiURL.download+"?action=download&path="+encodeURIComponent(previewPath)); + return; + } + + } + + enterFolder = ()=>{ + this.props.navitateTo(this.props.path==="/"?this.props.path+this.props.file.name:this.props.path+"/"+this.props.file.name ); + } + + render() { + + const { classes } = this.props; + + if(this.props.viewMethod === "list"){ + return ( + + ); + } + + return ( +
+
+ {(this.props.file.type==="dir" &&this.props.viewMethod !== "list") && + + } + {((this.props.file.type==="file") && this.props.viewMethod === "icon") && + + } + {((this.props.file.type==="file") && this.props.viewMethod === "smallIcon") && + + } +
+
+ ); + } +} + +ObjectCompoment.propTypes = { + classes: PropTypes.object.isRequired, + file: PropTypes.object.isRequired, +}; + + +const ObjectIcon = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(ObjectCompoment)) + +export default ObjectIcon \ No newline at end of file diff --git a/src/component/FileManager/PathSelector.js b/src/component/FileManager/PathSelector.js new file mode 100644 index 0000000..cd318bc --- /dev/null +++ b/src/component/FileManager/PathSelector.js @@ -0,0 +1,157 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import MenuList from '@material-ui/core/MenuList'; +import MenuItem from '@material-ui/core/MenuItem'; +import IconButton from '@material-ui/core/IconButton'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import FolderIcon from '@material-ui/icons/Folder' +import { withStyles } from '@material-ui/core/styles'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import RightIcon from "@material-ui/icons/KeyboardArrowRight" +import UpIcon from "@material-ui/icons/ArrowUpward" +import { connect } from 'react-redux' +import classNames from 'classnames'; +import { + toggleSnackbar, +} from "../../actions/index" +import axios from 'axios' + +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + + +const styles = theme => ({ + iconWhite:{ + color: theme.palette.common.white, + }, + selected: { + backgroundColor: theme.palette.primary.main+"!important", + '& $primary, & $icon': { + color: theme.palette.common.white, + }, + }, + primary: {}, + icon: {}, + buttonIcon:{}, + selector:{ + minWidth: "300px", + }, + container:{ + maxHeight: "330px", + overflowY:" auto", + } +}) + + +class PathSelectorCompoment extends Component { + + state={ + presentPath:"/", + dirList:[], + selectedTarget:null, + } + + componentDidMount= ()=>{ + let toBeLoad = this.props.presentPath; + this.enterFolder(toBeLoad); + } + + back = ()=>{ + let paths = this.state.presentPath.split("/"); + paths.pop(); + let toBeLoad = paths.join("/"); + this.enterFolder(toBeLoad===""?"/":toBeLoad); + } + + enterFolder = (toBeLoad)=>{ + axios.post('/File/ListFile', { + action: 'list', + path: toBeLoad, + }) + .then( (response)=> { + var dirList = response.data.result.filter( (x)=> { + return (x.type === "dir" && (this.props.selected.findIndex((value)=>{ + return (value.name === x.name )&&(value.path === x.path); + }))===-1); + }); + if(toBeLoad ==="/"){ + dirList.unshift({name:"/",path:""}) + } + this.setState({ + presentPath:toBeLoad, + dirList:dirList, + selectedTarget:null, + }) + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message,"warning"); + }); + } + + handleSelect = (index) =>{ + this.setState({selectedTarget:index}); + this.props.onSelect(this.state.dirList[index]); + } + + render() { + + const { classes} = this.props; + + return ( +
+ + {this.state.presentPath!=="/"&& + + + + + + + } + {this.state.dirList.map((value,index)=>( + this.handleSelect(index)}> + + + + + + this.enterFolder(value.path === "/"?value.path+value.name:value.path+"/"+value.name)}> + + + + + ))} + + + +
+ ); + } +} + +PathSelectorCompoment.propTypes = { + classes: PropTypes.object.isRequired, + presentPath:PropTypes.string.isRequired, + selected:PropTypes.array.isRequired, +}; + +export default connect( + mapStateToProps, + mapDispatchToProps + +)(withStyles(styles)(PathSelectorCompoment)) \ No newline at end of file diff --git a/src/component/FileManager/SmallIcon.js b/src/component/FileManager/SmallIcon.js new file mode 100644 index 0000000..b6dedfd --- /dev/null +++ b/src/component/FileManager/SmallIcon.js @@ -0,0 +1,257 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' + +import { withStyles } from '@material-ui/core/styles'; + +import ButtonBase from '@material-ui/core/ButtonBase'; +import Typography from '@material-ui/core/Typography'; +import classNames from 'classnames'; +import Tooltip from '@material-ui/core/Tooltip'; +import ImageIcon from '@material-ui/icons/PhotoSizeSelectActual' +import VideoIcon from '@material-ui/icons/Videocam' +import AudioIcon from '@material-ui/icons/Audiotrack' +import PdfIcon from "@material-ui/icons/PictureAsPdf" +import {FileWordBox,FilePowerpointBox,FileExcelBox,ScriptText,MagnetOn,ZipBox,WindowRestore,Android} from 'mdi-material-ui' +import FileShowIcon from "@material-ui/icons/InsertDriveFile" +import {mediaType} from "../../config" + +const styles = theme => ({ + container: { + padding: "7px", + }, + + selected: { + "&:hover": { + border: "1px solid #d0d0d0", + }, + backgroundColor: theme.palette.explorer.bgSelected, + + }, + + notSelected: { + "&:hover": { + backgroundColor: "#f9f9f9", + border: "1px solid #d0d0d0", + }, + backgroundColor: theme.palette.background.paper, + }, + + button: { + height: "50px", + border: "1px solid #dadce0", + width: "100%", + borderRadius: "6px", + boxSizing: "border-box", + transition: "background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms", + display: "flex", + justifyContent: "left", + alignItems: "initial", + }, + icon: { + margin: "10px 10px 10px 16px", + height: "30px", + minWidth: "30px", + backgroundColor: theme.palette.background.paper, + borderRadius: "90%", + paddingTop: "2px", + color: theme.palette.explorer.icon, + }, + folderNameSelected: { + color: theme.palette.primary.dark, + fontWeight: "500", + }, + folderNameNotSelected: { + color: theme.palette.explorer.filename, + }, + folderName: { + marginTop: "15px", + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden", + marginRight: "20px", + }, + iconImg : { + color:"#d32f2f", + }, + iconImgBig:{ + color:"#d32f2f", + fontSize: "50px", + }, + iconVideo : { + color:"#d50000", + }, + iconVideoBig:{ + color:"#d50000", + fontSize: "50px", + }, + iconAudio : { + color:"#651fff", + }, + iconAudioBig:{ + color:"#651fff", + fontSize: "50px", + }, + iconPdf : { + color:"#f44336", + }, + iconPdfBig:{ + color:"#f44336", + fontSize: "50px", + }, + iconWord : { + color:"#538ce5", + }, + iconWordBig:{ + color:"#538ce5", + fontSize: "50px", + }, + iconPpt : { + color:"rgb(239, 99, 63)", + }, + iconPptBig:{ + color:"rgb(239, 99, 63)", + fontSize: "50px", + }, + iconExcel : { + color:"#4caf50", + }, + iconExcelBig:{ + color:"#4caf50", + fontSize: "50px", + }, + iconText : { + color:"#607d8b", + }, + iconTextBig:{ + color:"#607d8b", + fontSize: "50px", + }, + iconFile : { + color:"#424242", + }, + iconFileBig:{ + color:"#424242", + fontSize: "50px", + }, + iconTorrent : { + color:"#5c6bc0", + }, + iconTorrentBig:{ + color:"#5c6bc0", + fontSize: "50px", + }, + iconZip : { + color:"#f9a825", + }, + iconZipBig:{ + color:"#f9a825", + fontSize: "50px", + }, + iconAndroid : { + color:"#8bc34a", + }, + iconAndroidBig:{ + color:"#8bc34a", + fontSize: "50px", + }, + iconExe : { + color:"#1a237e", + }, + iconExeBig:{ + color:"#1a237e", + fontSize: "50px", + }, +}) + +const mapStateToProps = state => { + return { + selected: state.explorer.selected, + } +} + +const mapDispatchToProps = dispatch => { + return { + } +} + +class SmallIconCompoment extends Component { + + state = { + } + + + render() { + + const { classes } = this.props; + + let icon; + let fileType =this.props.file.name.split(".").pop().toLowerCase(); + if (mediaType["image"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["video"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["audio"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["pdf"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["word"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["ppt"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["excel"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["text"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["torrent"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["zip"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["excute"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["android"].indexOf(fileType)!==-1){ + icon = (); + }else{ + icon = (); + } + + + const isSelected = (this.props.selected.findIndex((value) => { + return value === this.props.file; + })) !== -1; + + return ( + +
{icon}
+ + {this.props.file.name} + +
+ ); + } +} + +SmallIconCompoment.propTypes = { + classes: PropTypes.object.isRequired, + file: PropTypes.object.isRequired, +}; + + +const SmallIcon = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(SmallIconCompoment)) + +export default SmallIcon \ No newline at end of file diff --git a/src/component/FileManager/TableRow.js b/src/component/FileManager/TableRow.js new file mode 100644 index 0000000..c66e65e --- /dev/null +++ b/src/component/FileManager/TableRow.js @@ -0,0 +1,232 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' + +import { withStyles } from '@material-ui/core/styles'; + +import TableCell from '@material-ui/core/TableCell'; +import TableRow from '@material-ui/core/TableRow'; +import Typography from '@material-ui/core/Typography'; +import FolderIcon from '@material-ui/icons/Folder' +import classNames from 'classnames'; +import ImageIcon from '@material-ui/icons/PhotoSizeSelectActual' +import VideoIcon from '@material-ui/icons/Videocam' +import AudioIcon from '@material-ui/icons/Audiotrack' +import PdfIcon from "@material-ui/icons/PictureAsPdf" +import {FileWordBox,FilePowerpointBox,FileExcelBox,ScriptText,MagnetOn,ZipBox,WindowRestore,Android} from 'mdi-material-ui' +import FileShowIcon from "@material-ui/icons/InsertDriveFile" +import {sizeToString} from "../../untils/index" +import {mediaType} from "../../config" + +const styles = theme => ({ + selected: { + "&:hover": { + }, + backgroundColor: theme.palette.explorer.bgSelected, + + }, + + notSelected: { + "&:hover": { + backgroundColor: "#ececec", + }, + }, + icon:{ + verticalAlign: "middle", + marginRight: "20px", + color: theme.palette.explorer.icon, + }, + iconImg : { + verticalAlign: "middle", + color:"#d32f2f", + marginRight: "20px", + }, + iconVideo : { + verticalAlign: "middle", + color:"#d50000", + marginRight: "20px", + }, + iconAudio : { + color:"#651fff", + marginRight: "20px", + verticalAlign: "middle", + }, + iconPdf : { + verticalAlign: "middle", + color:"#f44336", + marginRight: "20px", + }, + iconWord : { + verticalAlign: "middle", + color:"#538ce5", + marginRight: "20px", + }, + iconPpt : { + verticalAlign: "middle", + color:"rgb(239, 99, 63)", + marginRight: "20px", + }, + iconExcel : { + verticalAlign: "middle", + color:"#4caf50", + marginRight: "20px", + }, + iconText : { + verticalAlign: "middle", + color:"#607d8b", + marginRight: "20px", + }, + iconFile : { + verticalAlign: "middle", + color:"#424242", + marginRight: "20px", + }, + iconTorrent : { + color:"#5c6bc0", + marginRight: "20px", + verticalAlign: "middle", + }, + iconZip : { + color:"#f9a825", + marginRight: "20px", + verticalAlign: "middle", + }, + iconAndroid : { + color:"#8bc34a", + marginRight: "20px", + verticalAlign: "middle", + }, + iconExe : { + color:"#1a237e", + marginRight: "20px", + verticalAlign: "middle", + }, + folderNameSelected: { + color: theme.palette.primary.dark, + fontWeight: "500", + userSelect: "none", + }, + folderNameNotSelected: { + color: theme.palette.explorer.filename, + userSelect: "none", + }, + folderName:{ + marginRight:"20px", + }, + hideAuto:{ + [theme.breakpoints.down('sm')]: { + display:"none", + } + } +}) + +const mapStateToProps = state => { + return { + selected: state.explorer.selected, + } +} + +const mapDispatchToProps = dispatch => { + return { + } +} + + +class TableRowCompoment extends Component { + + state = { + } + + + render() { + + const { classes } = this.props; + + let icon; + let fileType =this.props.file.name.split(".").pop().toLowerCase(); + if(this.props.file.type==="dir"){ + icon = (); + }else{ + if (mediaType["image"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["video"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["audio"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["pdf"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["word"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["ppt"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["excel"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["text"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["torrent"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["zip"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["excute"].indexOf(fileType)!==-1){ + icon = (); + }else if(mediaType["android"].indexOf(fileType)!==-1){ + icon = (); + }else{ + icon = (); + } + } + + + + const isSelected = (this.props.selected.findIndex((value) => { + return value === this.props.file; + })) !== -1; + + return ( + + + + {icon}{this.props.file.name} + + + + {this.props.file.type!=="dir"&&sizeToString(this.props.file.size)} + + + {this.props.file.date} + + + + ); + } +} + +TableRowCompoment.propTypes = { + classes: PropTypes.object.isRequired, + file: PropTypes.object.isRequired, +}; + + +const TableItem = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(TableRowCompoment)) + +export default TableItem \ No newline at end of file diff --git a/src/component/LockedFile.js b/src/component/LockedFile.js new file mode 100644 index 0000000..2826bda --- /dev/null +++ b/src/component/LockedFile.js @@ -0,0 +1,140 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Button from '@material-ui/core/Button'; +import Card from '@material-ui/core/Card'; +import Divider from '@material-ui/core/Divider'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import CardActions from '@material-ui/core/CardActions'; +import TextField from '@material-ui/core/TextField'; +import Avatar from '@material-ui/core/Avatar'; +import { toggleSnackbar,}from "../actions/index" +import axios from 'axios' +const styles = theme => ({ + card: { + maxWidth: 400, + margin: "0 auto", + }, + actions: { + display: 'flex', + }, + layout: { + width: 'auto', + marginTop:'110px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 1100, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + continue:{ + marginLeft:"auto", + marginRight: "10px", + marginRottom: "10px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +class LockedFileCompoment extends Component { + + state = { + pwd:"", + loading:false, + }; + + handleChange = name => event => { + this.setState({ [name]: event.target.value }); + }; + + submit = e=>{ + e.preventDefault(); + if(this.state.pwd===""){ + return; + } + this.setState({ + loading:true, + }); + axios.post("/Share/chekPwd",{ + key: window.shareInfo.shareId, + password: this.state.pwd + }) + .then( (response)=> { + if(response.data.error!==0){ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",response.data.msg ,"warning"); + }else{ + window.location.reload(); + } + + }) + .catch((error) =>{ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",error.message ,"error"); + }); + } + + render() { + const { classes } = this.props; + + + return ( +
+ + + + } + title={window.shareInfo.ownerNick+" 的加密分享"} + subheader={window.shareInfo.shareDate} + /> + + +
+ + +
+ + + +
+
+ ); + } + +} + +const LockedFile = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(LockedFileCompoment)) + +export default LockedFile diff --git a/src/component/Login/EmailActivication.js b/src/component/Login/EmailActivication.js new file mode 100644 index 0000000..9066df6 --- /dev/null +++ b/src/component/Login/EmailActivication.js @@ -0,0 +1,113 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Button from '@material-ui/core/Button'; +import EmailIcon from '@material-ui/icons/EmailOutlined'; +import Paper from '@material-ui/core/Paper'; +import Avatar from '@material-ui/core/Avatar'; +import { toggleSnackbar, } from "../../actions/index" +import Typography from '@material-ui/core/Typography'; + +const styles = theme => ({ + layout: { + width: 'auto', + marginTop: '110px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 400, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + paper: { + marginTop: theme.spacing.unit * 8, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`, + }, + avatar: { + margin: theme.spacing.unit, + backgroundColor: theme.palette.secondary.main, + }, + avatarSuccess:{ + margin: theme.spacing.unit, + backgroundColor: theme.palette.primary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing.unit, + }, + submit: { + marginTop: theme.spacing.unit * 3, + }, + link: { + marginTop: "10px", + display:"flex", + width: "100%", + justifyContent: "space-between", + }, + captchaContainer:{ + display:"flex", + marginTop: "10px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar: (vertical, horizontal, msg, color) => { + dispatch(toggleSnackbar(vertical, horizontal, msg, color)) + }, + } +} + +class EmailActivicationCompoment extends Component { + + state={ + } + + + render() { + const { classes } = this.props; + + + return ( +
+ + + + + + 激活成功 + + 您的账号已被成功激活。 + + + + +
+ ); + } + +} + +const EmailActivication = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(EmailActivicationCompoment)) + +export default EmailActivication diff --git a/src/component/Login/LoginForm.js b/src/component/Login/LoginForm.js new file mode 100644 index 0000000..b0dea47 --- /dev/null +++ b/src/component/Login/LoginForm.js @@ -0,0 +1,216 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Button from '@material-ui/core/Button'; +import FormControl from '@material-ui/core/FormControl'; +import Divider from '@material-ui/core/Divider'; +import Link from '@material-ui/core/Link'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import LockOutlinedIcon from '@material-ui/icons/LockOutlined'; +import Paper from '@material-ui/core/Paper'; +import Avatar from '@material-ui/core/Avatar'; +import { toggleSnackbar, } from "../../actions/index" +import Typography from '@material-ui/core/Typography'; +import axios from 'axios' +const styles = theme => ({ + layout: { + width: 'auto', + marginTop: '110px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 400, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + paper: { + marginTop: theme.spacing.unit * 8, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`, + }, + avatar: { + margin: theme.spacing.unit, + backgroundColor: theme.palette.secondary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing.unit, + }, + submit: { + marginTop: theme.spacing.unit * 3, + }, + link: { + marginTop: "10px", + display:"flex", + width: "100%", + justifyContent: "space-between", + }, + captchaContainer:{ + display:"flex", + marginTop: "10px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar: (vertical, horizontal, msg, color) => { + dispatch(toggleSnackbar(vertical, horizontal, msg, color)) + }, + } +} + +class LoginFormCompoment extends Component { + + state={ + email:"", + pwd:"", + captcha:"", + loading:false, + captchaUrl:"/captcha?initial", + } + + refreshCaptcha = ()=>{ + this.setState({ + captchaUrl:"/captcha?"+Math.random(), + }); + } + + login = e=>{ + e.preventDefault(); + this.setState({ + loading:true, + }); + axios.post('/Member/Login',{ + userMail:this.state.email, + userPass:this.state.pwd, + captchaCode:this.state.captcha, + }).then( (response)=> { + if(response.data.code!=="200"){ + this.setState({ + loading:false, + }); + if(response.data.message==="tsp"){ + window.location.href="/Member/TwoStep"; + }else{ + this.props.toggleSnackbar("top","right",response.data.message,"warning"); + this.refreshCaptcha(); + } + }else{ + this.setState({ + loading:false, + }); + window.location.href="/Home"; + this.props.toggleSnackbar("top","right","登录成功","success"); + } + }) + .catch((error) =>{ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",error.message,"error"); + + + }); + } + + handleChange = name => event => { + this.setState({ [name]: event.target.value }); + }; + + + render() { + const { classes } = this.props; + + + return ( +
+ + + + + + 登录{window.siteInfo.mainTitle} + +
+ + 电子邮箱 + + + + 密码 + + + {window.captcha==="1"&& +
+ + 验证码 + + +
+ captcha +
+
+ } +
+
+
+ + 忘记密码 + +
+
+ + 注册账号 + +
+
+ +
+
+ ); + } + +} + +const LoginForm = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(LoginFormCompoment)) + +export default LoginForm diff --git a/src/component/Login/RegisterForm.js b/src/component/Login/RegisterForm.js new file mode 100644 index 0000000..97d5e03 --- /dev/null +++ b/src/component/Login/RegisterForm.js @@ -0,0 +1,258 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Button from '@material-ui/core/Button'; +import FormControl from '@material-ui/core/FormControl'; +import Divider from '@material-ui/core/Divider'; +import Link from '@material-ui/core/Link'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import RegIcon from '@material-ui/icons/AssignmentIndOutlined'; +import EmailIcon from '@material-ui/icons/EmailOutlined'; +import Paper from '@material-ui/core/Paper'; +import Avatar from '@material-ui/core/Avatar'; +import { toggleSnackbar, } from "../../actions/index" +import Typography from '@material-ui/core/Typography'; +import axios from 'axios' + +const styles = theme => ({ + layout: { + width: 'auto', + marginTop: '110px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 400, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + paper: { + marginTop: theme.spacing.unit * 8, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`, + }, + avatar: { + margin: theme.spacing.unit, + backgroundColor: theme.palette.secondary.main, + }, + avatarSuccess:{ + margin: theme.spacing.unit, + backgroundColor: theme.palette.primary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing.unit, + }, + submit: { + marginTop: theme.spacing.unit * 3, + }, + link: { + marginTop: "10px", + display:"flex", + width: "100%", + justifyContent: "space-between", + }, + captchaContainer:{ + display:"flex", + marginTop: "10px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar: (vertical, horizontal, msg, color) => { + dispatch(toggleSnackbar(vertical, horizontal, msg, color)) + }, + } +} + +const sleep= (time)=> { + return new Promise((resolve) => setTimeout(resolve, time)); +} + +class RegisterFormCompoment extends Component { + + state={ + email:"", + pwd:"", + pwdRepeat:"", + captcha:"", + loading:false, + captchaUrl:"/captcha?initial", + showStatue:"loginForm", + } + + refreshCaptcha = ()=>{ + this.setState({ + captchaUrl:"/captcha?"+Math.random(), + }); + } + + register = e=>{ + e.preventDefault(); + if(this.state.pwdRepeat !== this.state.pwd){ + this.props.toggleSnackbar("top","right","两次密码输入不一致","warning"); + return; + } + this.setState({ + loading:true, + }); + axios.post('/Member/Register',{ + "username-reg":this.state.email, + "password-reg":this.state.pwd, + captchaCode:this.state.captcha, + }).then( (response)=> { + if(response.data.code!=="200"){ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",response.data.message,"warning"); + this.refreshCaptcha(); + }else{ + if(response.data.message==="ec"){ + this.setState({ + showStatue:"email", + }); + }else{ + this.props.toggleSnackbar("top","right","注册成功","success"); + sleep(1000).then(() => { + window.location.href="/Home"; + this.setState({ + loading:false, + }); + }) + } + } + }) + .catch((error) =>{ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",error.message,"error"); + + + }); + } + + handleChange = name => event => { + this.setState({ [name]: event.target.value }); + }; + + + render() { + const { classes } = this.props; + + + return ( +
+ {this.state.showStatue==="loginForm"&& + + + + + 注册{window.siteInfo.mainTitle} + +
+ + 电子邮箱 + + + + 密码 + + + + 密码 + + + {window.regCaptcha==="1"&& +
+ + 验证码 + + +
+ captcha +
+
+ } +
+
+
+ + 返回登录 + +
+
+ + 忘记密码 + +
+
+ +
} + + {this.state.showStatue==="email"&& + + + + + 邮件激活 + + 一封激活邮件已经发送至您的邮箱,请访问邮件中的链接以继续完成注册。 + + } + +
+ ); + } + +} + +const RegisterForm = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(RegisterFormCompoment)) + +export default RegisterForm diff --git a/src/component/Login/ResetPwd.js b/src/component/Login/ResetPwd.js new file mode 100644 index 0000000..4643a84 --- /dev/null +++ b/src/component/Login/ResetPwd.js @@ -0,0 +1,200 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Button from '@material-ui/core/Button'; +import FormControl from '@material-ui/core/FormControl'; +import Divider from '@material-ui/core/Divider'; +import Link from '@material-ui/core/Link'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import KeyIcon from '@material-ui/icons/VpnKeyOutlined'; +import Paper from '@material-ui/core/Paper'; +import Avatar from '@material-ui/core/Avatar'; +import { toggleSnackbar, } from "../../actions/index" +import Typography from '@material-ui/core/Typography'; +import axios from 'axios' +const styles = theme => ({ + layout: { + width: 'auto', + marginTop: '110px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 400, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + paper: { + marginTop: theme.spacing.unit * 8, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`, + }, + avatar: { + margin: theme.spacing.unit, + backgroundColor: theme.palette.secondary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing.unit, + }, + submit: { + marginTop: theme.spacing.unit * 3, + }, + link: { + marginTop: "10px", + display:"flex", + width: "100%", + justifyContent: "space-between", + }, + captchaContainer:{ + display:"flex", + marginTop: "10px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar: (vertical, horizontal, msg, color) => { + dispatch(toggleSnackbar(vertical, horizontal, msg, color)) + }, + } +} + +class ResetPwdCompoment extends Component { + + state={ + email:"", + captcha:"", + loading:false, + captchaUrl:"/captcha?initial", + } + + refreshCaptcha = ()=>{ + this.setState({ + captchaUrl:"/captcha?"+Math.random(), + }); + } + + login = e=>{ + e.preventDefault(); + this.setState({ + loading:true, + }); + axios.post('/Member/ForgetPwd',{ + regEmail:this.state.email, + captchaCode:this.state.captcha, + }).then( (response)=> { + if(response.data.code!=="200"){ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",response.data.message,"warning"); + this.refreshCaptcha(); + }else{ + this.setState({ + loading:false, + email:"", + }); + this.props.toggleSnackbar("top","right","密码重置邮件已发送,请注意查收","success"); + } + }) + .catch((error) =>{ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",error.message,"error"); + + + }); + } + + handleChange = name => event => { + this.setState({ [name]: event.target.value }); + }; + + + render() { + const { classes } = this.props; + + + return ( +
+ + + + + + 找回密码 + +
+ + 注册邮箱 + + + {window.findPwdCaptcha==="1"&& +
+ + 验证码 + + +
+ captcha +
+
+ } +
+
+
+ + 返回登录 + +
+
+ + 注册账号 + +
+
+ +
+
+ ); + } + +} + +const ResetPwd = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(ResetPwdCompoment)) + +export default ResetPwd diff --git a/src/component/Login/ResetPwdForm.js b/src/component/Login/ResetPwdForm.js new file mode 100644 index 0000000..eb1f150 --- /dev/null +++ b/src/component/Login/ResetPwdForm.js @@ -0,0 +1,191 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Button from '@material-ui/core/Button'; +import FormControl from '@material-ui/core/FormControl'; +import Divider from '@material-ui/core/Divider'; +import Link from '@material-ui/core/Link'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import KeyIcon from '@material-ui/icons/VpnKeyOutlined'; +import Paper from '@material-ui/core/Paper'; +import Avatar from '@material-ui/core/Avatar'; +import { toggleSnackbar, } from "../../actions/index" +import Typography from '@material-ui/core/Typography'; +import axios from 'axios' +const styles = theme => ({ + layout: { + width: 'auto', + marginTop: '110px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 400, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + paper: { + marginTop: theme.spacing.unit * 8, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`, + }, + avatar: { + margin: theme.spacing.unit, + backgroundColor: theme.palette.secondary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing.unit, + }, + submit: { + marginTop: theme.spacing.unit * 3, + }, + link: { + marginTop: "10px", + display:"flex", + width: "100%", + justifyContent: "space-between", + }, + captchaContainer:{ + display:"flex", + marginTop: "10px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar: (vertical, horizontal, msg, color) => { + dispatch(toggleSnackbar(vertical, horizontal, msg, color)) + }, + } +} + +class ResetPwdFormCompoment extends Component { + + state={ + pwd:"", + pwdRepeat:"", + loading:false, + } + + login = e=>{ + e.preventDefault(); + if(this.state.pwdRepeat !== this.state.pwd){ + this.props.toggleSnackbar("top","right","两次密码输入不一致","warning"); + return; + } + this.setState({ + loading:true, + }); + axios.post('/Member/Reset',{ + pwd:this.state.pwd, + key:window.resetKey, + }).then( (response)=> { + if(response.data.code!=="200"){ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",response.data.message,"warning"); + }else{ + this.setState({ + loading:false, + pwd:"", + pwdRepeat:"", + }); + this.props.toggleSnackbar("top","right","密码重设成功","success"); + } + }) + .catch((error) =>{ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",error.message,"error"); + + + }); + } + + handleChange = name => event => { + this.setState({ [name]: event.target.value }); + }; + + + render() { + const { classes } = this.props; + + + return ( +
+ + + + + + 找回密码 + +
+ + 新密码 + + + + 重复新密码 + + +
+
+
+ + 返回登录 + +
+
+ + 注册账号 + +
+
+ +
+
+ ); + } + +} + +const ResetPwdForm = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(ResetPwdFormCompoment)) + +export default ResetPwdForm diff --git a/src/component/Login/TwoStep.js b/src/component/Login/TwoStep.js new file mode 100644 index 0000000..c6ba2e9 --- /dev/null +++ b/src/component/Login/TwoStep.js @@ -0,0 +1,160 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Button from '@material-ui/core/Button'; +import FormControl from '@material-ui/core/FormControl'; +import Divider from '@material-ui/core/Divider'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import VpnIcon from '@material-ui/icons/VpnKeyOutlined'; +import Paper from '@material-ui/core/Paper'; +import Avatar from '@material-ui/core/Avatar'; +import { toggleSnackbar, } from "../../actions/index" +import Typography from '@material-ui/core/Typography'; +import axios from 'axios' +const styles = theme => ({ + layout: { + width: 'auto', + marginTop: '110px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 400, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + paper: { + marginTop: theme.spacing.unit * 8, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`, + }, + avatar: { + margin: theme.spacing.unit, + backgroundColor: theme.palette.secondary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing.unit, + }, + submit: { + marginTop: theme.spacing.unit * 3, + }, + link: { + marginTop: "10px", + display:"flex", + width: "100%", + justifyContent: "space-between", + }, + captchaContainer:{ + display:"flex", + marginTop: "10px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar: (vertical, horizontal, msg, color) => { + dispatch(toggleSnackbar(vertical, horizontal, msg, color)) + }, + } +} + +class TwoStepCompoment extends Component { + + state={ + code:"", + loading:false, + } + + login = e=>{ + e.preventDefault(); + this.setState({ + loading:true, + }); + axios.post('/Member/TwoStepCheck',{ + code:this.state.code, + }).then( (response)=> { + if(response.data.code!=="200"){ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right","验证代码不正确","warning"); + }else{ + this.setState({ + loading:false, + }); + window.location.href="/Home"; + this.props.toggleSnackbar("top","right","登录成功","success"); + } + }) + .catch((error) =>{ + this.setState({ + loading:false, + }); + this.props.toggleSnackbar("top","right",error.message,"error"); + + + }); + } + + handleChange = name => event => { + this.setState({ [name]: event.target.value }); + }; + + + render() { + const { classes } = this.props; + + + return ( +
+ + + + + + 二步验证 + +
+ + 请输入六位二步验证代码 + + +
+ +
+
+ ); + } + +} + +const TwoStep = connect( + mapStateToProps, + mapDispatchToProps +)(withStyles(styles)(TwoStepCompoment)) + +export default TwoStep diff --git a/src/component/MyShare.js b/src/component/MyShare.js new file mode 100644 index 0000000..3ce9add --- /dev/null +++ b/src/component/MyShare.js @@ -0,0 +1,270 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Tooltip from '@material-ui/core/Tooltip'; +import Card from '@material-ui/core/Card'; +import Avatar from '@material-ui/core/Avatar'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardActions from '@material-ui/core/CardActions'; +import { toggleSnackbar,}from "../actions/index" +import Typography from '@material-ui/core/Typography'; +import axios from 'axios' +import Grid from '@material-ui/core/Grid'; +import IconButton from '@material-ui/core/IconButton'; +import OpenIcon from '@material-ui/icons/OpenInNew'; +import FileIcon from '@material-ui/icons/InsertDriveFile'; +import FolderIcon from '@material-ui/icons/Folder'; +import LockIcon from '@material-ui/icons/Lock'; +import UnlockIcon from '@material-ui/icons/LockOpen'; +import EyeIcon from '@material-ui/icons/RemoveRedEye'; +import DeleteIcon from '@material-ui/icons/Delete'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: "0 auto", + }, + actions: { + display: 'flex', + }, + layout: { + width: 'auto', + marginTop:'50px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 1100, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + shareTitle:{ + maxWidth:"200px", + }, + avatarFile:{ + backgroundColor: theme.palette.primary.light, + }, + avatarFolder:{ + backgroundColor: theme.palette.secondary.light, + }, + gird:{ + marginTop:"30px", + }, + loadMore:{ + textAlign:"center", + marginTop:"20px", + marginBottom:"20px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +class MyShareCompoment extends Component { + + state={ + page:1, + shareList:[], + showPwd:null, + } + + componentDidMount = ()=>{ + this.loadList(1); + } + + loadMore = ()=>{ + this.loadList(this.state.page+1); + } + + showPwd = (pwd)=>{ + this.setState({showPwd:pwd}); + } + + handleClose = ()=>{ + this.setState({showPwd:null}); + } + + removeShare = id=>{ + axios.post('/Share/Delete', { + id:id, + }).then( (response)=> { + if(response.data.error!==1){ + let oldList = this.state.shareList; + oldList = oldList.filter(value=>{ + return value.share_key !== id; + }); + this.setState({ + shareList:oldList + }); + this.props.toggleSnackbar("top","right","分享已取消","success"); + } + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right","请求失败","error"); + }); + } + + changePermission = id=>{ + axios.post('/Share/ChangePromission', { + id:id, + }).then( (response)=> { + if(response.data.error!==1){ + let oldList = this.state.shareList; + let shareIndex = oldList.findIndex(value=>{ + return value.share_key === id; + }); + oldList[shareIndex].type = (oldList[shareIndex].type==="public")?"private":"public"; + oldList[shareIndex].share_pwd = response.data.newPwd; + this.setState({ + shareList:oldList + }); + } + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right","请求失败","error"); + }); + } + + loadList= page=>{ + axios.post('/Share/ListMyShare', { + page:page, + }).then( (response)=> { + if(response.data.length===0){ + this.props.toggleSnackbar("top","right","没有更多了","info"); + } + this.setState({ + page:page, + shareList:this.state.shareList.concat(response.data), + }) + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right","加载失败","error"); + }); + } + + render() { + const { classes } = this.props; + + + return ( +
+ 我的分享 + + {this.state.shareList.map(value=>( + + + + {value.source_type==="file"&& + + + + } + {value.source_type==="dir"&& + + + + } + +
+ } + title={{value.fileData}} + subheader={value.share_time} + /> + + + + window.open("/s/"+value.share_key)}> + + + + {value.type==="private"&& +
+ this.changePermission(value.share_key)}> + + + + + this.showPwd(value.share_pwd)}> + + + + +
+ } + {value.type==="public"&& + this.changePermission(value.share_key)}> + + + + + } + + this.removeShare(value.share_key)}> + + + + + +
+ + + ))} + +
+ +
+ + 分享密码 + + + + + + + + + ); + } + +} + +const MyShare = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(MyShareCompoment)) + +export default MyShare diff --git a/src/component/Navbar.js b/src/component/Navbar.js new file mode 100644 index 0000000..418b5e3 --- /dev/null +++ b/src/component/Navbar.js @@ -0,0 +1,678 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import { withStyles } from '@material-ui/core/styles'; +import { withTheme } from '@material-ui/core/styles'; +import classNames from 'classnames'; +import { connect } from 'react-redux' +import VideoIcon from '@material-ui/icons/VideoLibrary'; +import MusicIcon from '@material-ui/icons/LibraryMusic'; +import ImageIcon from '@material-ui/icons/Collections'; +import AddIcon from '@material-ui/icons/Add'; +import DocIcon from '@material-ui/icons/FileCopy'; +import ShareIcon from '@material-ui/icons/Share'; +import BackIcon from '@material-ui/icons/ArrowBack'; +import OpenIcon from '@material-ui/icons/OpenInNew' +import DownloadIcon from '@material-ui/icons/CloudDownload' +import OpenFolderIcon from '@material-ui/icons/FolderOpen' +import RenameIcon from '@material-ui/icons/BorderColor' +import MoveIcon from '@material-ui/icons/Input' +import DeleteIcon from '@material-ui/icons/Delete' +import Button from '@material-ui/core/Button'; +import Drawer from '@material-ui/core/Drawer'; +import SwipeableDrawer from '@material-ui/core/SwipeableDrawer' +import IconButton from '@material-ui/core/IconButton'; +import Hidden from '@material-ui/core/Hidden'; +import Avatar from '@material-ui/core/Avatar'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import UploadIcon from '@material-ui/icons/CloudUpload'; +import FolderShared from '@material-ui/icons/FolderShared'; +import SaveIcon from '@material-ui/icons/Save'; +import List from '@material-ui/core/List'; +import MenuIcon from '@material-ui/icons/Menu'; +import Badge from '@material-ui/core/Badge'; +import Grow from '@material-ui/core/Grow'; +import Tooltip from '@material-ui/core/Tooltip'; +import {isPreviewable} from "../config" +import { + drawerToggleAction, + setSelectedTarget, + navitateTo, + openCreateFolderDialog, + changeContextMenu, + searchMyFile, + saveFile, + openMusicDialog, + showImgPreivew, + toggleSnackbar, + openMoveDialog, + openRemoveDialog, + openShareDialog, + openRenameDialog, +} from "../actions/index" +import {allowSharePreview,checkGetParameters,changeThemeColor} from "../untils/index" +import Uploader from "./Uploader.js" +import {sizeToString} from "../untils/index" +import SezrchBar from "./SearchBar" +import StorageBar from "./StorageBar" +import UserAvatar from "./UserAvatar" +import UserInfo from "./UserInfo" +import { + AccountArrowRight, + AccountPlus +} from 'mdi-material-ui' +const drawerWidth = 240; +const drawerWidthMobile = 270; + +const mapStateToProps = state => { + return { + desktopOpen: state.viewUpdate.open, + selected:state.explorer.selected, + isMultiple:state.explorer.selectProps.isMultiple, + withFolder:state.explorer.selectProps.withFolder, + withFile:state.explorer.selectProps.withFile, + path:state.navigator.path, + keywords:state.explorer.keywords, + } +} + +const mapDispatchToProps = dispatch => { + return { + handleDesktopToggle: open => { + dispatch(drawerToggleAction(open)) + }, + setSelectedTarget:targets=>{ + dispatch(setSelectedTarget(targets)) + }, + navitateTo:path => { + dispatch(navitateTo(path)) + }, + openCreateFolderDialog:()=>{ + dispatch(openCreateFolderDialog()) + }, + changeContextMenu:(type,open)=>{ + dispatch(changeContextMenu(type,open)) + }, + searchMyFile:(keywords)=>{ + dispatch(searchMyFile(keywords)); + }, + saveFile:()=>{ + dispatch(saveFile()) + }, + openMusicDialog:()=>{ + dispatch(openMusicDialog()) + }, + showImgPreivew:(first)=>{ + dispatch(showImgPreivew(first)) + }, + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + openRenameDialog:()=>{ + dispatch(openRenameDialog()) + }, + openMoveDialog:()=>{ + dispatch(openMoveDialog()) + }, + openRemoveDialog:()=>{ + dispatch(openRemoveDialog()) + }, + openShareDialog:()=>{ + dispatch(openShareDialog()) + }, + } +} + +const styles = theme => ({ + appBar: { + marginLeft: drawerWidth, + [theme.breakpoints.down('xs')]: { + marginLeft: drawerWidthMobile, + }, + zIndex: theme.zIndex.drawer + 1, + transition:" background-color 250ms" , + }, + + drawer: { + width: 0, + flexShrink: 0, + }, + drawerDesktop:{ + width: drawerWidth, + flexShrink: 0, + }, + icon: { + marginRight: theme.spacing.unit * 2, + }, + menuButton: { + marginRight: 20, + [theme.breakpoints.up('sm')]: { + display: 'none', + }, + }, + menuButtonDesktop:{ + marginRight: 20, + [theme.breakpoints.down('sm')]: { + display: 'none', + }, + }, + menuIcon:{ + marginRight: 20, + }, + toolbar: theme.mixins.toolbar, + drawerPaper:{ + width:drawerWidthMobile, + }, + drawerOpen: { + width: drawerWidth, + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + drawerClose: { + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + overflowX: 'hidden', + width: 0, + + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 3, + }, + hiddenButton: { + display: "none", + }, + grow: { + flexGrow: 1, + }, + badge: { + top: 1, + right:-15, + + }, + nested: { + paddingLeft: theme.spacing.unit * 4, + }, + sectionForFile:{ + + + display: 'flex', + + }, + extendedIcon: { + marginRight: theme.spacing.unit, + }, + addButton:{ + marginLeft: "40px", + marginTop: "25px", + marginBottom: "15px", + }, + fabButton:{ + borderRadius:"100px", + }, + badgeFix:{ + right:"10px", + }, + iconFix:{ + marginLeft: "16px", + }, + dividerFix:{ + marginTop: "8px", + }, + folderShareIcon:{ + verticalAlign: "sub", + marginRight: "5px", + }, + shareInfoContainer:{ + display:"flex", + marginTop: "15px", + marginBottom: "20px", + marginLeft: "28px", + textDecoration:"none", + }, + shareAvatar:{ + width:"40px", + height:"40px", + }, + stickFooter:{ + bottom: "0px", + position: "absolute", + backgroundColor:theme.palette.background.paper, + width: "100%", + }, + ownerInfo:{ + marginLeft: "10px", + width: "150px", + } +}); +class NavbarCompoment extends Component { + + constructor(props) { + super(props); + this.state = { + mobileOpen: false, + queued: 0, + }; + this.UploaderRef = React.createRef(); + } + + componentDidMount=()=>{ + changeThemeColor((this.props.selected.length <=1 && ! (!this.props.isMultiple&&this.props.withFile))?this.props.theme.palette.primary.main:this.props.theme.palette.background.default); + } + + componentWillReceiveProps = (nextProps)=>{ + if((this.props.selected.length <=1 && ! (!this.props.isMultiple&&this.props.withFile))!==(nextProps.selected.length <=1 && ! (!nextProps.isMultiple&&nextProps.withFile))){ + changeThemeColor(!(this.props.selected.length <=1 && ! (!this.props.isMultiple&&this.props.withFile))?this.props.theme.palette.primary.main:this.props.theme.palette.background.default); + } + } + + handleDrawerToggle = () => { + this.setState(state => ({ mobileOpen: !state.mobileOpen })); + }; + + clickUpload = () => { + if (this.state.queued === 0) { + //document.getElementsByClassName("uploadForm")[0].click(); + this.props.changeContextMenu("empty",true); + } else { + this.UploaderRef.current.getWrappedInstance().openFileList(); + } + } + + updateQueueStatus = (queued) => { + this.setState({ queued: queued }); + } + + loadUploader() { + if (window.isHomePage) { + return ( this.updateQueueStatus(queued)} ref={this.UploaderRef} />) + } + } + + filterFile = (type)=>{ + this.props.searchMyFile("{filterType:"+type+"}") + } + + openPreview = ()=>{ + if(!allowSharePreview()){ + this.props.toggleSnackbar("top","right","未登录用户无法预览","warning"); + return; + } + this.props.changeContextMenu("file",false); + let previewPath = this.props.selected[0].path === "/" ? this.props.selected[0].path+this.props.selected[0].name:this.props.selected[0].path+"/"+this.props.selected[0].name; + switch(isPreviewable(this.props.selected[0].name)){ + case 'img': + this.props.showImgPreivew(this.props.selected[0]); + return; + case 'msDoc': + window.open(window.apiURL.docPreiview+"/?path="+encodeURIComponent(previewPath)); + return; + case 'audio': + this.props.openMusicDialog(); + return; + case 'open': + window.open(window.apiURL.preview+"/?action=preview&path="+encodeURIComponent(previewPath)); + return; + case 'video': + if(window.isSharePage){ + window.location.href=("/Viewer/Video?share=true&shareKey="+window.shareInfo.shareId+"&path="+encodeURIComponent(previewPath)); + return; + } + window.location.href=("/Viewer/Video?path="+encodeURIComponent(previewPath)); + return; + case 'edit': + if(window.isSharePage){ + window.location.href=("/Viewer/Markdown?share=true&shareKey="+window.shareInfo.shareId+"&path="+encodeURIComponent(previewPath)); + return; + } + window.location.href=("/Viewer/Markdown?path="+encodeURIComponent(previewPath)); + return; + default: + return; + } + } + + openDownload = ()=>{ + if(!allowSharePreview()){ + this.props.toggleSnackbar("top","right","未登录用户无法预览","warning"); + return; + } + let downloadPath = this.props.selected[0].path === "/" ? this.props.selected[0].path+this.props.selected[0].name:this.props.selected[0].path+"/"+this.props.selected[0].name; + window.open(window.apiURL.download+"?action=download&path="+encodeURIComponent(downloadPath)); + } + + render() { + const { classes } = this.props; + + const drawer = ( +
+ {window.isMobile&& + + } + {window.isHomePage&& +
+ + + +
+ } + {(!window.isHomePage&&(window.userInfo.uid!==-1))&& +
+ window.location.href="/"}> + + + + + +
+ } + + {window.isHomePage&&
+ + + + + + + + this.filterFile("video")}> + + + + + + + this.filterFile("image")}> + + + + + + + this.filterFile("audio")}> + + + + + + + this.filterFile("doc")}> + + + + +
} + + {window.userInfo.uid!==-1&&
+ window.location.href="/Share/My"}> + + + + + + window.location.href="/Home/Download"}> + + + + + + {!window.isSharePage&& +
+ +
+ } + +
+ } + {window.userInfo.uid===-1&& +
+ window.location.href="/Login"}> + + + + + + window.location.href="/Signup"}> + + + + + +
+ } + {window.isSharePage&& +
+ + +
+ {window.shareInfo.ownerNick} + 分享于{window.shareInfo.shareDate } +
+
+ +
} + + + +
+ ); + const iOS = process.browser && /iPad|iPhone|iPod/.test(navigator.userAgent); + return ( +
+ + + {(((this.props.selected.length <=1 && !(!this.props.isMultiple&&this.props.withFile))||!window.isHomePage)&&!window.isSharePage)&& + + + + } + {(((this.props.selected.length <=1 && !(!this.props.isMultiple&&this.props.withFile))||!window.isHomePage)&&!window.isSharePage)&&this.props.handleDesktopToggle(!this.props.desktopOpen)} + className={classes.menuButtonDesktop} + > + + } + {((this.props.selected.length >1 || (!this.props.isMultiple&&this.props.withFile) )&& (window.isHomePage||window.isSharePage))&& + 1) || (!this.props.isMultiple&&this.props.withFile)}> + this.props.setSelectedTarget([])} + > + + + + } + {(this.props.selected.length <=1 && !(!this.props.isMultiple&&this.props.withFile))&& + + {window.isSharePage&&window.pageId===""&&}{window.siteInfo.mainTitle} + + } + + {(!this.props.isMultiple&&this.props.withFile&&!window.isMobile)&& + + {this.props.selected[0].name} {(window.isHomePage||window.isSharePage)&&"("+sizeToString(this.props.selected[0].size)+")"} + + } + + {(this.props.selected.length >1&&!window.isMobile)&& + + {this.props.selected.length}个对象 + + } + {(this.props.selected.length <=1 && !(!this.props.isMultiple&&this.props.withFile))&& + + } +
+ {((this.props.selected.length>1 || (!this.props.isMultiple&&this.props.withFile)) && !window.isHomePage&&!window.isSharePage && window.userInfo.uid!==-1&&!checkGetParameters("share"))&& +
+ + this.props.saveFile()}> + + + +
+ } + {((this.props.selected.length>1 || (!this.props.isMultiple&&this.props.withFile) )&& (window.isHomePage||window.isSharePage))&& +
+ {(!this.props.isMultiple&&this.props.withFile&&isPreviewable(this.props.selected[0].name))&& + + + this.openPreview()} + > + + + + + } + {(!this.props.isMultiple&&this.props.withFile)&& + + + this.openDownload()} + > + + + + + } + {(!this.props.isMultiple && this.props.withFolder)&& + + + this.props.navitateTo(this.props.path==="/"?this.props.path+this.props.selected[0].name:this.props.path+"/"+this.props.selected[0].name) } + > + + + + + } + {(!this.props.isMultiple&&!window.isSharePage)&& + + + this.props.openShareDialog()}> + + + + + } + {(!this.props.isMultiple&&!window.isSharePage)&& + + + this.props.openRenameDialog()}> + + + + + } + {!window.isSharePage&&
+ {!window.isMobile&& + + this.props.openMoveDialog()}> + + + + } + + + + this.props.openRemoveDialog()}> + + + + +
} + + +
+ } + {(this.props.selected.length <=1 && !(!this.props.isMultiple&&this.props.withFile))&& + + } + + + {this.loadUploader()} + + + this.setState(state => ({ mobileOpen: true}))} + disableDiscovery={iOS} + ModalProps={{ + keepMounted: true, // Better open performance on mobile. + }} + > + {drawer} + + + +
+ {drawer} + + + +
+ ); + } + +} +NavbarCompoment.propTypes = { + classes: PropTypes.object.isRequired, + theme: PropTypes.object.isRequired, +}; + +const Navbar = connect( + mapStateToProps, + mapDispatchToProps + )( withTheme()(withStyles(styles)(NavbarCompoment))) + +export default Navbar \ No newline at end of file diff --git a/src/component/Profile.js b/src/component/Profile.js new file mode 100644 index 0000000..d60d15e --- /dev/null +++ b/src/component/Profile.js @@ -0,0 +1,253 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Paper from '@material-ui/core/Paper'; +import Avatar from '@material-ui/core/Avatar'; +import { toggleSnackbar,}from "../actions/index" +import Typography from '@material-ui/core/Typography'; +import axios from 'axios' +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import IconButton from '@material-ui/core/IconButton'; +import LeftIcon from '@material-ui/icons/KeyboardArrowLeft' +import RighttIcon from '@material-ui/icons/KeyboardArrowRight' +import Grid from '@material-ui/core/Grid'; + +const styles = theme => ({ + layout: { + width: 'auto', + marginTop:'50px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + marginBottom: "30px", + [theme.breakpoints.up("sm")]: { + width: 700, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + userNav:{ + height:"270px", + backgroundColor: theme.palette.primary.main, + padding: "20px 20px 2em", + backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1600 900'%3E%3Cpolygon fill='"+theme.palette.primary.light.replace("#","%23")+"' points='957 450 539 900 1396 900'/%3E%3Cpolygon fill='"+theme.palette.primary.dark.replace("#","%23")+"' points='957 450 872.9 900 1396 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.main.replace("#","%23")+"' points='-60 900 398 662 816 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.dark.replace("#","%23")+"' points='337 900 398 662 816 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.light.replace("#","%23")+"' points='1203 546 1552 900 876 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.main.replace("#","%23")+"' points='1203 546 1552 900 1162 900'/%3E%3Cpolygon fill='"+theme.palette.primary.dark.replace("#","%23")+"' points='641 695 886 900 367 900'/%3E%3Cpolygon fill='"+theme.palette.primary.main.replace("#","%23")+"' points='587 900 641 695 886 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.light.replace("#","%23")+"' points='1710 900 1401 632 1096 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.dark.replace("#","%23")+"' points='1710 900 1401 632 1365 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.main.replace("#","%23")+"' points='1210 900 971 687 725 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.dark.replace("#","%23")+"' points='943 900 1210 900 971 687'/%3E%3C/svg%3E\")", + backgroundSize: "cover", + backgroundPosition: "bottom", + }, + avatarContainer:{ + height: "80px", + width: "80px", + borderRaidus:"50%", + margin: "auto", + marginTop: "50px", + boxShadow: "0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12)", + border: "2px solid #fff", + }, + nickName:{ + width: "200px", + margin: "auto", + textAlign: "center", + marginTop: "1px", + fontSize: "25px", + color: "#ffffffcf", + }, + th:{ + minWidth: "106px", + }, + mobileHide:{ + [theme.breakpoints.down("md")]: { + display:"none", + } + }, + tableLink:{ + cursor: "pointer", + }, + navigator:{ + display:"flex", + justifyContent:"space-between", + }, + pageInfo:{ + marginTop: "14px", + marginLeft: "23px", + }, + infoItem:{ + paddingLeft: "46px!important", + paddingBottom: "20px!important", + }, + infoContainer:{ + marginTop:"30px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +class ProfileCompoment extends Component { + + state={ + listType:0, + shareList:[], + page:1, + } + + handleChange = (event, listType) => { + this.setState({ listType }); + if(listType===1){ + this.loadList(1,"hot"); + }else if(listType===0){ + this.loadList(1,"default"); + } + }; + + componentDidMount = ()=>{ + this.loadList(1,"default"); + } + + loadList = (page,shareType) => { + axios.post('/Profile/getList', { + uid:window.taegetUserInfo.uid, + type:shareType, + page:page, + }).then( (response)=> { + this.setState({ + page:page, + shareList:response.data, + }) + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right","加载失败","error"); + }); + } + + loadNext = ()=>{ + this.loadList(this.state.page+1,this.state.listType===0?"default":"hot"); + } + + loadPrev = ()=>{ + this.loadList(this.state.page-1,this.state.listType===0?"default":"hot"); + } + + render() { + const { classes } = this.props; + + + return ( +
+ +
+
+ +
+
+ {window.taegetUserInfo.nickname} +
+
+ + + + + + {this.state.listType===2&& +
+ + + UID + {window.taegetUserInfo.uid} + + + 昵称 + {window.taegetUserInfo.nickname} + + + 用户组 + {window.taegetUserInfo.group} + + + 分享总数 + {window.taegetUserInfo.shareCount} + + + 注册日期 + {window.taegetUserInfo.regDate} + + +
} + {(this.state.listType===0||this.state.listType===1)&&
+ + + + 文件名 + 分享日期 + 下载次数 + 浏览次数 + + + + {this.state.shareList.map(row => ( + window.open("/s/"+row.share_key)} + > + {row.fileData} + {row.share_time} + {row.download_num} + {row.view_num} + + ))} + +
+ {(this.state.shareList.length!==0 && this.state.listType===0)&& +
+ 第{this.state.page}页 +
+ + + + + + +
+
+ } +
} +
+
+ ); + } + +} + +const Profile = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(ProfileCompoment)) + +export default Profile diff --git a/src/component/Search.js b/src/component/Search.js new file mode 100644 index 0000000..b56c5b5 --- /dev/null +++ b/src/component/Search.js @@ -0,0 +1,120 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Tooltip from '@material-ui/core/Tooltip'; +import Card from '@material-ui/core/Card'; +import Avatar from '@material-ui/core/Avatar'; +import CardHeader from '@material-ui/core/CardHeader'; +import { toggleSnackbar,}from "../actions/index" +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; +import IconButton from '@material-ui/core/IconButton'; +import OpenIcon from '@material-ui/icons/OpenInNew'; +import FileIcon from '@material-ui/icons/InsertDriveFile'; +import FolderIcon from '@material-ui/icons/Folder'; + + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: "0 auto", + }, + actions: { + display: 'flex', + }, + layout: { + width: 'auto', + marginTop:'50px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 1100, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + shareTitle:{ + maxWidth:"200px", + }, + avatarFile:{ + backgroundColor: theme.palette.primary.light, + }, + avatarFolder:{ + backgroundColor: theme.palette.secondary.light, + }, + gird:{ + marginTop:"30px", + }, +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +class SearchCompoment extends Component { + + state={ + shareList:[], + } + + render() { + const { classes } = this.props; + + + return ( +
+ 搜索结果 + + {window.list.map(value=>( + + + + {value.source_type==="file"&& + + + + } + {value.source_type==="dir"&& + + + + } + +
+ } + action={ + window.open("/s/"+value.share_key)}> + + + } + title={{value.fileData}} + subheader={value.share_time} + /> + + + + ))} + +
+ ); + } + +} + +const Search = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(SearchCompoment)) + +export default Search diff --git a/src/component/SearchBar.js b/src/component/SearchBar.js new file mode 100644 index 0000000..18a662b --- /dev/null +++ b/src/component/SearchBar.js @@ -0,0 +1,180 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import SearchIcon from '@material-ui/icons/Search'; +import { fade } from '@material-ui/core/styles/colorManipulator'; +import InputBase from '@material-ui/core/InputBase'; +import Popper from '@material-ui/core/Popper'; +import Fade from '@material-ui/core/Fade'; +import Paper from '@material-ui/core/Paper'; +import MenuItem from '@material-ui/core/MenuItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Typography from '@material-ui/core/Typography'; +import FileIcon from '@material-ui/icons/InsertDriveFile' +import ShareIcon from '@material-ui/icons/Share' +import { connect } from 'react-redux' +import { + searchMyFile, +}from "../actions/index" + +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + searchMyFile:(keywords)=>{ + dispatch(searchMyFile(keywords)); + }, + } +} + +const styles = theme => ({ + search: { + [theme.breakpoints.down('sm')]: { + display:"none", + }, + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: fade(theme.palette.common.white, 0.15), + '&:hover': { + backgroundColor: fade(theme.palette.common.white, 0.25), + }, + marginRight: theme.spacing.unit * 2, + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing.unit * 7.2, + width: 'auto', + }, + }, + searchIcon: { + width: theme.spacing.unit * 9, + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + inputRoot: { + color: 'inherit', + width: '100%', + }, + inputInput: { + paddingTop: theme.spacing.unit, + paddingRight: theme.spacing.unit, + paddingBottom: theme.spacing.unit, + paddingLeft: theme.spacing.unit * 7, + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('md')]: { + width: 200, + '&:focus': { + width: 300, + } + }, + }, + suggestBox:{ + zIndex: "9999", + width:364, + } +}) + +class SearchBarCompoment extends Component { + + state = { + anchorEl: null, + input:"", + }; + + handleChange = (event)=>{ + const { currentTarget } = event; + this.input = event.target.value; + this.setState({ + anchorEl:currentTarget, + input:event.target.value, + }); + } + + cancelSuggest = ()=>{ + this.setState({ + input:"", + }); + } + + searchMyFile = ()=>{ + this.props.searchMyFile(this.input); + } + + searchShare = ()=>{ + window.location.href="/Explore/Search/"+this.input; + } + + render() { + + const { classes} = this.props; + const { anchorEl } = this.state; + const id = this.state.input!=="" ? 'simple-popper' : null; + + return ( +
+
+ +
+ + + {({ TransitionProps }) => ( + + + {window.isHomePage&& + + + + + + 在我的文件中搜索 {this.state.input} + } /> + + } + + + + + + + 在全站分享中搜索 {this.state.input} + } /> + + + + )} + +
+ ); + } +} + +SearchBarCompoment.propTypes = { + classes: PropTypes.object.isRequired, +}; + +const SearchBar = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(SearchBarCompoment)) + +export default SearchBar \ No newline at end of file diff --git a/src/component/SharedFile.js b/src/component/SharedFile.js new file mode 100644 index 0000000..1e38c4f --- /dev/null +++ b/src/component/SharedFile.js @@ -0,0 +1,178 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import FileIcon from "../component/FileManager/FileIcon" +import PreviewIcon from "@material-ui/icons/RemoveRedEye" +import InfoIcon from "@material-ui/icons/Info" +import DownloadIcon from "@material-ui/icons/CloudDownload" +import Button from '@material-ui/core/Button'; +import {allowSharePreview,sizeToString} from "../untils/index" +import { toggleSnackbar,}from "../actions/index" +import { isPreviewable,}from "../config" +import Popper from '@material-ui/core/Popper'; +import Typography from '@material-ui/core/Typography'; +import Fade from '@material-ui/core/Fade'; +import Paper from '@material-ui/core/Paper'; +import axios from 'axios' +const styles = theme => ({ + layout: { + width: 'auto', + marginTop:'110px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 1100, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + player:{ + borderRadius: "4px", + }, + fileCotainer:{ + width:"200px", + margin:"0 auto", + }, + buttonCotainer:{ + width:"400px", + margin:"0 auto", + textAlign: "center", + marginTop: "20px", + }, + button: { + margin: theme.spacing.unit, + }, + paper: { + padding: theme.spacing.unit * 2, + }, + icon:{ + marginRight: theme.spacing.unit, + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +const allowDownload = allowSharePreview(); + +class SharedFileCompoment extends Component { + + state = { + anchorEl: null, + open: false, + }; + + preview = ()=>{ + if(!allowDownload){ + this.props.toggleSnackbar("top","right","未登录用户无法下载","warning"); + return; + } + switch(isPreviewable(window.shareInfo.fileName)){ + case 'img': + window.open(window.apiURL.preview); + return; + case 'msDoc': + window.open(window.apiURL.docPreiview); + return; + case 'audio': + //this.props.openMusicDialog(); + return; + case 'open': + window.open(window.apiURL.preview); + return; + case 'video': + window.location.href=("/Viewer/Video?single=true&shareKey="+window.shareInfo.shareId+"&path=/"+window.shareInfo.fileName); + return ; + case 'edit': + window.location.href=("/Viewer/Markdown?single=true&shareKey="+window.shareInfo.shareId+"&path=/"+window.shareInfo.fileName); + return; + default: + this.props.toggleSnackbar("top","right","此文件无法预览","warning"); + return; + } + } + + download = ()=>{ + if(!allowDownload){ + this.props.toggleSnackbar("top","right","未登录用户无法下载","warning"); + return; + } + axios.post("/Share/getDownloadUrl",{ + key: window.shareInfo.shareId + }) + .then( (response)=> { + if(response.data.error!==0){ + this.props.toggleSnackbar("top","right",response.data.msg ,"warning"); + }else{ + window.location.href=response.data.result; + } + + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + }); + } + + handleOpen = event => { + const { currentTarget } = event; + this.setState(state => ({ + anchorEl: currentTarget, + open: !state.open, + })); + }; + + render() { + const { classes } = this.props; + const file={ + name:window.shareInfo.fileName, + path:"/", + type:"file", + pic:"", + }; + const id = this.state.open ? 'simple-popper' : null; + + return ( +
+
+
+
+ + + + + {({ TransitionProps }) => ( + + + 此分享被浏览{window.shareInfo.ViewNum}次,被下载{window.shareInfo.downloadNum}次 + + + )} + +
+
+ ); + } + +} + +const SharedFile = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(SharedFileCompoment)) + +export default SharedFile diff --git a/src/component/SideDrawer.js b/src/component/SideDrawer.js new file mode 100644 index 0000000..56a73ff --- /dev/null +++ b/src/component/SideDrawer.js @@ -0,0 +1,98 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Drawer from '@material-ui/core/Drawer'; +import { withStyles } from '@material-ui/core/styles'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import UploadIcon from '@material-ui/icons/CloudUpload'; +import List from '@material-ui/core/List'; +const drawerWidth = 240; +const styles = theme => ({ + drawer: { + [theme.breakpoints.up('sm')]: { + width: drawerWidth, + flexShrink: 0, + }, + }, + drawerPaper: { + width: drawerWidth, + }, + toolbar: theme.mixins.toolbar, +}); +class SideDrawer extends Component { + + state = { + mobileOpen: false, + }; + + handleDrawerToggle = () => { + this.setState(state => ({ mobileOpen: !state.mobileOpen })); + }; + + upload(){ + alert(""); + } + + render() { + const { classes } = this.props; + + const drawer = ( +
+ + + + + + + + + + +
+ ); + + return ( + + ); + } + +} +SideDrawer.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(SideDrawer); \ No newline at end of file diff --git a/src/component/Snackbar.js b/src/component/Snackbar.js new file mode 100644 index 0000000..1f5d578 --- /dev/null +++ b/src/component/Snackbar.js @@ -0,0 +1,150 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { connect } from 'react-redux' +import { + } from "../actions/index" +import classNames from 'classnames'; +import { withStyles } from '@material-ui/core/styles'; +import SnackbarContent from '@material-ui/core/SnackbarContent'; +import Snackbar from '@material-ui/core/Snackbar' +import ErrorIcon from '@material-ui/icons/Error'; +import InfoIcon from '@material-ui/icons/Info'; +import CloseIcon from '@material-ui/icons/Close'; +import green from '@material-ui/core/colors/green'; +import CheckCircleIcon from '@material-ui/icons/CheckCircle'; +import amber from '@material-ui/core/colors/amber'; +import IconButton from '@material-ui/core/IconButton'; +import WarningIcon from '@material-ui/icons/Warning'; + +const mapStateToProps = state => { + return { + snackbar:state.viewUpdate.snackbar, + } +} + +const mapDispatchToProps = dispatch => { + return { + } +} + +const variantIcon = { + success: CheckCircleIcon, + warning: WarningIcon, + error: ErrorIcon, + info: InfoIcon, + }; + +const styles1 = theme => ({ + success: { + backgroundColor: green[600], + }, + error: { + backgroundColor: theme.palette.error.dark, + }, + info: { + backgroundColor: theme.palette.primary.dark, + }, + warning: { + backgroundColor: amber[700], + }, + icon: { + fontSize: 20, + }, + iconVariant: { + opacity: 0.9, + marginRight: theme.spacing.unit, + }, + message: { + display: 'flex', + alignItems: 'center', + }, +}) + +function MySnackbarContent(props) { + const { classes, className, message, onClose, variant, ...other } = props; + const Icon = variantIcon[variant]; + + return ( + + + {message} + + } + action={[ + + + , + ]} + {...other} + /> + ); + } + MySnackbarContent.propTypes = { + classes: PropTypes.object.isRequired, + className: PropTypes.string, + message: PropTypes.node, + onClose: PropTypes.func, + variant: PropTypes.oneOf(['success', 'warning', 'error', 'info']).isRequired, + }; + +const MySnackbarContentWrapper = withStyles(styles1)(MySnackbarContent); +const styles = theme => ({ + margin: { + margin: theme.spacing.unit, + }, +}) +class SnackbarCompoment extends Component { + + state={ + open:false, + } + + componentWillReceiveProps = (nextProps)=>{ + if(nextProps.snackbar.toggle !== this.props.snackbar.toggle){ + this.setState({open:true}); + } + } + + handleClose= ()=>{ + this.setState({open:false}); + } + + render() { + + return ( + + + + ); + } + +} + +const AlertBar = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(SnackbarCompoment)) + +export default AlertBar \ No newline at end of file diff --git a/src/component/StorageBar.js b/src/component/StorageBar.js new file mode 100644 index 0000000..7f3de25 --- /dev/null +++ b/src/component/StorageBar.js @@ -0,0 +1,153 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import LinearProgress from '@material-ui/core/LinearProgress'; +import Typography from '@material-ui/core/Typography'; +import Divider from '@material-ui/core/Divider'; +import StorageIcon from '@material-ui/icons/Storage' +import { connect } from 'react-redux' +import axios from 'axios' +import Tooltip from '@material-ui/core/Tooltip'; +import { + toggleSnackbar, +}from "../actions/index" + +const mapStateToProps = state => { + return { + refresh:state.viewUpdate.storageRefresh, + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +const styles = theme => ({ + iconFix:{ + marginLeft: "32px", + marginRight: "32px", + color: theme.palette.text.secondary, + marginTop: "2px", + }, + textFix:{ + "padding":" 0 0 0 16px", + }, + storageContainer:{ + display:"flex", + marginTop: "15px", + + marginBottom: "20px", + }, + detail:{ + width: "100%", + marginRight: "20px", + }, + info:{ + width:"131px", + marginTop:"5px", + }, + bar:{ + marginTop: "5px", + }, + stickFooter:{ + bottom: "0px", + position: "absolute", + backgroundColor:theme.palette.background.paper, + } +}) + +class StorageBarCompoment extends Component { + + state={ + percent:0, + used:null, + total:null, + } + + firstLoad = true; + + componentDidMount = ()=>{ + if(this.firstLoad){ + this.updateStatus(); + this.firstLoad = !this.firstLoad; + } + + } + + componentWillReceiveProps = (nextProps)=>{ + if(this.props.refresh!==nextProps.refresh){ + this.updateStatus(); + } + } + + updateStatus = ()=>{ + let percent = 0; + axios.get('/Member/Memory') + .then( (response)=> { + if(response.data.rate>=100){ + percent = 100; + this.props.toggleSnackbar("top","right","您的已用容量已超过容量配额,请尽快删除多余文件或购买容量","warning"); + }else{ + percent = response.data.rate; + } + this.setState({ + percent:percent, + used:response.data.used, + total:response.data.total, + }); + }) + .catch((error) =>{ + + }); + } + + render() { + const { classes} = this.props; + if(!window.isMobile){ + return ( +
+
+ +
+ 存储空间 + +
+ + 已使用{(this.state.used===null)?" -- ":this.state.used},共{(this.state.total===null)?" -- ":this.state.total} +
+
+
+
+ ); + }else{ + return ( +
+ +
+ 存储空间 + +
+ + 已使用{(this.state.used===null)?" -- ":this.state.used},共{(this.state.total===null)?" -- ":this.state.total} +
+
+
+ ); + } + } +} + +StorageBarCompoment.propTypes = { + classes: PropTypes.object.isRequired, +}; + +const StorageBar = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(StorageBarCompoment)) + +export default StorageBar \ No newline at end of file diff --git a/src/component/Upload/FileList.js b/src/component/Upload/FileList.js new file mode 100644 index 0000000..4766643 --- /dev/null +++ b/src/component/Upload/FileList.js @@ -0,0 +1,253 @@ +import React, { Component } from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import Dialog from '@material-ui/core/Dialog'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItem from '@material-ui/core/ListItem'; +import List from '@material-ui/core/List'; +import Divider from '@material-ui/core/Divider'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import CloseIcon from '@material-ui/icons/Close'; +import FileIcon from '@material-ui/icons/InsertDriveFile'; +import PhotoIcon from '@material-ui/icons/MusicNote'; +import VideoIcon from '@material-ui/icons/Videocam'; +import AddIcon from '@material-ui/icons/AddCircleOutline'; +import MusicIcon from '@material-ui/icons/MusicNote'; +import LinearProgress from '@material-ui/core/LinearProgress'; +import Slide from '@material-ui/core/Slide'; +import Avatar from '@material-ui/core/Avatar'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import DeleteIcon from '@material-ui/icons/Delete'; +import withWidth, { isWidthDown } from '@material-ui/core/withWidth'; +import DialogContent from '@material-ui/core/DialogContent'; + +const styles = theme => ({ + appBar: { + position: 'relative', + }, + flex: { + flex: 1, + }, + progressBar:{ + marginTop:5, + }, + minHight:{ + [theme.breakpoints.up('sm')]: { + minWidth:500, + } + }, + dialogContent:{ + padding:0, + }, + successStatus:{ + marginBottom:10, + color:"#4caf50", + }, + errorStatus:{ + marginBottom:10, + color:"#ff5722", + }, +}); +class FileList extends Component { + + state = { + open: false, + files: [ + ], + }; + + //入队 + enQueue(files) { + var filesNow = this.state.files; + if (filesNow.findIndex((file) => { return file.id === files.id }) === -1) { + filesNow.push(files); + this.setState({ + files: filesNow, + }); + } + console.log(this.state); + + } + + deQueue(file){ + var filesNow = this.state.files; + var fileID = filesNow.findIndex((f) => { return f.id === file.id }); + if (fileID !== -1) { + filesNow.splice(fileID, 1); + this.setState({ + files: filesNow, + }); + } + } + + updateStatus(file){ + var filesNow = this.state.files; + var fileID = filesNow.findIndex((f) => { return f.id === file.id }); + if (fileID !== -1) { + if(filesNow[fileID].status!==4){ + filesNow[fileID] = file; + this.setState({ + files: filesNow, + }); + } + + } + } + + setComplete(file){ + var filesNow = this.state.files; + var fileID = filesNow.findIndex((f) => { return f.id === file.id }); + if (fileID !== -1) { + if(filesNow[fileID].status!==4){ + filesNow[fileID].status = 5; + this.setState({ + files: filesNow, + }); + } + + } + } + + setError(file,errMsg){ + var filesNow = this.state.files; + var fileID = filesNow.findIndex((f) => { return f.id === file.id }); + if (fileID !== -1) { + filesNow[fileID].status = 4; + filesNow[fileID].errMsg = errMsg; + }else{ + file.status = 4; + file.errMsg = errMsg; + filesNow.push(file); + } + this.setState({ + files: filesNow, + }); + } + + Transition(props) { + return ; + } + openFileList = () => { + if(!this.state.open){ + this.setState({ open: true }); + } + + }; + + cancelUpload = file =>{ + this.props.cancelUpload(file); + this.deQueue(file); + } + + handleClose = () => { + this.setState({ open: false }); + + }; + + addNewFile = () => { + document.getElementsByClassName("uploadForm")[0].click(); + } + + render() { + + const { classes } = this.props; + const { width } = this.props; + + const fileIcon = { + "image":["jpg","bpm","png","gif","jpeg","webp","svg"], + "video":["mp4","rmvb","flv","avi"], + "audio":["mp3","ogg","flac","aac"], + }; + + this.props.inRef({ + "openFileList":this.openFileList.bind(this), + "enQueue":this.enQueue.bind(this), + "updateStatus":this.updateStatus.bind(this), + "setComplete":this.setComplete.bind(this), + "setError":this.setError.bind(this), + }); + + var listContent = ( + this.state.files.map(function(item, i){ + var progressItem; + var queueIcon; + + if(fileIcon["image"].indexOf(item.name.split(".").pop())!==-1){ + queueIcon = (); + }else if(fileIcon["video"].indexOf(item.name.split(".").pop())!==-1){ + queueIcon = (); + }else if(fileIcon["audio"].indexOf(item.name.split(".").pop())!==-1){ + queueIcon = (); + }else{ + queueIcon = (); + } + + if(item.status ===5){ + progressItem = (已完成
} />); + }else if (item.status ===2){ + progressItem = ({window.plupload.formatSize(item.speed).toUpperCase()}/s 已上传 {window.plupload.formatSize(item.loaded).toUpperCase()} , 共 {window.plupload.formatSize(item.size).toUpperCase()} - {item.percent}%
}/>); + }else if (item.status ===1){ + progressItem = (排队中
} />); + }else if (item.status ===4){ + progressItem = ({item.errMsg}
} />); + }else{ + progressItem = (); + } + return ( +
+ + + {queueIcon} + + {progressItem} + + + this.cancelUpload(item)}> + + + + + +
+ ); + },this) + + ); + + return ( + + + + + + + + 上传队列 + + + + + + + + + + {listContent} + + + + ); + } + +} +FileList.propTypes = { +}; + +export default withStyles(styles)(withWidth()(FileList)); \ No newline at end of file diff --git a/src/component/Uploader.js b/src/component/Uploader.js new file mode 100644 index 0000000..b98bb10 --- /dev/null +++ b/src/component/Uploader.js @@ -0,0 +1,163 @@ +import React, { Component } from 'react' +import scriptLoader from '../loader/index.js' +import { connect } from 'react-redux' +import { + refreshFileList, + refreshStorage +} from "../actions/index" +import FileList from "./Upload/FileList.js" + +let loaded = false; + +const mapStateToProps = state => { + return { + path:state.navigator.path, + } +} + +const mapDispatchToProps = dispatch => { + return { + refreshFileList:()=>{ + dispatch(refreshFileList()) + }, + refreshStorage:()=>{ + dispatch(refreshStorage()) + } + } +} + +class UploaderCompoment extends Component { + + constructor(props) { + super(props); + this.state={ + queued:0, + } + } + + setRef(val){ + this.fileList=val; + } + + cancelUpload(file){ + this.uploader.removeFile(file); + } + shouldComponentUpdate(nextProps,nextState){ + if(nextState.queued !== this.state.queued){ + this.props.queueChange(nextState.queued); + } + return false; + } + + componentWillReceiveProps({ isScriptLoaded, isScriptLoadSucceed }) { + if (isScriptLoaded && !this.props.isScriptLoaded) { // load finished + if (isScriptLoadSucceed) { + if(loaded){ + return; + } + loaded = true; + this.uploader = window.Qiniu.uploader({ + runtimes: 'html5', + browse_button: 'pickfiles', + container: 'container', + drop_element: 'container', + max_file_size: window.uploadConfig.maxSize, + dragdrop: true, + chunk_size: window.ChunkSize, + filters: { + mime_types: window.uploadConfig.allowedType, + }, + multi_selection: !(window.moxie.core.utils.Env.OS.toLowerCase() === "ios"), + uptoken_url: "/Upload/Token", + domain: "s", + get_new_uptoken: true, + auto_start: true, + log_level: 5, + init: { + 'FilesAdded':({up, files})=>{ + this.fileList["openFileList"](); + window.plupload.each(files, (files)=> { + window.pathCache[files.id] = this.props.path; + this.fileList["enQueue"](files); + }) + console.log(window.pathCache); + }, + + + 'BeforeUpload': function (up, file) { + }, + "QueueChanged":(up=>{ + this.setState({queued:up.total.queued}); + }), + 'UploadProgress': (up, file)=>{ + this.fileList["updateStatus"](file); + }, + 'UploadComplete': (up, file)=>{ + if(file.length===0){ + return + } + if(file[0].status === 5){ + this.fileList["setComplete"](file[0]); + this.props.refreshFileList(); + this.props.refreshStorage(); + } + + }, + 'FileUploaded': function (up, file, info) { + + }, + 'Error': (up, err, errTip)=>{ + this.fileList["openFileList"](); + this.fileList["setError"](err.file,errTip); + }, + "FilesRemoved":(up, files)=>{ + }, + } + }); + // this.fileList["openFileList"](); + } + else this.onError() + } + } + + componentDidMount() { + const { isScriptLoaded, isScriptLoadSucceed } = this.props + if (isScriptLoaded && isScriptLoadSucceed) { + } + + } + + onError(){ + + } + + openFileList=()=>{ + this.fileList["openFileList"](); + }; + + + render() { + return ( +
+ +
+ ); + } + +} + +const Uploader = connect( + mapStateToProps, + mapDispatchToProps, + null, + {forwardRef : true} +)( scriptLoader( + ['/static/js/uploader/moxie.js'], + ['/static/js/uploader/plupload.dev.js'], + ['/static/js/uploader/i18n/zh_CN.js'], + ['/static/js/uploader/ui.js'], + ['/static/js/uploader/uploader.js'], + +)(UploaderCompoment)) + +export default Uploader \ No newline at end of file diff --git a/src/component/UserAvatar.js b/src/component/UserAvatar.js new file mode 100644 index 0000000..55aa3c8 --- /dev/null +++ b/src/component/UserAvatar.js @@ -0,0 +1,189 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import Grow from '@material-ui/core/Grow'; +import { connect } from 'react-redux' +import Avatar from '@material-ui/core/Avatar'; +import IconButton from '@material-ui/core/IconButton'; +import Popover from '@material-ui/core/Popover'; +import SettingIcon from '@material-ui/icons/Settings' +import Typography from '@material-ui/core/Typography'; +import Chip from '@material-ui/core/Chip'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import MenuItem from '@material-ui/core/MenuItem'; +import Divider from '@material-ui/core/Divider'; +import { + LogoutVariant, + HomeAccount, + DesktopMacDashboard, + AccountCircle, + AccountArrowRight, + AccountPlus +} from 'mdi-material-ui' +import { + +}from "../actions/index" + +const mapStateToProps = state => { + return { + selected:state.explorer.selected, + isMultiple:state.explorer.selectProps.isMultiple, + withFolder:state.explorer.selectProps.withFolder, + withFile:state.explorer.selectProps.withFile, + } +} + +const mapDispatchToProps = dispatch => { + return { + + } +} + +const styles = theme => ({ + mobileHidden:{ + [theme.breakpoints.down('xs')]: { + display: 'none', + }, + }, + avatar:{ + width:"30px", + height:"30px", + }, + header:{ + display:"flex", + padding: "20px 20px 20px 20px", + }, + largeAvatar:{ + height:"90px", + width:"90px", + }, + info:{ + marginLeft: "10px", + width: "139px", + }, + badge:{ + marginTop:"10px", + }, + visitorMenu:{ + width:200, + } +}) + +class UserAvatarCompoment extends Component { + + state={ + anchorEl:null, + } + + showUserInfo = e=>{ + this.setState({ + anchorEl:e.currentTarget + }); + } + + handleClose=()=>{ + this.setState({ + anchorEl:null, + }); + } + + openURL = (url)=>{ + window.location.href=url; + } + + render() { + const { classes} = this.props; + return ( +
+ + +
+ {(window.userInfo.uid!==-1)&&window.location.href="/Member/Setting"} color="inherit" + > + + } + + + {window.userInfo.uid===-1&& + + } + {window.userInfo.uid!==-1&&} + +
+
+ + {window.userInfo.uid===-1&& +
+ + this.openURL("/Login")}> + + 登录 + + this.openURL("/Signup")}> + + 注册 + +
+ } + {window.userInfo.uid!==-1&& +
+
+
+ +
+
+ {window.userInfo.nick} + {window.userInfo.email} + +
+
+
+ + this.openURL("/Profile/"+window.userInfo.uid)}> + + 个人主页 + + {(window.userInfo.groupId === 1)&& + this.openURL("/Admin")}> + + 管理面板 + + } + + this.openURL("/Member/LogOut")}> + + 退出 + +
} +
+
+ ); + } +} + +UserAvatarCompoment.propTypes = { + classes: PropTypes.object.isRequired, +}; + +const UserAvatar = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(UserAvatarCompoment)) + +export default UserAvatar \ No newline at end of file diff --git a/src/component/UserInfo.js b/src/component/UserInfo.js new file mode 100644 index 0000000..446e2bb --- /dev/null +++ b/src/component/UserInfo.js @@ -0,0 +1,89 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Typography from '@material-ui/core/Typography'; +import { +}from "../actions/index" + +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + } +} + +const styles = theme => ({ + userNav:{ + height:"170px", + backgroundColor: theme.palette.primary.main, + padding: "20px 20px 2em", + backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1600 900'%3E%3Cpolygon fill='"+theme.palette.primary.light.replace("#","%23")+"' points='957 450 539 900 1396 900'/%3E%3Cpolygon fill='"+theme.palette.primary.dark.replace("#","%23")+"' points='957 450 872.9 900 1396 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.main.replace("#","%23")+"' points='-60 900 398 662 816 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.dark.replace("#","%23")+"' points='337 900 398 662 816 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.light.replace("#","%23")+"' points='1203 546 1552 900 876 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.main.replace("#","%23")+"' points='1203 546 1552 900 1162 900'/%3E%3Cpolygon fill='"+theme.palette.primary.dark.replace("#","%23")+"' points='641 695 886 900 367 900'/%3E%3Cpolygon fill='"+theme.palette.primary.main.replace("#","%23")+"' points='587 900 641 695 886 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.light.replace("#","%23")+"' points='1710 900 1401 632 1096 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.dark.replace("#","%23")+"' points='1710 900 1401 632 1365 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.main.replace("#","%23")+"' points='1210 900 971 687 725 900'/%3E%3Cpolygon fill='"+theme.palette.secondary.dark.replace("#","%23")+"' points='943 900 1210 900 971 687'/%3E%3C/svg%3E\")", + backgroundSize: "cover", + }, + avatar:{ + display: "block", + width: "80px", + height: "80px", + border:" 2px solid #fff", + borderRadius: "50%", + overflow: "hidden", + boxShadow: "0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12)", + }, + avatarImg:{ + width: "80px", + height: "80px", + }, + nickName:{ + color: "#fff", + marginLeft: "10px", + marginTop: "15px", + fontSize: "17px", + }, + flexAvatar:{ + display:"flex", + }, + groupName:{ + marginLeft: "10px", + color:"#ffffff8a", + }, + storageCircle:{ + width: "200px", + } +}) + +class UserInfoCompoment extends Component { + + render() { + const { classes} = this.props; + + return ( +
+ +
{window.userInfo.nick} + {window.userInfo.group}
+
+ ); + + } +} + +UserInfoCompoment.propTypes = { + classes: PropTypes.object.isRequired, +}; + +const UserInfo = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(UserInfoCompoment)) + +export default UserInfo \ No newline at end of file diff --git a/src/component/UserSetting.js b/src/component/UserSetting.js new file mode 100644 index 0000000..5122a82 --- /dev/null +++ b/src/component/UserSetting.js @@ -0,0 +1,775 @@ +import React, { Component } from 'react' +import { withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux' +import Button from '@material-ui/core/Button'; +import Divider from '@material-ui/core/Divider'; +import TextField from '@material-ui/core/TextField'; +import PhotoIcon from '@material-ui/icons/InsertPhoto' +import GroupIcon from '@material-ui/icons/Group' +import DateIcon from '@material-ui/icons/DateRange' +import EmailIcon from '@material-ui/icons/Email' +import HomeIcon from '@material-ui/icons/Home' +import LinkIcon from '@material-ui/icons/Phonelink' +import InputIcon from '@material-ui/icons/Input' +import SecurityIcon from '@material-ui/icons/Security' +import NickIcon from '@material-ui/icons/PermContactCalendar' +import LockIcon from '@material-ui/icons/Lock' +import VerifyIcon from '@material-ui/icons/VpnKey' +import ColorIcon from '@material-ui/icons/Palette' +import Avatar from '@material-ui/core/Avatar'; +import Paper from '@material-ui/core/Paper'; +import { toggleSnackbar,}from "../actions/index" +import Typography from '@material-ui/core/Typography'; +import axios from 'axios' +import FingerprintIcon from '@material-ui/icons/Fingerprint' +import List from '@material-ui/core/List'; +import ToggleButton from '@material-ui/lab/ToggleButton'; +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import RightIcon from '@material-ui/icons/KeyboardArrowRight' +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import blue from '@material-ui/core/colors/blue'; +import yellow from '@material-ui/core/colors/yellow'; +import { ListItemIcon } from '@material-ui/core'; +import Switch from '@material-ui/core/Switch'; + +const styles = theme => ({ + + layout: { + width: 'auto', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 700, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + sectionTitle:{ + paddingBottom: "10px", + paddingTop: "30px", + }, + rightIcon:{ + marginTop: "4px", + marginRight: "10px", + color:theme.palette.text.secondary, + }, + uploadFromFile:{ + backgroundColor: blue[100], + color: blue[600], + }, + userGravatar:{ + backgroundColor: yellow[100], + color: yellow[800], + }, + infoText:{ + marginRight: "17px", + }, + infoTextWithIcon:{ + marginRight: "17px", + marginTop: "1px", + }, + rightIconWithText:{ + marginTop: "0px", + marginRight: "10px", + color:theme.palette.text.secondary, + }, + iconFix:{ + marginRight: "11px", + marginLeft: "7px", + }, + flexContainer:{ + display:"flex", + }, + desenList:{ + paddingTop:0, + paddingBottom:0, + }, + flexContainerResponse:{ + display:"flex", + [theme.breakpoints.down("sm")]: { + display:"initial", + }, + }, + desText:{ + marginTop:"10px", + }, + secondColor:{ + height: "20px", + width: "20px", + backgroundColor: theme.palette.secondary.main, + borderRadius: "50%", + marginRight: "17px", + }, + firstColor:{ + height: "20px", + width: "20px", + backgroundColor: theme.palette.primary.main, + borderRadius: "50%", + marginRight: "6px", + }, + themeBlock:{ + height: "20px", + width: "20px", + }, + paddingBottom:{ + marginBottom:"30px", + } +}) +const mapStateToProps = state => { + return { + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} + +class UserSettingCompoment extends Component { + + constructor(props){ + super(props); + this.fileInput = React.createRef(); + } + + state={ + avatarModal:false, + nickModal:false, + changePassword:false, + loading:"", + oldPwd:"", + newPwd:"", + webdavPwd:"", + newPwdRepeat:"", + homePage:window.userInfo.homePage, + nick:window.userInfo.nick, + twoFactor:false, + authCode:"", + changeTheme:false, + chosenTheme:null, + showWebDavUrl:false, + showWebDavUserName:false, + changeWebDavPwd:false, + } + + handleClose = () => { + this.setState({ + avatarModal: false, + nickModal:false, + changePassword:false, + loading:"", + twoFactor:false, + changeTheme:false, + showWebDavUrl:false, + showWebDavUserName:false, + changeWebDavPwd:false, + }); + }; + + useGravatar = ()=>{ + this.setState({ + loading:"gravatar", + }) + axios.post('/Member/SetGravatar', { + t:"comfirm", + }).then( (response)=> { + window.location.reload(); + this.setState({ + loading:"", + }) + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.setState({ + loading:"", + }) + }); + } + + changeNick = ()=>{ + this.setState({ + loading:"nick", + }) + axios.post('/Member/Nick', { + nick:this.state.nick, + }).then( (response)=> { + if(response.data.error==="1"){ + this.props.toggleSnackbar("top","right",response.data.msg ,"error"); + }else{ + window.location.reload(); + } + this.setState({ + loading:"", + }) + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.setState({ + loading:"", + }) + }); + } + + uploadAvatar = ()=>{ + this.setState({ + loading:"avatar", + }) + var formData = new FormData(); + formData.append("avatar", this.fileInput.current.files[0]); + axios.post('/Member/SaveAvatar', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }).then( (response)=> { + if(response.data.result==="error"){ + this.props.toggleSnackbar("top","right",response.data.msg ,"warning"); + }else{ + window.location.reload(); + } + this.setState({ + loading:"", + }) + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.setState({ + loading:"", + }) + }); + } + + handleToggle = () =>{ + axios.post('/Member/HomePage', { + status:this.state.homePage==="1"?"false":"true", + }).then( (response)=> { + if(response.data.error==="1"){ + this.props.toggleSnackbar("top","right",response.data.msg ,"error"); + }else{ + this.props.toggleSnackbar("top","right","设置已保存" ,"success"); + } + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + }); + this.setState({ + homePage:this.state.homePage==="1"?"0":"1", + }); + } + + changhePwd =()=>{ + if(this.state.newPwd!==this.state.newPwdRepeat){ + this.props.toggleSnackbar("top","right","两次密码输入不一致" ,"warning"); + } + this.setState({ + loading:"changePassword", + }); + axios.post('/Member/ChangePwd', { + origin: this.state.oldPwd, + new: this.state.newPwd + }).then( (response)=> { + if(response.data.error==="1"){ + this.props.toggleSnackbar("top","right",response.data.msg ,"error"); + this.setState({ + loading:"", + }); + }else{ + window.location.reload(); + } + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.setState({ + loading:"", + }); + }); + } + + changeTheme = ()=>{ + this.setState({ + loading:"changeTheme", + }); + axios.post('/Member/ChangeThemeColor', { + theme:this.state.chosenTheme, + }).then( (response)=> { + if(response.data.error==="1"){ + this.props.toggleSnackbar("top","right",response.data.msg ,"error"); + this.setState({ + loading:"", + }); + }else{ + window.location.reload(); + } + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.setState({ + loading:"", + }); + }); + } + + changheWebdavPwd = ()=>{ + this.setState({ + loading:"changheWebdavPwd", + }); + axios.post('/Member/setWebdavPwd', { + pwd: this.state.webdavPwd, + }).then( (response)=> { + if(response.data.error==="1"){ + this.props.toggleSnackbar("top","right",response.data.msg ,"error"); + this.setState({ + loading:"", + }); + }else{ + this.props.toggleSnackbar("top","right",response.data.msg ,"success"); + this.setState({ + loading:"", + changeWebDavPwd:false, + }); + } + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.setState({ + loading:"", + }); + }); + } + + twoFactor = ()=>{ + this.setState({ + loading:"twoFactor", + }); + axios.post('/Member/TwoFactorConfirm', { + code:this.state.authCode, + }).then( (response)=> { + if(response.data.error==="1"){ + this.props.toggleSnackbar("top","right",response.data.msg ,"error"); + this.setState({ + loading:"", + }); + }else{ + window.location.reload(); + } + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + this.setState({ + loading:"", + }); + }); + } + + handleChange = name => event => { + this.setState({ [name]: event.target.value }); + }; + + handleAlignment = (event, chosenTheme) => this.setState({ chosenTheme }); + + render() { + const { classes } = this.props; + + + return ( +
+
+ 个人资料 + + + + this.setState({avatarModal:true})}> + + + + + + + + + + + + + + + {window.userInfo.uid} + + + + this.setState({nickModal:true})}> + + + + + {window.userInfo.nick} + + + + + + + + + {window.userInfo.email} + + + + + + + + + {window.userInfo.group} + + + + + + + + + {window.userInfo.regTime} + + + + + 安全隐私 + + + + + + + + + + + + this.setState({changePassword:true})}> + + + + + + + + + window.userInfo.twoFactor==="0"?this.setState({twoFactor:true}):""}> + + + + + {window.userInfo.twoFactor==="0"?"未开启":"已开启"} + + + + + + 个性化 + + + this.setState({changeTheme:true})}> + + + + +
+
+
+
+
+
+ {window.userInfo.webdav==="1"&&
+ WebDAV + + + this.setState({showWebDavUrl:true})}> + + + + + + + + + this.setState({showWebDavUserName:true})}> + + + + + + + + + this.setState({changeWebDavPwd:true})}> + + + + + + + + + +
} +
+ +
+ + 修改头像 + + + + + + + + + + + + + + + + + + + + + + + + + 修改昵称 + + + + + + + + + + 修改登录密码 + +
+
+
+
+
+
+
+ + + + +
+ + 启用二步验证 + +
+ qrcode +
+ 请使用任意二步验证APP或者支持二步验证的密码管理软件扫描左侧二维码添加本站。扫描完成后请填写二步验证APP给出的6位验证码以开启二步验证。 + +
+
+
+ + + + +
+ + 更改主题配色 + + + {Object.keys(window.colorThemeOptions).map((value,key)=>( + +
+
+ ))} +
+
+ + + + +
+ + WebDAV连接地址 + + + + + + + + + WebDAV用户名 + + + + + + + + + 修改/设置WebDAV密码 + + + + + + + + +
+ ); + } + +} + +const UserSetting = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(UserSettingCompoment)) + +export default UserSetting diff --git a/src/component/Viewer/markdown.js b/src/component/Viewer/markdown.js new file mode 100644 index 0000000..df49448 --- /dev/null +++ b/src/component/Viewer/markdown.js @@ -0,0 +1,159 @@ +import React, { Component } from 'react' +import Paper from '@material-ui/core/Paper'; +import { withStyles } from '@material-ui/core/styles'; +import { + toggleSnackbar, +} from "../../actions/index" +import axios from 'axios' +import { connect } from 'react-redux' +import {editSuffix} from "../../config" +const styles = theme => ({ + layout: { + width: 'auto', + marginTop:'30px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 1100, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, +}) +const mapStateToProps = state => { + return { + save:state.explorer.fileSave, + } +} + +const mapDispatchToProps = dispatch => { + return { + toggleSnackbar:(vertical,horizontal,msg,color)=>{ + dispatch(toggleSnackbar(vertical,horizontal,msg,color)) + }, + } +} +const codeSuffix = { + "html":"text/html", + "sql":"text/html", + "go":"go", + "py":"python", + "js":"javascript", + "json":"text/json", + "c":"clike", + "cpp":"clike", + "css":"css", + "txt":"text/html" +}; +class MarkdownViewerCompoment extends Component { + + state = { + val:"", + } + + componentWillReceiveProps = (nextProps)=>{ + if(this.props.save!==nextProps.save){ + this.save(); + } + } + + save = ()=>{ + axios.post("/File/Edit",{ + item:window.fileInfo.path, + content:window.document.getElementById("val").value, + }) + .then( (response)=> { + if(!response.data.result.success){ + this.props.toggleSnackbar("top","right",response.data.result.error ,"error"); + }else{ + this.props.toggleSnackbar("top","right","文件已保存" ,"success"); + } + + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + }); + } + + componentDidMount = ()=>{ + let suffix = window.fileInfo.name.split(".").pop().toLowerCase(); + if(suffix === "md"){ + // eslint-disable-next-line + var editor = window.editormd("editormd", { + path : "/static/js/mdeditor/lib/", + height: 740, + tex : true, + toolbarIcons : function() { + return [ + "undo", "redo", "|", + "bold", "del", "italic", "quote", "|", + "h1", "h2", "h3", "h4", "h5", "h6", "|", + "list-ul", "list-ol", "hr", "|", + "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "html-entities", "pagebreak", "|", + "goto-line", "watch", "clear", "search", "|", + "help", "info" + ]; + }, + }); + }else if(editSuffix.indexOf(suffix)!==-1){ + // eslint-disable-next-line + var editor = window.editormd("editormd", { + path : "/static/js/mdeditor/lib/", + height: 740, + watch : false, + toolbar : false, + codeFold : true, + searchReplace : true, + placeholder : "Enjoy coding!", + mode:codeSuffix[suffix], + }); + } + + axios.get(window.fileInfo.url) + .then( (response)=> { + if(response.data.result.hasOwnProperty("success")){ + this.props.toggleSnackbar("top","right",response.data.result.error ,"error"); + }else{ + this.setState({ + val:response.data.result, + }); + } + + }) + .catch((error) =>{ + this.props.toggleSnackbar("top","right",error.message ,"error"); + }); + } + + + + handleChange(event) { + this.setState({val: event.target.value}); + } + + render() { + const { classes } = this.props; + return ( +
+ +
+ +
+ +
+
+ ); + } + +} + +const MarkdownViewer = connect( + mapStateToProps, + mapDispatchToProps +)( withStyles(styles)(MarkdownViewerCompoment)) + +export default MarkdownViewer \ No newline at end of file diff --git a/src/component/Viewer/video.js b/src/component/Viewer/video.js new file mode 100644 index 0000000..3e357dc --- /dev/null +++ b/src/component/Viewer/video.js @@ -0,0 +1,43 @@ +import React, { Component } from 'react' +import Paper from '@material-ui/core/Paper'; +import { withStyles } from '@material-ui/core/styles'; +import DPlayer from "react-dplayer"; + + +const styles = theme => ({ + layout: { + width: 'auto', + marginTop:'30px', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(1100 + theme.spacing.unit * 3 * 2)]: { + width: 1100, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + player:{ + borderRadius: "4px", + } +}) + +class VideoViewer extends Component { + + render() { + const { classes } = this.props; + return ( +
+ + + + +
+ ); + } + +} + +VideoViewer.propTypes = { +}; + +export default withStyles(styles)(VideoViewer); \ No newline at end of file diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..eb5ab6f --- /dev/null +++ b/src/config.js @@ -0,0 +1,44 @@ +export const imgPreviewSuffix = ["bmp","png","gif","jpg","jpeg","svg","webp"]; +export const msDocPreviewSuffix = ["ppt","pptx","pps","doc","docx","xlsx","xls"]; +export const audioPreviewSuffix = ["mp3","ogg"]; +export const videoPreviewSuffix = ["mp4"]; +export const directOpenPreviewSuffix = ["pdf"]; +export const editSuffix = ["md","html","sql","go","py","js","json","c","cpp","css","txt"]; +export const mediaType = { + audio:["mp3","flac","ape","wav","acc","ogg"], + video:["mp4","flv","avi","wmv","mkv","rm","rmvb","mov","ogv"], + image:["bmp","iff","png","gif","jpg","jpeg","psd","svg","webp"], + pdf:["pdf"], + word:["doc","docx"], + ppt:["ppt","pptx"], + excel:["xls","xlsx","csv"], + text:["txt","md","html"], + torrent:["torrent"], + zip:["zip","gz","tar","rar","7z"], + excute:["exe"], + android:["apk"], +}; +export const isPreviewable = name=>{ + let suffix = name.split(".").pop().toLowerCase(); + if(imgPreviewSuffix.indexOf(suffix)!==-1){ + return "img"; + }else if(msDocPreviewSuffix.indexOf(suffix)!==-1){ + return "msDoc"; + }else if(audioPreviewSuffix.indexOf(suffix)!==-1){ + return "audio"; + }else if(directOpenPreviewSuffix.indexOf(suffix)!==-1){ + return "open"; + }else if(videoPreviewSuffix.indexOf(suffix)!==-1){ + return "video"; + }else if(editSuffix.indexOf(suffix)!==-1){ + return "edit"; + } + return false; +} +export const isTorrent = name=>{ + let suffix = name.split(".").pop().toLowerCase(); + if(mediaType.torrent.indexOf(suffix)!==-1){ + return true; + } + return false; +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..9853865 --- /dev/null +++ b/src/index.js @@ -0,0 +1,75 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; +import * as serviceWorker from './serviceWorker'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import cloureveApp from './reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:true, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + getSource:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); + + + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: http://bit.ly/CRA-PWA +serviceWorker.unregister(); diff --git a/src/loader/index.js b/src/loader/index.js new file mode 100644 index 0000000..93b1492 --- /dev/null +++ b/src/loader/index.js @@ -0,0 +1,131 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import hoistStatics from 'hoist-non-react-statics' +import { newScript, series, noop } from './utils' + +const loadedScript = [] +const pendingScripts = {} +let failedScript = [] + +export function startLoadingScripts(scripts, onComplete = noop) { + // sequence load + const loadNewScript = (script) => { + const src = typeof script === 'object' ? script.src : script + if (loadedScript.indexOf(src) < 0) { + return taskComplete => { + const callbacks = pendingScripts[src] || [] + callbacks.push(taskComplete) + pendingScripts[src] = callbacks + if (callbacks.length === 1) { + return newScript(script)(err => { + pendingScripts[src].forEach(cb => cb(err, src)) + delete pendingScripts[src] + }) + } + } + } + } + const tasks = scripts.map(src => { + if (Array.isArray(src)) { + return src.map(loadNewScript) + } + else return loadNewScript(src) + }) + + series(...tasks)((err, src) => { + if (err) { + failedScript.push(src) + } + else { + if (Array.isArray(src)) { + src.forEach(addCache) + } + else addCache(src) + } + })(err => { + removeFailedScript() + onComplete(err) + }) +} + +const addCache = (entry) => { + if (loadedScript.indexOf(entry) < 0) { + loadedScript.push(entry) + } +} + +const removeFailedScript = () => { + if (failedScript.length > 0) { + failedScript.forEach((script) => { + const node = document.querySelector(`script[src='${script}']`) + if (node != null) { + node.parentNode.removeChild(node) + } + }) + + failedScript = [] + } +} + +const scriptLoader = (...scripts) => (WrappedComponent) => { + class ScriptLoader extends Component { + static propTypes = { + onScriptLoaded: PropTypes.func + } + + static defaultProps = { + onScriptLoaded: noop + } + + constructor (props, context) { + super(props, context) + + this.state = { + isScriptLoaded: false, + isScriptLoadSucceed: false + } + + this._isMounted = false; + } + + componentDidMount () { + this._isMounted = true; + startLoadingScripts(scripts, err => { + if(this._isMounted) { + this.setState({ + isScriptLoaded: true, + isScriptLoadSucceed: !err + }, () => { + if (!err) { + this.props.onScriptLoaded() + } + }) + } + }) + } + + componentWillUnmount () { + this._isMounted = false; + } + + getWrappedInstance () { + return this.refs.wrappedInstance; + } + + render () { + const props = { + ...this.props, + ...this.state, + ref: 'wrappedInstance' + } + + return ( + + ) + } + } + + return hoistStatics(ScriptLoader, WrappedComponent) +} + +export default scriptLoader diff --git a/src/loader/utils.js b/src/loader/utils.js new file mode 100644 index 0000000..6ad07be --- /dev/null +++ b/src/loader/utils.js @@ -0,0 +1,104 @@ +export const isDefined = val => val != null +export const isFunction = val => typeof val === 'function' +export const noop = _ => { } + +export const newScript = (src) => (cb) => { + const scriptElem = document.createElement('script') + if (typeof src === 'object') { + // copy every property to the element + for (var key in src) { + if (Object.prototype.hasOwnProperty.call(src, key)) { + scriptElem[key] = src[key]; + } + } + src = src.src; + } else { + scriptElem.src = src + } + scriptElem.addEventListener('load', () => cb(null, src)) + scriptElem.addEventListener('error', () => cb(true, src)) + document.body.appendChild(scriptElem) + return scriptElem +} + +const keyIterator = (cols) => { + const keys = Object.keys(cols) + let i = -1 + return { + next () { + i++ // inc + if (i >= keys.length) return null + else return keys[i] + } + } +} + +// tasks should be a collection of thunk +export const parallel = (...tasks) => (each) => (cb) => { + let hasError = false + let successed = 0 + const ret = [] + tasks = tasks.filter(isFunction) + + if (tasks.length <= 0) cb(null) + else { + tasks.forEach((task, i) => { + const thunk = task + thunk((err, ...args) => { + if (err) hasError = true + else { + // collect result + if (args.length <= 1) args = args[0] + + ret[i] = args + successed ++ + } + + if (isFunction(each)) each.call(null, err, args, i) + + if (hasError) cb(true) + else if (tasks.length === successed) { + cb(null, ret) + } + }) + }) + } +} + +// tasks should be a collection of thunk +export const series = (...tasks) => (each) => (cb) => { + tasks = tasks.filter(val => val != null) + const nextKey = keyIterator(tasks) + const nextThunk = () => { + const key = nextKey.next() + let thunk = tasks[key] + if (Array.isArray(thunk)) thunk = parallel.apply(null, thunk).call(null, each) + return [ +key, thunk ] // convert `key` to number + } + let key, thunk + let next = nextThunk() + key = next[0] + thunk = next[1] + if (thunk == null) return cb(null) + + const ret = [] + const iterator = () => { + thunk((err, ...args) => { + if (args.length <= 1) args = args[0] + if (isFunction(each)) each.call(null, err, args, key) + + if (err) cb(err) + else { + // collect result + ret.push(args) + + next = nextThunk() + key = next[0] + thunk = next[1] + if (thunk == null) return cb(null, ret) // finished + else iterator() + } + }) + } + iterator() +} diff --git a/src/pages/download.app.js b/src/pages/download.app.js new file mode 100644 index 0000000..15d83d7 --- /dev/null +++ b/src/pages/download.app.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { withStyles } from '@material-ui/core/styles'; +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import Download from '../component/Download' +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class DownloadList extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +DownloadList.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(DownloadList); diff --git a/src/pages/download.js b/src/pages/download.js new file mode 100644 index 0000000..2259d2d --- /dev/null +++ b/src/pages/download.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import DownloadApp from "./download.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/fileShare.app.js b/src/pages/fileShare.app.js new file mode 100644 index 0000000..c9e99de --- /dev/null +++ b/src/pages/fileShare.app.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { withStyles } from '@material-ui/core/styles'; +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import SharedFile from '../component/SharedFile' +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class FileShareApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +FileShareApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(FileShareApp); diff --git a/src/pages/fileShare.js b/src/pages/fileShare.js new file mode 100644 index 0000000..dc4bc6e --- /dev/null +++ b/src/pages/fileShare.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import FileShareApp from "./fileShare.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/folderShare.app.js b/src/pages/folderShare.app.js new file mode 100644 index 0000000..c4c8f7e --- /dev/null +++ b/src/pages/folderShare.app.js @@ -0,0 +1,51 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; + +import { withStyles } from '@material-ui/core/styles'; + +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import FileManager from "../component/FileManager/FileManager" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; + +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class FolderShareApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +FolderShareApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(FolderShareApp); diff --git a/src/pages/folderShare.js b/src/pages/folderShare.js new file mode 100644 index 0000000..14deaf9 --- /dev/null +++ b/src/pages/folderShare.js @@ -0,0 +1,67 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import FolderShareApp from "./folderShare.app" +import cloureveApp from '../reducers' +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + getSource:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + fileSave:false, + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/lock.app.js b/src/pages/lock.app.js new file mode 100644 index 0000000..980ba06 --- /dev/null +++ b/src/pages/lock.app.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { withStyles } from '@material-ui/core/styles'; +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import LockedFile from '../component/LockedFile' +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class LockApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +LockApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(LockApp); diff --git a/src/pages/lock.js b/src/pages/lock.js new file mode 100644 index 0000000..8ac4fe5 --- /dev/null +++ b/src/pages/lock.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import LockApp from "./lock.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/login.app.js b/src/pages/login.app.js new file mode 100644 index 0000000..dc699f5 --- /dev/null +++ b/src/pages/login.app.js @@ -0,0 +1,59 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { withStyles } from '@material-ui/core/styles'; +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import LoginForm from "../component/Login/LoginForm" +import TwoStep from "../component/Login/TwoStep" +import RegisterForm from "../component/Login/RegisterForm" +import EmailActivication from "../component/Login/EmailActivication" +import ResetPwd from "../component/Login/ResetPwd" +import ResetPwdForm from "../component/Login/ResetPwdForm" + +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class LoginApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ {window.pageId==="login"&&} + {window.pageId==="TwoStep"&&} + {window.pageId==="register"&&} + {window.pageId==="emailActivate"&&} + {window.pageId==="resetPwd"&&} + {window.pageId==="resetPwdForm"&&} +
+
+
+ ); + } +} + +LoginApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(LoginApp); diff --git a/src/pages/login.js b/src/pages/login.js new file mode 100644 index 0000000..258de59 --- /dev/null +++ b/src/pages/login.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import LoginApp from "./login.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/markdown.app.js b/src/pages/markdown.app.js new file mode 100644 index 0000000..824cc5a --- /dev/null +++ b/src/pages/markdown.app.js @@ -0,0 +1,51 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; + +import { withStyles } from '@material-ui/core/styles'; + +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import MarkdownViewer from "../component/Viewer/markdown" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; + +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class MarkdownApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +MarkdownApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(MarkdownApp); diff --git a/src/pages/markdown.js b/src/pages/markdown.js new file mode 100644 index 0000000..8388397 --- /dev/null +++ b/src/pages/markdown.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import cloureveApp from '../reducers' +import MarkdownApp from "./markdown.app" +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + fileSave:false, + dirList:[ + ], + selected:[{path:"/",name:window.fileInfo.name,type:"file"}], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:true, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/myShare.app.js b/src/pages/myShare.app.js new file mode 100644 index 0000000..c86cb4b --- /dev/null +++ b/src/pages/myShare.app.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { withStyles } from '@material-ui/core/styles'; +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import MyShare from '../component/MyShare' +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class MyShareApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +MyShareApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(MyShareApp); diff --git a/src/pages/myShare.js b/src/pages/myShare.js new file mode 100644 index 0000000..80ebefa --- /dev/null +++ b/src/pages/myShare.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import MyShareApp from "./myShare.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/profile.app.js b/src/pages/profile.app.js new file mode 100644 index 0000000..eb2821d --- /dev/null +++ b/src/pages/profile.app.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { withStyles } from '@material-ui/core/styles'; +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import Profile from '../component/Profile' +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class ProfileApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +ProfileApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(ProfileApp); diff --git a/src/pages/profile.js b/src/pages/profile.js new file mode 100644 index 0000000..af795ba --- /dev/null +++ b/src/pages/profile.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import ProfileApp from "./profile.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/search.app.js b/src/pages/search.app.js new file mode 100644 index 0000000..c46870e --- /dev/null +++ b/src/pages/search.app.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { withStyles } from '@material-ui/core/styles'; +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import Search from '../component/Search' +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class SearchApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +SearchApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(SearchApp); diff --git a/src/pages/search.js b/src/pages/search.js new file mode 100644 index 0000000..72cb763 --- /dev/null +++ b/src/pages/search.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import SearchApp from "./search.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/setting.app.js b/src/pages/setting.app.js new file mode 100644 index 0000000..ad68be5 --- /dev/null +++ b/src/pages/setting.app.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { withStyles } from '@material-ui/core/styles'; +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import UserSetting from '../component/UserSetting' +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class SettingApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +SettingApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(SettingApp); diff --git a/src/pages/setting.js b/src/pages/setting.js new file mode 100644 index 0000000..f9ffdac --- /dev/null +++ b/src/pages/setting.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import SettingApp from "./setting.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/pages/video.app.js b/src/pages/video.app.js new file mode 100644 index 0000000..3e63dd1 --- /dev/null +++ b/src/pages/video.app.js @@ -0,0 +1,51 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; + +import { withStyles } from '@material-ui/core/styles'; + +import Navbar from "../component/Navbar.js" +import AlertBar from "../component/Snackbar" +import VideoViewer from "../component/Viewer/video" +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'; + +const theme = createMuiTheme(window.colorTheme); +const styles = theme => ({ + + root: { + display: 'flex', + }, + content: { + flexGrow: 1, + padding: theme.spacing.unit * 0, + minWidth: 0, + }, + toolbar: theme.mixins.toolbar, +}); + +class VideoApp extends Component { + + render() { + const { classes } = this.props; + return ( + + +
+ + + +
+
+ +
+
+
+ ); + } +} + +VideoApp.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default withStyles(styles)(VideoApp); diff --git a/src/pages/video.js b/src/pages/video.js new file mode 100644 index 0000000..ad15622 --- /dev/null +++ b/src/pages/video.js @@ -0,0 +1,66 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import { createStore } from 'redux' +import VideoApp from "./video.app" +import cloureveApp from '../reducers' + +const defaultStatus = { + navigator:{ + path:window.path, + refresh:true, + }, + viewUpdate:{ + open:window.isHomePage, + explorerViewMethod: "icon", + sortMethod:"timePos", + contextType:"none", + menuOpen:false, + navigatorLoading:true, + navigatorError:false, + navigatorErrorMsg:null, + modalsLoading:false, + storageRefresh:false, + modals:{ + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + }, + snackbar:{ + toggle:false, + vertical:"top", + horizontal:"center", + msg:"", + color:"", + } + }, + explorer:{ + fileList:[], + dirList:[ + ], + selected:[], + selectProps:{ + isMultiple:false, + withFolder:false, + withFile:false, + }, + imgPreview:{ + first:null, + other:[], + }, + keywords:null, + } +}; + +let store = createStore(cloureveApp,defaultStatus) +ReactDOM.render( + + + +, document.getElementById('root')); diff --git a/src/reducers/explorer.js b/src/reducers/explorer.js new file mode 100644 index 0000000..9e4850e --- /dev/null +++ b/src/reducers/explorer.js @@ -0,0 +1,67 @@ +const checkSelectedProps = (state)=>{ + let isMultiple,withFolder,withFile=false; + isMultiple = (state.selected.length>1); + state.selected.map((value)=>{ + if(value.type==="dir"){ + withFolder = true; + }else if(value.type==="file"){ + withFile = true; + } + }) + return [isMultiple,withFolder,withFile]; +} + +const explorer = (state = [], action) => { + switch (action.type) { + case 'UPDATE_FILE_LIST': + var dirList = action.list.filter(function (x) { + return x .type === "dir"; + }); + return Object.assign({}, state, { + fileList: action.list, + dirList: dirList, + }); + case 'ADD_SELECTED_TARGET': + var newState = Object.assign({}, state, { + selected: [...state.selected,action.targets] + }); + var selectedProps = checkSelectedProps(newState); + return Object.assign({}, newState, { + selectProps: { + isMultiple:selectedProps[0], + withFolder:selectedProps[1], + withFile:selectedProps[2], + } + }); + case 'SET_SELECTED_TARGET': + var newState = Object.assign({}, state, { + selected: action.targets + }); + var selectedProps = checkSelectedProps(newState); + return Object.assign({}, newState, { + selectProps: { + isMultiple:selectedProps[0], + withFolder:selectedProps[1], + withFile:selectedProps[2], + } + }); + case 'RMOVE_SELECTED_TARGET': + var oldSelected = state.selected.concat(); + oldSelected.splice(action.id,1); + var newState = Object.assign({}, state, { + selected: oldSelected, + }); + var selectedProps = checkSelectedProps(newState); + return Object.assign({}, newState, { + selectProps: { + isMultiple:selectedProps[0], + withFolder:selectedProps[1], + withFile:selectedProps[2], + } + }); + default: + return state + } + } + + export default explorer \ No newline at end of file diff --git a/src/reducers/index.js b/src/reducers/index.js new file mode 100644 index 0000000..5e6d6f7 --- /dev/null +++ b/src/reducers/index.js @@ -0,0 +1,350 @@ +const checkSelectedProps = (state)=>{ + let isMultiple,withFolder,withFile=false; + isMultiple = (state.selected.length>1); + // eslint-disable-next-line + state.selected.map((value)=>{ + if(value.type==="dir"){ + withFolder = true; + }else if(value.type==="file"){ + withFile = true; + } + }) + return [isMultiple,withFolder,withFile]; +} + +const doNavigate = (path,state)=>{ + return Object.assign({}, state, { + navigator:Object.assign({}, state.navigator, { + path: path + }), + viewUpdate:Object.assign({}, state.viewUpdate, { + contextOpen:false, + navigatorError:false, + navigatorLoading:true, + }), + explorer:Object.assign({}, state.explorer, { + selected:[], + selectProps: { + isMultiple:false, + withFolder:false, + withFile:false, + }, + keywords:null, + }), + }); +} + +const cloudreveApp = (state = [], action) => { + switch (action.type) { + case 'DRAWER_TOGGLE': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + open:action.open, + }), + }); + case 'CHANGE_VIEW_METHOD': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + explorerViewMethod:action.method, + }), + }); + case 'CHANGE_SORT_METHOD': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + sortMethod:action.method, + }), + }); + case 'CHANGE_CONTEXT_MENU': + if(state.viewUpdate.contextOpen && action.open){ + return Object.assign({}, state); + } + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + contextOpen: action.open, + contextType:action.menuType, + }), + }); + case 'SET_NAVIGATOR_LOADING_STATUE': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + navigatorLoading:action.status, + }), + }); + case 'SET_NAVIGATOR_ERROR': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + navigatorError: action.status, + navigatorErrorMsg: action.msg, + }), + }); + case 'UPDATE_FILE_LIST': + // eslint-disable-next-line + action.list.sort((a,b)=>{ + switch (state.viewUpdate.sortMethod) { + case "sizePos": + return a.size-b.size; + case "sizeRes": + return b.size-a.size; + case 'namePos': + return a.name.localeCompare(b.name); + case 'nameRev': + return b.name.localeCompare(a.name); + case 'timePos': + return Date.parse(a.date)-Date.parse(b.date); + case 'timeRev': + return Date.parse(b.date)-Date.parse(a.date); + default: + break; + } + }) + var dirList = action.list.filter(function (x) { + return x.type === "dir"; + }); + var fileList = action.list.filter(function (x) { + return x.type === "file"; + }); + return Object.assign({}, state, { + explorer: Object.assign({}, state.explorer, { + fileList: fileList, + dirList: dirList, + }), + }); + case 'ADD_SELECTED_TARGET': + var newState = Object.assign({}, state, { + explorer:Object.assign({}, state.explorer, { + selected: [...state.explorer.selected,action.targets] + }), + }); + var selectedProps = checkSelectedProps(newState.explorer); + return Object.assign({}, newState, { + explorer:Object.assign({}, newState.explorer, { + selectProps: { + isMultiple:selectedProps[0], + withFolder:selectedProps[1], + withFile:selectedProps[2], + } + }), + }); + case 'SET_SELECTED_TARGET': + // eslint-disable-next-line + var newState = Object.assign({}, state, { + explorer:Object.assign({}, state.explorer, { + selected: action.targets + }), + }); + // eslint-disable-next-line + var selectedProps = checkSelectedProps(newState.explorer); + return Object.assign({}, newState, { + explorer:Object.assign({}, newState.explorer, { + selectProps: { + isMultiple:selectedProps[0], + withFolder:selectedProps[1], + withFile:selectedProps[2], + } + }), + }); + case 'RMOVE_SELECTED_TARGET': + var oldSelected = state.explorer.selected.concat(); + oldSelected.splice(action.id,1); + // eslint-disable-next-line + var newState = Object.assign({}, state, { + explorer:Object.assign({}, state.explorer, { + selected: oldSelected + }), + }); + // eslint-disable-next-line + var selectedProps = checkSelectedProps(newState.explorer); + return Object.assign({}, newState, { + explorer:Object.assign({}, newState.explorer, { + selectProps: { + isMultiple:selectedProps[0], + withFolder:selectedProps[1], + withFile:selectedProps[2], + } + }), + }); + case 'NAVIGATOR_TO': + return doNavigate(action.path,state); + case 'NAVIGATOR_UP': + let pathSplit = state.navigator.path.split("/"); + pathSplit.pop(); + let newPath = pathSplit.length===1?"/":pathSplit.join("/"); + return doNavigate(newPath,state); + case 'OPEN_CREATE_FOLDER_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + createNewFolder:true, + }), + contextOpen:false, + }), + }); + case 'OPEN_RENAME_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + rename:true, + }), + contextOpen:false, + }), + }); + case 'OPEN_REMOVE_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + remove:true, + }), + contextOpen:false, + }), + }); + case 'OPEN_MOVE_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + move:true, + }), + contextOpen:false, + }), + }); + case 'OPEN_SHARE_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + share:true, + }), + contextOpen:false, + }), + }); + case 'OPEN_MUSIC_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + music:true, + }), + contextOpen:false, + }), + }); + case 'OPEN_REMOTE_DOWNLOAD_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + remoteDownload:true, + }), + contextOpen:false, + }), + }); + case 'OPEN_TORRENT_DOWNLOAD_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + torrentDownload:true, + }), + contextOpen:false, + }), + }); + case 'OPEN_GET_SOURCE_DIALOG': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + getSource:true, + }), + contextOpen:false, + }), + }); + case 'CLOSE_ALL_MODALS': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modals: Object.assign({}, state.viewUpdate.modals, { + createNewFolder:false, + rename:false, + move:false, + remove:false, + share:false, + music:false, + remoteDownload:false, + torrentDownload:false, + getSource:false, + }), + }), + }); + case 'TOGGLE_SNACKBAR': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + snackbar:{ + toggle:!state.viewUpdate.snackbar.toggle, + vertical:action.vertical, + horizontal:action.horizontal, + msg:action.msg, + color:action.color, + }, + }), + }); + case 'SET_MODALS_LOADING': + return Object.assign({}, state, { + viewUpdate: Object.assign({}, state.viewUpdate, { + modalsLoading:action.status, + }), + }); + case 'REFRESH_FILE_LIST': + return Object.assign({}, state, { + navigator: Object.assign({}, state.navigator, { + refresh:!state.navigator.refresh, + }), + explorer:Object.assign({}, state.explorer, { + selected:[], + selectProps: { + isMultiple:false, + withFolder:false, + withFile:false, + } + }), + }); + case 'SEARCH_MY_FILE': + return Object.assign({}, state, { + navigator: Object.assign({}, state.navigator, { + path: "/搜索结果", + refresh:!state.navigator.refresh, + }), + viewUpdate:Object.assign({}, state.viewUpdate, { + contextOpen:false, + navigatorError:false, + navigatorLoading:true, + }), + explorer:Object.assign({}, state.explorer, { + selected:[], + selectProps: { + isMultiple:false, + withFolder:false, + withFile:false, + }, + keywords:action.keywords, + }), + }); + case 'SHOW_IMG_PREIVEW': + return Object.assign({}, state, { + explorer:Object.assign({}, state.explorer, { + imgPreview: { + first:action.first, + other:state.explorer.fileList, + }, + }), + }); + case 'REFRESH_STORAGE': + return Object.assign({}, state, { + viewUpdate:Object.assign({}, state.viewUpdate, { + storageRefresh:!state.viewUpdate.storageRefresh, + }), + }); + case 'SAVE_FILE': + return Object.assign({}, state, { + explorer:Object.assign({}, state.explorer, { + fileSave:!state.explorer.fileSave, + }), + }); + default: + return state + } + } + + +export default cloudreveApp \ No newline at end of file diff --git a/src/reducers/navigator.js b/src/reducers/navigator.js new file mode 100644 index 0000000..e970143 --- /dev/null +++ b/src/reducers/navigator.js @@ -0,0 +1,12 @@ +const navigator = (state = [], action) => { + switch (action.type) { + case 'NAVIGATOR_TO': + return Object.assign({}, state, { + path: action.path + }); + default: + return state + } + } + + export default navigator \ No newline at end of file diff --git a/src/reducers/view.js b/src/reducers/view.js new file mode 100644 index 0000000..a52c4fa --- /dev/null +++ b/src/reducers/view.js @@ -0,0 +1,38 @@ +const viewUpdate = (state = [], action) => { + switch (action.type) { + case 'DRAWER_TOGGLE': + return Object.assign({}, state, { + open: action.open + }); + case 'CHANGE_VIEW_METHOD': + return Object.assign({}, state, { + explorerViewMethod: action.method + + }); + case 'CHANGE_SORT_METHOD': + return Object.assign({}, state, { + sortMethod: action.method + }); + case 'CHANGE_CONTEXT_MENU': + if(state.contextOpen && action.open){ + return Object.assign({}, state); + } + return Object.assign({}, state, { + contextType: action.menuType, + contextOpen: action.open, + }); + case 'SET_NAVIGATOR_LOADING_STATUE': + return Object.assign({}, state, { + navigatorLoading: action.status + }); + case 'SET_NAVIGATOR_ERROR': + return Object.assign({}, state, { + navigatorError: action.status, + navigatorErrorMsg: action.msg, + }); + default: + return state + } + } + + export default viewUpdate \ No newline at end of file diff --git a/src/serviceWorker.js b/src/serviceWorker.js new file mode 100644 index 0000000..012c322 --- /dev/null +++ b/src/serviceWorker.js @@ -0,0 +1,131 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read http://bit.ly/CRA-PWA. + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit http://bit.ly/CRA-PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/src/untils/index.js b/src/untils/index.js new file mode 100644 index 0000000..259015a --- /dev/null +++ b/src/untils/index.js @@ -0,0 +1,69 @@ +export const sizeToString = (bytes) => { + if (bytes === 0) return '0 B'; + var k = 1024; + var sizes = ['B','KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + var i = Math.floor(Math.log(bytes) / Math.log(k)); + return (bytes / Math.pow(k, i)).toFixed(1) + ' ' + sizes[i]; +} + +export const setCookie = (name,value,days)=>{ + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days*24*60*60*1000)); + } + document.cookie = name + "=" + (value || "") +"; path=/"; +} + +export const setGetParameter = (paramName, paramValue) =>{ + var url = window.location.href; + var hash = window.location.hash; + url = url.replace(hash, ''); + if (url.indexOf(paramName + "=") >= 0) + { + var prefix = url.substring(0, url.indexOf(paramName)); + var suffix = url.substring(url.indexOf(paramName)); + suffix = suffix.substring(suffix.indexOf("=") + 1); + suffix = (suffix.indexOf("&") >= 0) ? suffix.substring(suffix.indexOf("&")) : ""; + url = prefix + paramName + "=" + paramValue + suffix; + } + else + { + if (url.indexOf("?") < 0) + url += "?" + paramName + "=" + paramValue; + else + url += "&" + paramName + "=" + paramValue; + } + if(url===window.location.href){ + return; + } + window.history.pushState(null, null, url); +} + +export const allowSharePreview=()=>{ + if(!window.isSharePage){ + return true; + } + if(window.isSharePage){ + if(window.shareInfo.allowPreview){ + return true; + } + if(window.userInfo.uid===-1){ + return false; + } + return true; + } +} + +export const checkGetParameters= field=>{ + var url = window.location.href; + if(url.indexOf('?' + field + '=') !== -1) + return true; + else if(url.indexOf('&' + field + '=') !== -1) + return true; + return false +} + +export const changeThemeColor = color=>{ + var metaThemeColor = window.document.querySelector("meta[name=theme-color]"); + metaThemeColor.setAttribute("content", color); +} \ No newline at end of file