├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── README.md ├── mock └── 新建文本文档.txt ├── package.json ├── proxy.config.js ├── src ├── assets │ ├── folder-96.png │ ├── folder.png │ ├── font │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ └── iconfont.woff │ └── images │ │ ├── css-64.png │ │ ├── folder-64.png │ │ ├── js-64.png │ │ ├── psd-64.png │ │ └── zip-64.png ├── index.css ├── index.html ├── index.js ├── models │ ├── DiskModel │ │ ├── DiskModel.js │ │ └── index.js │ ├── HomeModel │ │ ├── HomeModel.js │ │ └── index.js │ └── index.js ├── pages │ ├── DiskPage │ │ ├── BlockBrowser │ │ │ ├── File │ │ │ │ ├── index.js │ │ │ │ ├── jsx.js │ │ │ │ └── styles.css │ │ │ ├── FileMenu │ │ │ │ ├── index.js │ │ │ │ └── jsx.js │ │ │ ├── FolderMenu │ │ │ │ ├── index.js │ │ │ │ └── jsx.js │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── CheckAll │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── CopyModal │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── Layout │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── Menu │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── MkdirModal │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── Mode │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── MoveModal │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── Path │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── RenameModal │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── Search │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── Total │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── index.js │ │ ├── jsx.js │ │ └── styles.css │ ├── HomePage │ │ ├── Layout │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── Nav │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── Side │ │ │ ├── index.js │ │ │ ├── jsx.js │ │ │ └── styles.css │ │ ├── index.js │ │ ├── jsx.js │ │ └── styles.css │ └── index.js ├── router.js ├── services │ └── DiskService │ │ ├── DiskService.js │ │ └── index.js ├── tests │ └── models │ │ └── example-test.js └── utils │ └── request.js └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/**/*-test.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "eslint-config-airbnb", 4 | "rules": { 5 | "spaced-comment": [0], 6 | "no-unused-vars": [0], 7 | "no-empty": [0], 8 | "react/wrap-multilines": [0], 9 | "react/no-multi-comp": [0], 10 | "no-constant-condition": [0], 11 | "react/jsx-no-bind": [0], 12 | "react/prop-types": [0], 13 | "arrow-body-style": [0], 14 | "react/prefer-stateless-function": [0], 15 | "semi": [0], 16 | "global-require": [0], 17 | "no-shadow": [0], 18 | "no-useless-computed-key": [0], 19 | "no-underscore-dangle": [0] 20 | }, 21 | "ecmaFeatures": { 22 | "experimentalObjectRestSpread": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # r2r-web 2 | 一个模仿百度网盘的前端实现 3 | 4 | 这是线上 DEMO 5 | -------------------------------------------------------------------------------- /mock/新建文本文档.txt: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "entry": { 4 | "index": "./src/index.js" 5 | }, 6 | "dependencies": { 7 | "antd": "^2.4.3", 8 | "dva": "^1.1.0", 9 | "react": "^15.3.2", 10 | "react-contextmenu": "^2.0.0-alpha.2", 11 | "react-dom": "^15.3.2", 12 | "react-scrollbar": "^0.4.2" 13 | }, 14 | "devDependencies": { 15 | "atool-build": "^0.9.0", 16 | "atool-test-mocha": "^0.1.5", 17 | "babel-plugin-dev-expression": "^0.2.1", 18 | "babel-plugin-dva-hmr": "^0.2.0", 19 | "babel-plugin-transform-runtime": "^6.9.0", 20 | "babel-runtime": "^6.9.2", 21 | "dora": "^0.4.3", 22 | "dora-plugin-proxy": "^0.8.4", 23 | "dora-plugin-webpack": "^0.8.1", 24 | "dora-plugin-webpack-hmr": "^0.2.1", 25 | "expect": "^1.20.2", 26 | "font-awesome": "^4.7.0", 27 | "redbox-react": "^1.3.2" 28 | }, 29 | "scripts": { 30 | "start": "dora --plugins \"proxy?watchDirs=./mock,webpack,webpack-hmr\"", 31 | "build": "atool-build", 32 | "test": "atool-test-mocha ./src/**/*-test.js" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /proxy.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mock = {}; 4 | 5 | require('fs').readdirSync(require('path').join(__dirname + '/mock')) 6 | .forEach(function (file) { 7 | Object.assign(mock, require('./mock/' + file)); 8 | }); 9 | 10 | module.exports = mock; 11 | -------------------------------------------------------------------------------- /src/assets/folder-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/folder-96.png -------------------------------------------------------------------------------- /src/assets/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/folder.png -------------------------------------------------------------------------------- /src/assets/font/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('iconfont.eot?t=1480059376323'); /* IE9*/ 4 | src: url('iconfont.eot?t=1480059376323#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('iconfont.woff?t=1480059376323') format('woff'), /* chrome, firefox */ 6 | url('iconfont.ttf?t=1480059376323') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1480059376323#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -webkit-text-stroke-width: 0.2px; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | .icon-duihao:before { content: "\e623"; } 20 | 21 | .icon-duihao1:before { content: "\e621"; } 22 | 23 | .icon-duihao2:before { content: "\e608"; } 24 | 25 | -------------------------------------------------------------------------------- /src/assets/font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/font/iconfont.eot -------------------------------------------------------------------------------- /src/assets/font/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Created by FontForge 20120731 at Fri Nov 25 15:36:16 2016 6 | By admin 7 | 8 | 9 | 10 | 24 | 26 | 28 | 30 | 32 | 34 | 38 | 40 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/assets/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/font/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/font/iconfont.woff -------------------------------------------------------------------------------- /src/assets/images/css-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/images/css-64.png -------------------------------------------------------------------------------- /src/assets/images/folder-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/images/folder-64.png -------------------------------------------------------------------------------- /src/assets/images/js-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/images/js-64.png -------------------------------------------------------------------------------- /src/assets/images/psd-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/images/psd-64.png -------------------------------------------------------------------------------- /src/assets/images/zip-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iuhjui-r2r/r2r-web/47610e4341d1cf41ee24434e7d42e5d4c23612db/src/assets/images/zip-64.png -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | :global(@font-face) { 4 | font-family: "iconfont"; 5 | src: url('./assets/font/iconfont.eot'); /* IE9*/ 6 | src: url('./assets/font/iconfont.eot') format('embedded-opentype'), /* IE6-IE8 */ 7 | url('./assets/font/iconfont.woff') format('woff'), /* chrome, firefox */ 8 | url('./assets/font/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 9 | url('./assets/font/iconfont.svg') format('svg'); /* iOS 4.1- */ 10 | } 11 | 12 | :global(.iconfont) { 13 | font-family:"iconfont" !important; 14 | font-size:16px; 15 | font-style:normal; 16 | -webkit-font-smoothing: antialiased; 17 | -webkit-text-stroke-width: 0.2px; 18 | -moz-osx-font-smoothing: grayscale; 19 | } 20 | 21 | :global(.icon-duihao:before){ 22 | content: "\e623"; 23 | } 24 | 25 | 26 | :global(.icon-duihao1:before){ 27 | content: "\e621"; 28 | } 29 | 30 | 31 | :global(.icon-duihao2:before){ 32 | content: "\e608"; 33 | } 34 | 35 | 36 | 37 | :global(*){ 38 | margin: 0; 39 | padding: 0; 40 | } 41 | 42 | :global(html,body){ 43 | width: 100%; 44 | height: 100%; 45 | } 46 | 47 | :global(#root){ 48 | height: 100%; 49 | background: #EFF4F8; 50 | } 51 | 52 | 53 | /** react-contextmenu */ 54 | 55 | 56 | 57 | :global(.react-contextmenu){ 58 | min-width: 100px; 59 | padding: 2px 0; 60 | margin: 2px 0 0; 61 | font-size: 14px; 62 | color: #373a3c; 63 | text-align: left; 64 | background-color: #fff; 65 | background-clip: padding-box; 66 | border: 1px solid rgba(0,0,0,.15); 67 | border-radius: .25rem; 68 | outline: none; 69 | opacity: 0; 70 | } 71 | 72 | 73 | :global(.react-contextmenu.react-contextmenu--visible) { 74 | opacity: 1; 75 | pointer-events: auto; 76 | } 77 | 78 | 79 | :global(.react-contextmenu-item) { 80 | width: 100px; 81 | padding: 3px 20px; 82 | font-weight: 400; 83 | line-height: 1.5; 84 | color: #373a3c; 85 | text-align: inherit; 86 | white-space: nowrap; 87 | background: 0 0; 88 | border: 0; 89 | cursor: pointer; 90 | } 91 | 92 | 93 | :global(.react-contextmenu-item:hover) { 94 | color: #fff; 95 | background-color: #0275d8; 96 | border-color: #0275d8; 97 | text-decoration: none; 98 | } 99 | 100 | 101 | 102 | 103 | :global(.react-contextmenu-link) { 104 | display: inline-block; 105 | width: 100%; 106 | padding: 3px 20px; 107 | clear: both; 108 | font-weight: 400; 109 | line-height: 1.5; 110 | color: #373a3c; 111 | text-align: inherit; 112 | white-space: nowrap; 113 | background: 0 0; 114 | border: 0; 115 | } 116 | 117 | :global(.react-contextmenu-link.active, 118 | .react-contextmenu-link:hover) { 119 | color: #fff; 120 | background-color: #0275d8; 121 | border-color: #0275d8; 122 | text-decoration: none; 123 | } 124 | :global(.react-contextmenu-item.submenu > a) { 125 | padding-right: 27px; 126 | } 127 | 128 | :global(.react-contextmenu-item.submenu > a:after) { 129 | content: "▶"; 130 | display: inline-block; 131 | position: absolute; 132 | right: 7px; 133 | } 134 | 135 | :global(.example-multiple-targets::after ){ 136 | content: attr(data-count); 137 | display: block; 138 | } 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | R2R-WEB 分布式共享性网盘 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './index.html'; 2 | import './index.css'; 3 | import 'antd/dist/antd.css' 4 | import 'font-awesome/css/font-awesome.css' 5 | 6 | 7 | import dva from 'dva'; 8 | 9 | import { HomeModel,DiskModel } from "./models" 10 | 11 | 12 | 13 | // 1. Initialize 14 | const app = dva(); 15 | 16 | // 2. Plugins 17 | //app.use({}); 18 | 19 | // 3. Model 20 | app.model(HomeModel); 21 | app.model(DiskModel); 22 | 23 | // 4. Router 24 | app.router(require('./router')); 25 | 26 | // 5. Start 27 | app.start('#root'); 28 | 29 | -------------------------------------------------------------------------------- /src/models/DiskModel/DiskModel.js: -------------------------------------------------------------------------------- 1 | import DiskService from "../../services/DiskService" 2 | import { message } from "antd" 3 | 4 | 5 | 6 | 7 | 8 | const DiskModel = { 9 | namespace : "disk", 10 | state : { 11 | //路径,这是一个先进后出的栈结构,负责表达文件系统的层级结构 12 | path : [{id:0,name:"全部文件",type:"folder",parent_id:-1}], 13 | //目录树,负责表达目录的树结构 14 | tree : {id:0,name:"全部文件",list:[]}, 15 | //文件列表负责保存基本的文件对象 16 | list : [], 17 | //该目录下的总元素数 18 | total : 0, 19 | // 对话框相关 20 | modal : { 21 | mkdir : { 22 | visible : false, 23 | load : false, 24 | text : "", 25 | }, 26 | rename : { 27 | visible : false, 28 | load : false, 29 | text : "", 30 | source : "", 31 | target : "", 32 | }, 33 | move : { 34 | visible : false, 35 | load : false, 36 | source_id : "", 37 | target_id : "", 38 | }, 39 | copy : { 40 | visible : false, 41 | load : false, 42 | source_id : "", 43 | target_id : "", 44 | }, 45 | }, 46 | }, 47 | reducers : { 48 | update(state,{ payload }){ 49 | let data = {} 50 | if (payload.list){ 51 | data.list=payload.list 52 | } 53 | if (payload.path){ 54 | data.path=payload.path 55 | } 56 | if (payload.tree){ 57 | 58 | data.tree=payload.tree 59 | } 60 | return { ...state,...data } 61 | }, 62 | select( state ,{ payload : { file } }){ 63 | state.list.forEach((el,i)=>{ 64 | if (el.id == file.id){ 65 | el.select = !el.select 66 | return false; 67 | } 68 | }); 69 | return { ...state } 70 | }, 71 | // 文件夹创建相关 72 | mkdir_modal_open(state){ 73 | state.modal.mkdir.visible = true 74 | return { ...state } 75 | }, 76 | mkdir_modal_change(state,{ payload : { text } }){ 77 | state.modal.mkdir.text = text 78 | return { ...state } 79 | }, 80 | mkdir_modal_load(state,action){ 81 | state.modal.mkdir.load = action.payload 82 | return { ...state } 83 | }, 84 | mkdir_modal_close(state){ 85 | if (state.modal.mkdir.load){ 86 | return state 87 | } 88 | state.modal.mkdir.visible = false 89 | state.modal.mkdir.text = "" 90 | return { ...state } 91 | }, 92 | // 重命名相关 93 | rename_modal_open( state , { payload : { source } } ){ 94 | state.modal.rename.visible = true 95 | state.modal.rename.source = source 96 | return { ...state } 97 | }, 98 | rename_modal_change(state,{ payload : { text } }){ 99 | state.modal.rename.text = text 100 | return { ...state } 101 | }, 102 | rename_modal_load(state,action){ 103 | state.modal.rename.load = action.payload; 104 | return { ...state } 105 | }, 106 | rename_modal_close( state ){ 107 | if (state.modal.rename.load){ 108 | return state 109 | } 110 | state.modal.rename.visible = false 111 | state.modal.rename.text = "" 112 | state.modal.rename.source = "" 113 | return { ...state } 114 | }, 115 | //文件移动相关 116 | move_modal_open( state,{ payload : { source_id } } ){ 117 | state.modal.move.visible = true; 118 | state.modal.move.source_id = source_id+"" 119 | state.modal.move.target_id = "" 120 | return { ...state } 121 | }, 122 | move_modal_change(state,{ payload :{ target_id } }){ 123 | state.modal.move.target_id = target_id; 124 | return { ...state } 125 | }, 126 | move_modal_load(state,action){ 127 | state.modal.move.load = action.payload; 128 | return { ...state } 129 | }, 130 | move_modal_close(state){ 131 | if (state.modal.move.load){ 132 | return state 133 | } 134 | state.modal.move.visible = false; 135 | return { ...state } 136 | }, 137 | //文件复制相关 138 | copy_modal_open( state,{ payload : { source_id } } ){ 139 | state.modal.copy.visible = true; 140 | state.modal.copy.source_id = source_id+"" 141 | state.modal.copy.target_id = "" 142 | return { ...state } 143 | }, 144 | copy_modal_change(state,{ payload :{ target_id } }){ 145 | state.modal.copy.target_id = target_id; 146 | return { ...state } 147 | }, 148 | copy_modal_load(state,action){ 149 | state.modal.copy.load = action.payload; 150 | return { ...state } 151 | }, 152 | copy_modal_close(state){ 153 | if (state.modal.copy.load){ 154 | return state 155 | } 156 | state.modal.copy.visible = false; 157 | return { ...state } 158 | }, 159 | }, 160 | effects : { 161 | *fetch_list({ payload : { parent_id = 0 } },{call,put}){ 162 | let { data } = yield call(DiskService.fetch_list, { parent_id }); 163 | if (data.success) { 164 | yield put({type:"update",payload : data }); 165 | }else{ 166 | message.error(data.info); 167 | } 168 | }, 169 | *fetch_tree( { payload }, { call,put } ){ 170 | let { data } = yield call(DiskService.fetch_tree,{}); 171 | if (data.success) { 172 | yield put({type:"update",payload : data }); 173 | }else{ 174 | message.error(data.info); 175 | } 176 | }, 177 | *open_folder( { payload : { folder } },{ call,put } ){ 178 | yield put({type : "fetch_list",payload:{ parent_id:folder.id } }) 179 | }, 180 | *mkdir({ payload : { parent_id,name } },{ call,put }){ 181 | yield put({type : "mkdir_modal_load",payload:true}); 182 | let { data } = yield call(DiskService.mkdir, { parent_id,name }); 183 | yield put({type : "mkdir_modal_load",payload:false}); 184 | yield put({type : "mkdir_modal_close"}); 185 | 186 | if (data.success) { 187 | yield put({type:"fetch_list",payload:{ parent_id }}) 188 | yield put({type:"fetch_tree",payload:{ }}) 189 | }else{ 190 | message.error(data.info); 191 | } 192 | }, 193 | *rename( { payload : { parent_id,source,target } },{ call ,put } ){ 194 | yield put({type : "rename_modal_load",payload:true}); 195 | let { data } = yield call(DiskService.rename, { parent_id,source,target }); 196 | yield put({type : "rename_modal_load",payload:false}); 197 | yield put({type : "rename_modal_close"}); 198 | 199 | if (data.success ){ 200 | yield put({type : "fetch_list",payload:{parent_id}}) 201 | yield put({type : "fetch_tree",payload:{}}) 202 | }else{ 203 | message.error(data.info) 204 | } 205 | }, 206 | *remove( { payload :{ parent_id,id } },{ call,put } ){ 207 | let { data } = yield call(DiskService.remove, { id }); 208 | if (data.success ){ 209 | yield put({type : "fetch_list",payload:{parent_id}}) 210 | yield put({type : "fetch_tree",payload:{}}) 211 | }else{ 212 | message.error(data.info) 213 | } 214 | }, 215 | *move( { payload : { parent_id,source_id, target_id} },{ call,put } ){ 216 | yield put({type : "move_modal_load",payload:true}); 217 | let { data } = yield call(DiskService.move, { source_id,target_id }); 218 | yield put({type : "move_modal_load",payload:false}); 219 | yield put({type : "move_modal_close"}); 220 | 221 | if (data.success) { 222 | yield put({type:"fetch_list",payload:{ parent_id }}) 223 | yield put({type:"fetch_tree",payload:{ }}) 224 | }else{ 225 | message.error(data.info); 226 | } 227 | }, 228 | *copy( { payload : { parent_id,source_id, target_id} },{ call,put } ){ 229 | yield put({type : "copy_modal_load",payload:true}); 230 | let { data } = yield call(DiskService.copy, { source_id,target_id }); 231 | yield put({type : "copy_modal_load",payload:false}); 232 | yield put({type : "copy_modal_close"}); 233 | 234 | if (data.success) { 235 | yield put({type:"fetch_list",payload:{ parent_id }}) 236 | yield put({type:"fetch_tree",payload:{ }}) 237 | }else{ 238 | message.error(data.info); 239 | } 240 | }, 241 | }, 242 | subscriptions : { 243 | setup({ dispatch, history }) { 244 | return history.listen(({ pathname, query }) => { 245 | if (pathname === '/') { 246 | dispatch({ type: 'fetch_list', payload:{} }); 247 | dispatch({ type: 'fetch_tree', payload:{} }); 248 | } 249 | }); 250 | }, 251 | }, 252 | }; 253 | 254 | 255 | export default DiskModel -------------------------------------------------------------------------------- /src/models/DiskModel/index.js: -------------------------------------------------------------------------------- 1 | import DiskModel from "./DiskModel.js" 2 | 3 | export default DiskModel -------------------------------------------------------------------------------- /src/models/HomeModel/HomeModel.js: -------------------------------------------------------------------------------- 1 | 2 | const HomeModel = { 3 | namespace : "home", 4 | state : {}, 5 | reducers : {}, 6 | 7 | }; 8 | 9 | 10 | export default HomeModel -------------------------------------------------------------------------------- /src/models/HomeModel/index.js: -------------------------------------------------------------------------------- 1 | import HomeModel from "./HomeModel.js" 2 | 3 | export default HomeModel -------------------------------------------------------------------------------- /src/models/index.js: -------------------------------------------------------------------------------- 1 | import HomeModel from "./HomeModel" 2 | import DiskModel from "./DiskModel" 3 | 4 | export { HomeModel,DiskModel } -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/File/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/File/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | 4 | 5 | const File = ({ isSelect,type,name,onSelect }) => { 6 | 7 | let active = isSelect ? styles.active : ''; 8 | let icon = styles['icon_'+type]; 9 | if (!icon) icon=styles['icon_file']; 10 | 11 | return ( 12 |
13 |
14 |
15 | {name} 16 |
17 | 18 |
19 | ) 20 | } 21 | 22 | 23 | File.propTypes={}; 24 | 25 | 26 | export default File; 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/File/styles.css: -------------------------------------------------------------------------------- 1 | .file { 2 | margin-top: 20px; 3 | width: 122px; 4 | height: 129px; 5 | display: inline-block; 6 | margin-left:10px; 7 | margin-right: 10px; 8 | text-align: center; 9 | cursor: pointer; 10 | position: relative; 11 | } 12 | 13 | .file:hover { 14 | background-color: #f1f5fa; 15 | border-radius: 5px; 16 | } 17 | 18 | .active { 19 | background-color: #f1f5fa; 20 | border: 1px solid #90c3fd; 21 | border-radius: 5px; 22 | } 23 | 24 | .checkbox { 25 | font-family:"iconfont"; 26 | font-size:16px; 27 | color:#3b8cff; 28 | position: absolute; 29 | top: 5px; 30 | left: 5px; 31 | height: 21px; 32 | width: 21px; 33 | cursor: pointer; 34 | } 35 | 36 | .checkbox:before { 37 | content: "\e608"; 38 | } 39 | 40 | 41 | .file .checkbox { 42 | display:none; 43 | } 44 | 45 | .file:hover .checkbox { 46 | display:block; 47 | opacity:0.5; 48 | } 49 | 50 | .file.active .checkbox { 51 | display:block; 52 | opacity:1; 53 | } 54 | 55 | 56 | 57 | .text { 58 | margin-top: 12px; 59 | font-family:"微软雅黑"; 60 | font-size:12px; 61 | } 62 | 63 | .icon_file{ 64 | width: 64px; 65 | height: 64px; 66 | background: url("../../../../assets/images/zip-64.png") no-repeat center ; 67 | display: inline-block; 68 | margin-top: 20px; 69 | } 70 | 71 | .icon_folder{ 72 | width: 64px; 73 | height: 64px; 74 | background: url("../../../../assets/images/folder-64.png") no-repeat center ; 75 | display: inline-block; 76 | margin-top: 20px; 77 | } 78 | 79 | .icon_zip{ 80 | width: 64px; 81 | height: 64px; 82 | background: url("../../../../assets/images/zip-64.png") no-repeat center ; 83 | display: inline-block; 84 | margin-top: 20px; 85 | } 86 | 87 | .icon_css{ 88 | width: 64px; 89 | height: 64px; 90 | background: url("../../../../assets/images/css-64.png") no-repeat center ; 91 | display: inline-block; 92 | margin-top: 20px; 93 | } 94 | 95 | .icon_js{ 96 | width: 64px; 97 | height: 64px; 98 | background: url("../../../../assets/images/js-64.png") no-repeat center ; 99 | display: inline-block; 100 | margin-top: 20px; 101 | } 102 | 103 | 104 | .icon_psd{ 105 | width: 64px; 106 | height: 64px; 107 | background: url("../../../../assets/images/psd-64.png") no-repeat center ; 108 | display: inline-block; 109 | margin-top: 20px; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/FileMenu/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/FileMenu/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ContextMenu, MenuItem, ContextMenuTrigger,SubMenu } from "react-contextmenu"; 3 | 4 | 5 | const FileMenu = ({ id,onEvent })=>{ 6 | 7 | const onHandle = (e,data,target) => { 8 | onEvent(data); 9 | } 10 | 11 | return ( 12 | 13 | 14 | 下载 15 | 16 | 17 | 分享 18 | 19 | 20 | 复制到 21 | 22 | 23 | 移动到 24 | 25 | 26 | 重命名 27 | 28 | 29 | 删除 30 | 31 | 32 | ); 33 | } 34 | 35 | FileMenu.propTypes={}; 36 | 37 | 38 | export default FileMenu; -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/FolderMenu/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/FolderMenu/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ContextMenu, MenuItem, ContextMenuTrigger,SubMenu } from "react-contextmenu"; 3 | 4 | 5 | const FolderMenu = ({ id,onEvent })=>{ 6 | 7 | const onHandle = (e,data,target) => { 8 | onEvent(data); 9 | } 10 | 11 | return ( 12 | 13 | 14 | 打开 15 | 16 | 17 | 分享 18 | 19 | 20 | 复制到 21 | 22 | 23 | 移动到 24 | 25 | 26 | 重命名 27 | 28 | 29 | 删除 30 | 31 | 32 | ); 33 | } 34 | 35 | FolderMenu.propTypes={}; 36 | 37 | 38 | export default FolderMenu; -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { ContextMenuTrigger} from "react-contextmenu"; 4 | 5 | import File from "./File" 6 | import FileMenu from './FileMenu'; 7 | import FolderMenu from "./FolderMenu" 8 | 9 | 10 | const FileMenuID = "file_menu_id" 11 | const FolderMenuID = "folder_menu_id" 12 | 13 | 14 | const collect = (props) => { 15 | return { fileID: props.fileID }; 16 | } 17 | 18 | const CreateFile = ({fileID,name,type,select,onSelect})=>{ 19 | let onHandle = () =>{ 20 | onSelect(fileID) 21 | } 22 | return ( 23 |
24 | 25 | 26 | 27 |
28 | ) 29 | } 30 | 31 | const CreateFolder = ({ fileID,name,select,onSelect }) => { 32 | let onHandle = () =>{ 33 | onSelect(fileID) 34 | } 35 | 36 | return ( 37 |
38 | 39 | 40 | 41 |
42 | ); 43 | } 44 | 45 | 46 | const BlockBrowser = ({ list,bus }) => { 47 | 48 | let onSelect = (fileID) => { 49 | let file = list[fileID] 50 | bus.publish("SELECT", { file }); 51 | } 52 | 53 | let onMenu = ({ type,fileID }) => { 54 | if ( type == "rename" ){ 55 | let file = list[fileID]; 56 | return bus.publish("RENAME_MODAL_OPEN",{file}); 57 | } 58 | if ( type == "open_folder" ){ 59 | let folder = list[fileID] 60 | return bus.publish("OPEN_FOLDER",{ folder }); 61 | } 62 | if ( type == "remove" ){ 63 | let file = list[fileID] 64 | return bus.publish("REMOVE_FILE",{ file }); 65 | } 66 | if ( type == "move" ){ 67 | let file = list[fileID] 68 | return bus.publish("MOVE_MODAL_OPEN",{ file }); 69 | } 70 | if ( type == "copy" ){ 71 | let file = list[fileID] 72 | return bus.publish("COPY_MODAL_OPEN",{ file }); 73 | } 74 | } 75 | 76 | 77 | let FileList = list.map(function(el,i){ 78 | let fileID = i; 79 | if (el.type == "folder") { 80 | return CreateFolder({ ...el,fileID,onSelect}) 81 | }else{ 82 | return CreateFile({ ...el,fileID,onSelect }); 83 | } 84 | }) 85 | 86 | return ( 87 |
88 | {[ FileList ]} 89 | 90 | 91 |
92 | ) 93 | } 94 | 95 | 96 | BlockBrowser.propTypes={}; 97 | 98 | 99 | export default BlockBrowser; 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/pages/DiskPage/BlockBrowser/styles.css: -------------------------------------------------------------------------------- 1 | .block_browser{ 2 | } 3 | 4 | .file{ 5 | width: 122px; 6 | margin-left:4px; 7 | margin-right:4px; 8 | display:inline-block; 9 | } 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/pages/DiskPage/CheckAll/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/CheckAll/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { Checkbox } from 'antd'; 4 | 5 | const CheckAll = (props) => { 6 | return ( 7 |
8 | 9 | 全选 10 | 11 |
12 | ) 13 | } 14 | 15 | 16 | CheckAll.propTypes={}; 17 | 18 | 19 | export default CheckAll; 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/pages/DiskPage/CheckAll/styles.css: -------------------------------------------------------------------------------- 1 | .check_all{ 2 | padding-top:5px; 3 | padding-left:15px; 4 | } -------------------------------------------------------------------------------- /src/pages/DiskPage/CopyModal/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/CopyModal/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { message,Modal, Tree } from 'antd'; 4 | 5 | const Node = Tree.TreeNode; 6 | 7 | 8 | // 映射到树组件 9 | const mapToTree = (node) => { 10 | if (node.list.length > 0){ 11 | let comps = []; 12 | node.list.forEach((el)=>{ 13 | let comp = mapToTree(el) 14 | comps.push(comp) 15 | }); 16 | return ( 17 | 18 | { comps } 19 | 20 | ); 21 | }else{ 22 | return ( 23 | 24 | ); 25 | } 26 | } 27 | 28 | 29 | const CopyModal = ({ tree,visible,load,source_id,target_id,bus })=>{ 30 | 31 | const onSelect = ( keys ) =>{ 32 | let key = keys[0] 33 | if(key){ 34 | bus.publish("COPY_MODAL_CHANGE",{ key }) 35 | }else{ 36 | bus.publish("COPY_MODAL_CHANGE",{ key:"" }) 37 | } 38 | } 39 | 40 | let onOk = ()=>{ 41 | if (target_id == ""){ 42 | message.error("请选择一个目录") 43 | return; 44 | } 45 | bus.publish("COPY_MODAL_CONFIRM",{source_id,target_id}) 46 | } 47 | 48 | const onCancel = ()=>{ 49 | bus.publish("COPY_MODAL_CLOSE",{}); 50 | } 51 | 52 | 53 | let comp = mapToTree(tree); 54 | 55 | return ( 56 |
57 | 65 |
66 | 70 | { comp } 71 | 72 |
73 |
74 |
75 | ) 76 | } 77 | 78 | CopyModal.propTypes={}; 79 | 80 | 81 | export default CopyModal; 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/pages/DiskPage/CopyModal/styles.css: -------------------------------------------------------------------------------- 1 | .panel{ 2 | width:100%; 3 | height: 200px; 4 | } 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Layout/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/Layout/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import Scroll from 'react-scrollbar'; 4 | 5 | 6 | const Layout = ({ Menu,Search,Mode,Path,Total,CheckAll,Browser })=>{ 7 | return ( 8 |
9 |
10 |
11 | { Menu } 12 |
13 |
14 | { Mode } 15 |
16 |
17 | { Search } 18 |
19 |
20 |
21 |
22 | { Path } 23 |
24 |
25 | { Total } 26 |
27 |
28 |
29 |
30 | { CheckAll } 31 |
32 |
33 |
34 |
35 | 36 | { Browser } 37 | 38 |
39 |
40 |
41 | ) 42 | } 43 | 44 | Layout.propTypes={}; 45 | 46 | 47 | export default Layout; 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Layout/styles.css: -------------------------------------------------------------------------------- 1 | .root{ 2 | width:100%; 3 | height: 100%; 4 | background: #fff; 5 | border-radius: 10px; 6 | } 7 | 8 | 9 | .layer1{ 10 | height:50px; 11 | } 12 | 13 | .layer2{ 14 | height:20px; 15 | } 16 | 17 | .layer3{ 18 | height:29px; 19 | } 20 | 21 | .layer4{ 22 | border-top: 1px solid #d8dfea; 23 | width:100%; 24 | height: calc(100% - 100px ); 25 | } 26 | 27 | 28 | .menu{ 29 | float:left; 30 | } 31 | 32 | .search{ 33 | float:right; 34 | } 35 | 36 | .mode{ 37 | float:right; 38 | } 39 | 40 | .path{ 41 | float:left; 42 | } 43 | 44 | .total{ 45 | float:right; 46 | } 47 | 48 | .check_all{ 49 | 50 | } 51 | 52 | .browser{ 53 | width:100%; 54 | height:100%; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Menu/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/Menu/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { Button } from 'antd'; 4 | 5 | 6 | const Menu = ({ bus })=>{ 7 | 8 | let onMkdir = ()=>{ 9 | bus.publish("MKDIR_MODAL_OPEN") 10 | } 11 | 12 | 13 | return ( 14 |
15 |
16 | 22 |
23 |
24 | 30 |
31 |
32 | 38 |
39 |
40 | 46 |
47 |
48 | ); 49 | }; 50 | 51 | 52 | Menu.propTypes={} 53 | 54 | export default Menu; 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Menu/styles.css: -------------------------------------------------------------------------------- 1 | .menu{ 2 | padding-top: 10px; 3 | padding-left: 15px; 4 | } 5 | 6 | 7 | .item{ 8 | display:inline-block; 9 | margin-right:10px; 10 | } 11 | 12 | .text{ 13 | display:inline; 14 | margin-left:3px 15 | } 16 | -------------------------------------------------------------------------------- /src/pages/DiskPage/MkdirModal/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/MkdirModal/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { message,Modal,Input } from 'antd'; 4 | 5 | 6 | 7 | const MkdirModal = ({ visible,load,text,bus })=>{ 8 | let onChange = (e)=>{ 9 | let val = e.target.value; 10 | bus.publish("MKDIR_MODAL_CHANGE",{text:val}) 11 | } 12 | 13 | let onOk = ()=>{ 14 | if (text == ""){ 15 | message.error("文件名不能为空") 16 | return; 17 | } 18 | bus.publish("MKDIR_MODAL_CONFIRM",{name:text}) 19 | } 20 | 21 | let onCancel = ()=>{ 22 | bus.publish("MKDIR_MODAL_CLOSE") 23 | } 24 | 25 | return ( 26 |
27 | 35 | 40 | 41 |
42 | ) 43 | } 44 | 45 | MkdirModal.propTypes={}; 46 | 47 | 48 | export default MkdirModal; 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/pages/DiskPage/MkdirModal/styles.css: -------------------------------------------------------------------------------- 1 | .root{ 2 | width:100%; 3 | height: 100%; 4 | background: #fff; 5 | border-radius: 10px; 6 | } 7 | 8 | 9 | .layer1{ 10 | height:50px; 11 | } 12 | 13 | .layer2{ 14 | height:20px; 15 | } 16 | 17 | .layer3{ 18 | height:29px; 19 | } 20 | 21 | .layer4{ 22 | border-top: 1px solid #d8dfea; 23 | width:100%; 24 | height: calc(100% - 100px ); 25 | } 26 | 27 | 28 | .menu{ 29 | float:left; 30 | } 31 | 32 | .search{ 33 | float:right; 34 | } 35 | 36 | .mode{ 37 | float:right; 38 | } 39 | 40 | .path{ 41 | float:left; 42 | } 43 | 44 | .total{ 45 | float:right; 46 | } 47 | 48 | .check_all{ 49 | 50 | } 51 | 52 | .browser{ 53 | width:100%; 54 | height:100%; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Mode/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/Mode/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { Radio } from 'antd'; 4 | 5 | 6 | const Mode = (props) => { 7 | return ( 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | ) 19 | } 20 | 21 | 22 | 23 | Mode.propTypes={}; 24 | 25 | 26 | export default Mode; 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Mode/styles.css: -------------------------------------------------------------------------------- 1 | .mode{ 2 | padding-right: 15px; 3 | padding-top: 15px; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/pages/DiskPage/MoveModal/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/MoveModal/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { message,Modal, Tree } from 'antd'; 4 | 5 | const Node = Tree.TreeNode; 6 | 7 | 8 | // 映射到树组件 9 | const mapToTree = (node) => { 10 | if (node.list.length > 0){ 11 | let comps = []; 12 | node.list.forEach((el)=>{ 13 | let comp = mapToTree(el) 14 | comps.push(comp) 15 | }); 16 | return ( 17 | 18 | { comps } 19 | 20 | ); 21 | }else{ 22 | return ( 23 | 24 | ); 25 | } 26 | } 27 | 28 | 29 | const MoveModal = ({ tree,visible,load,source_id,target_id,bus })=>{ 30 | 31 | const onSelect = ( keys ) =>{ 32 | let key = keys[0] 33 | if(key){ 34 | bus.publish("MOVE_MODAL_CHANGE",{ key }) 35 | }else{ 36 | bus.publish("MOVE_MODAL_CHANGE",{ key:"" }) 37 | } 38 | } 39 | 40 | let onOk = ()=>{ 41 | if (target_id == ""){ 42 | message.error("请选择一个目录") 43 | return; 44 | } 45 | bus.publish("MOVE_MODAL_CONFIRM",{source_id,target_id}) 46 | } 47 | 48 | const onCancel = ()=>{ 49 | bus.publish("MOVE_MODAL_CLOSE",{}); 50 | } 51 | 52 | 53 | let comp = mapToTree(tree); 54 | 55 | return ( 56 |
57 | 65 |
66 | 70 | { comp } 71 | 72 |
73 |
74 |
75 | ) 76 | } 77 | 78 | MoveModal.propTypes={}; 79 | 80 | 81 | export default MoveModal; 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/pages/DiskPage/MoveModal/styles.css: -------------------------------------------------------------------------------- 1 | .panel{ 2 | width:100%; 3 | height: 200px; 4 | } 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Path/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/Path/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { Breadcrumb } from 'antd'; 4 | 5 | 6 | 7 | const makeLast = ( folder ) =>{ 8 | return ( 9 | 10 | { folder.name } 11 | 12 | ); 13 | } 14 | 15 | 16 | const makeChain = (data,onEvent)=>{ 17 | let array = [] 18 | if (data.length == 0){ 19 | return [] 20 | } 21 | let last = makeLast(data.pop()); 22 | data.forEach((folder,i)=>{ 23 | const onClick = ()=>{ 24 | onEvent(folder) 25 | } 26 | let comp = ( 27 | 28 | { folder.name } 29 | 30 | ); 31 | array.push(comp) 32 | }); 33 | array.push(last) 34 | 35 | return array 36 | } 37 | 38 | 39 | const Path = ({ path,bus }) => { 40 | let data = [ ...path ] 41 | if( data.length == 1 ){ 42 | return ( 43 |
44 |

全部文件

45 |
46 | ); 47 | } 48 | 49 | var root = data.shift() 50 | var prev = data[data.length-2]; 51 | 52 | 53 | const onRoot = () => { 54 | bus.publish("OPEN_FOLDER",{ folder:root }) 55 | } 56 | 57 | const onPrev = () => { 58 | if (prev){ 59 | bus.publish("OPEN_FOLDER",{ folder:prev }) 60 | }else{ 61 | bus.publish("OPEN_FOLDER",{ folder:root }) 62 | } 63 | } 64 | 65 | const onEvent = (folder) => { 66 | bus.publish("OPEN_FOLDER",{ folder }) 67 | } 68 | 69 | var chain = makeChain(data,onEvent) 70 | 71 | 72 | return ( 73 |
74 |
75 | 76 | 返回上一级 77 | 78 | 79 | | 80 | 81 | 82 | 全部文件 83 | 84 | 85 | | 86 | 87 |
88 |
89 | 90 | {[ chain ]} 91 | 92 |
93 |
94 | ); 95 | } 96 | 97 | 98 | Path.propTypes={}; 99 | 100 | 101 | export default Path; 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Path/styles.css: -------------------------------------------------------------------------------- 1 | .path{ 2 | padding-left: 15px; 3 | } 4 | 5 | 6 | .anchor{ 7 | display:inline-block; 8 | } 9 | 10 | .anchor > a { 11 | display:inline-block; 12 | } 13 | 14 | .chain{ 15 | display:inline-block; 16 | } 17 | 18 | .delim{ 19 | padding-left:5px; 20 | padding-right:5px; 21 | display:inline-block; 22 | } -------------------------------------------------------------------------------- /src/pages/DiskPage/RenameModal/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/RenameModal/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { message,Modal,Input } from 'antd'; 4 | 5 | 6 | 7 | const RenameModal = ({ visible,load,text,bus })=>{ 8 | let onChange = (e)=>{ 9 | let val = e.target.value; 10 | bus.publish("RENAME_MODAL_CHANGE",{text:val}) 11 | } 12 | 13 | let onOk = ()=>{ 14 | if (text == ""){ 15 | message.error("文件名不能为空") 16 | return; 17 | } 18 | bus.publish("RENAME_MODAL_CONFIRM",{name:text}) 19 | } 20 | 21 | let onCancel = ()=>{ 22 | bus.publish("RENAME_MODAL_CLOSE") 23 | } 24 | 25 | return ( 26 |
27 | 35 | 40 | 41 |
42 | ) 43 | } 44 | 45 | RenameModal.propTypes={}; 46 | 47 | 48 | export default RenameModal; 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/pages/DiskPage/RenameModal/styles.css: -------------------------------------------------------------------------------- 1 | .root{ 2 | width:100%; 3 | height: 100%; 4 | background: #fff; 5 | border-radius: 10px; 6 | } 7 | 8 | 9 | .layer1{ 10 | height:50px; 11 | } 12 | 13 | .layer2{ 14 | height:20px; 15 | } 16 | 17 | .layer3{ 18 | height:29px; 19 | } 20 | 21 | .layer4{ 22 | border-top: 1px solid #d8dfea; 23 | width:100%; 24 | height: calc(100% - 100px ); 25 | } 26 | 27 | 28 | .menu{ 29 | float:left; 30 | } 31 | 32 | .search{ 33 | float:right; 34 | } 35 | 36 | .mode{ 37 | float:right; 38 | } 39 | 40 | .path{ 41 | float:left; 42 | } 43 | 44 | .total{ 45 | float:right; 46 | } 47 | 48 | .check_all{ 49 | 50 | } 51 | 52 | .browser{ 53 | width:100%; 54 | height:100%; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Search/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/Search/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { Input } from 'antd'; 4 | 5 | 6 | const Search = (props)=>{ 7 | return ( 8 |
9 | 12 |
13 | ) 14 | } 15 | 16 | 17 | Search.propTypes={}; 18 | 19 | export default Search; 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Search/styles.css: -------------------------------------------------------------------------------- 1 | .search{ 2 | width:200px; 3 | padding-right: 15px; 4 | padding-top: 15px; 5 | } -------------------------------------------------------------------------------- /src/pages/DiskPage/Total/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/Total/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | 4 | 5 | const Total = (props)=>{ 6 | return ( 7 |
8 |

已全部加载,共60个

9 |
10 | ) 11 | } 12 | 13 | 14 | Total.propTypes={}; 15 | 16 | export default Total; 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/pages/DiskPage/Total/styles.css: -------------------------------------------------------------------------------- 1 | .total{ 2 | padding-right:15px; 3 | } -------------------------------------------------------------------------------- /src/pages/DiskPage/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/DiskPage/jsx.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import EventBus from "@nsisodiya/eventbus"; 3 | 4 | import styles from "./styles.css"; 5 | import { connect } from 'dva'; 6 | import { message } from "antd"; 7 | 8 | 9 | import Layout from "./Layout" 10 | import Menu from "./Menu" 11 | import Search from "./Search" 12 | import Mode from "./Mode" 13 | import Path from "./Path" 14 | import Total from "./Total" 15 | import CheckAll from "./CheckAll" 16 | import BlockBrowser from "./BlockBrowser" 17 | import MkdirModal from "./MkdirModal" 18 | import RenameModal from "./RenameModal" 19 | import MoveModal from "./MoveModal" 20 | import CopyModal from "./CopyModal" 21 | 22 | 23 | function DiskPage({ disk ,dispatch }){ 24 | 25 | let bus = new EventBus() 26 | bus.subscribe("SELECT",function( { file } ){ 27 | dispatch({ 28 | type : "disk/select", 29 | payload:{ file } 30 | }); 31 | }); 32 | 33 | //目录相关 34 | bus.subscribe("OPEN_FOLDER",function({ folder }){ 35 | dispatch({ 36 | type : "disk/open_folder", 37 | payload : { folder } 38 | }); 39 | }); 40 | 41 | //文件夹创建相关 42 | bus.subscribe("MKDIR_MODAL_OPEN",function(){ 43 | dispatch({ 44 | type:"disk/mkdir_modal_open" 45 | }); 46 | }); 47 | bus.subscribe("MKDIR_MODAL_CHANGE",function({ text }){ 48 | dispatch({ 49 | type : "disk/mkdir_modal_change", 50 | payload:{text}, 51 | }); 52 | }); 53 | bus.subscribe("MKDIR_MODAL_CONFIRM",function({ name }){ 54 | let parent_id = disk.path[disk.path.length-1].id 55 | dispatch({ 56 | type : "disk/mkdir", 57 | payload : { parent_id,name } 58 | }); 59 | }); 60 | bus.subscribe("MKDIR_MODAL_CLOSE",function(){ 61 | dispatch({ 62 | type : "disk/mkdir_modal_close", 63 | }); 64 | }); 65 | 66 | //重命名相关 67 | bus.subscribe("RENAME_MODAL_OPEN",function({ file }){ 68 | dispatch({ 69 | type:"disk/rename_modal_open", 70 | payload : { source:file.name } 71 | }); 72 | }); 73 | bus.subscribe("RENAME_MODAL_CHANGE",function({ text }){ 74 | dispatch({ 75 | type : "disk/rename_modal_change", 76 | payload : { text }, 77 | }); 78 | }); 79 | bus.subscribe("RENAME_MODAL_CONFIRM",function( { name } ){ 80 | let parent_id = disk.path[disk.path.length-1].id 81 | dispatch({ 82 | type : "disk/rename", 83 | payload : { 84 | parent_id : parent_id, 85 | source : disk.modal.rename.source, 86 | target : name 87 | } 88 | }); 89 | }); 90 | bus.subscribe("RENAME_MODAL_CLOSE",function(){ 91 | dispatch({ 92 | type : "disk/rename_modal_close", 93 | }); 94 | }); 95 | 96 | //删除相关 97 | bus.subscribe("REMOVE_FILE",function({ file }){ 98 | let parent_id = disk.path[disk.path.length-1].id 99 | dispatch({ 100 | type : "disk/remove", 101 | payload:{ parent_id:parent_id,id:file.id } 102 | }); 103 | }); 104 | 105 | //移动文件相关 106 | bus.subscribe("MOVE_MODAL_OPEN",function({ file }){ 107 | dispatch({ 108 | type : "disk/move_modal_open", 109 | payload : { source_id :file.id+"" }, 110 | }); 111 | }); 112 | bus.subscribe("MOVE_MODAL_CHANGE",function({ key }){ 113 | dispatch({ 114 | type : "disk/move_modal_change", 115 | payload : { target_id:key }, 116 | }); 117 | }); 118 | bus.subscribe("MOVE_MODAL_CONFIRM",function( { source_id,target_id } ){ 119 | let parent_id = disk.path[disk.path.length-1].id 120 | dispatch({ 121 | type : "disk/move", 122 | payload : { 123 | parent_id : parent_id, 124 | source_id : source_id, 125 | target_id : target_id, 126 | } 127 | }); 128 | }); 129 | bus.subscribe("MOVE_MODAL_CLOSE",function(){ 130 | dispatch({ 131 | type : "disk/move_modal_close", 132 | payload : {}, 133 | }); 134 | }); 135 | 136 | //复制文件相关 137 | bus.subscribe("COPY_MODAL_OPEN",function({ file }){ 138 | dispatch({ 139 | type : "disk/copy_modal_open", 140 | payload : { source_id :file.id+"" }, 141 | }); 142 | }); 143 | bus.subscribe("COPY_MODAL_CHANGE",function({ key }){ 144 | dispatch({ 145 | type : "disk/copy_modal_change", 146 | payload : { target_id:key }, 147 | }); 148 | }); 149 | bus.subscribe("COPY_MODAL_CONFIRM",function( { source_id,target_id } ){ 150 | let parent_id = disk.path[disk.path.length-1].id 151 | dispatch({ 152 | type : "disk/copy", 153 | payload : { 154 | parent_id : parent_id, 155 | source_id : source_id, 156 | target_id : target_id, 157 | } 158 | }); 159 | }); 160 | bus.subscribe("COPY_MODAL_CLOSE",function(){ 161 | dispatch({ 162 | type : "disk/copy_modal_close", 163 | payload : {}, 164 | }); 165 | }); 166 | 167 | 168 | 169 | return ( 170 |
171 | )} 173 | Search = {( )} 174 | Mode = {( )} 175 | Path = {( )} 176 | Total = {( )} 177 | CheckAll = {( )} 178 | Browser = {( )} 179 | /> 180 | 186 | 192 | 200 | 208 |
209 | ); 210 | }; 211 | 212 | DiskPage.propTypes = { 213 | }; 214 | 215 | function mapStateToProps({disk}) { 216 | return {disk}; 217 | } 218 | 219 | export default connect(mapStateToProps)(DiskPage); 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /src/pages/DiskPage/styles.css: -------------------------------------------------------------------------------- 1 | .disk{ 2 | width:100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/HomePage/Layout/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/HomePage/Layout/jsx.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { Row , Col } from 'antd' 3 | import styles from './styles.css'; 4 | 5 | 6 | 7 | function Layout({ Nav , Side , Render }){ 8 | return ( 9 | 10 | 11 | 12 | 13 | R2R-WEB 14 | 15 | 16 | 17 | { Nav } 18 | 19 | 20 | 21 | 22 | { Side } 23 | 24 | 25 |
26 | { Render } 27 |
28 | 29 |
30 |
31 | ); 32 | }; 33 | 34 | Layout.propTypes = { 35 | }; 36 | 37 | 38 | export default Layout 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/pages/HomePage/Layout/styles.css: -------------------------------------------------------------------------------- 1 | .all { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | .top { 7 | height: 62px; 8 | } 9 | 10 | .logo{ 11 | width: 100%; 12 | height: 100%; 13 | display: block; 14 | font-size: 24px; 15 | font-weight: bold; 16 | color: #4E97FF; 17 | text-align: center; 18 | margin-top: 15px; 19 | } 20 | 21 | .main { 22 | height: calc(100% - 62px ); 23 | } 24 | 25 | .side { 26 | height: 100%; 27 | } 28 | 29 | .content{ 30 | height: 100%; 31 | } 32 | 33 | 34 | .render { 35 | width: calc(100% - 10px); 36 | height: 100%; 37 | background: #fff; 38 | border: 1px solid #d8dfea; 39 | border-radius: 10px; 40 | } 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/pages/HomePage/Nav/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/HomePage/Nav/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | 4 | 5 | const Nav = (props)=>{ 6 | return ( 7 | 13 | ); 14 | } 15 | 16 | Nav.propTypes={} 17 | 18 | export default Nav; 19 | -------------------------------------------------------------------------------- /src/pages/HomePage/Nav/styles.css: -------------------------------------------------------------------------------- 1 | .nav{ 2 | height: 62px; 3 | width: 100%; 4 | font-size: 16px; 5 | 6 | font-family: "微软雅黑"; 7 | } 8 | 9 | .nav > li{ 10 | display: inline-block; 11 | height: 100%; 12 | width: 150px; 13 | line-height: 62px; 14 | text-align: center; 15 | cursor: pointer; 16 | } 17 | 18 | .nav > li:hover { 19 | color: #2db7f5; 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/HomePage/Side/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/HomePage/Side/jsx.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | 4 | const Side = (props) => { 5 | return ( 6 | 50 | ); 51 | } 52 | 53 | Side.propTypes={}; 54 | 55 | export default Side; 56 | -------------------------------------------------------------------------------- /src/pages/HomePage/Side/styles.css: -------------------------------------------------------------------------------- 1 | .side { 2 | width: 100%; 3 | font-size: 16px; 4 | padding-top: 20px; 5 | } 6 | 7 | .side > li { 8 | height: 50px; 9 | line-height: 50px; 10 | width: 100%; 11 | font-size: 0px; 12 | } 13 | 14 | .side > li:hover { 15 | background: #E4E9EC; 16 | color: #2db7f5; 17 | } 18 | 19 | .active { 20 | background: #E4E9EC; 21 | color: #2db7f5; 22 | } 23 | 24 | .left{ 25 | display: inline-block; 26 | font-size: 16px; 27 | width: 40px; 28 | } 29 | 30 | .right{ 31 | display: inline-block; 32 | font-size: 16px; 33 | } 34 | -------------------------------------------------------------------------------- /src/pages/HomePage/index.js: -------------------------------------------------------------------------------- 1 | import Component from "./jsx.js" 2 | 3 | export default Component -------------------------------------------------------------------------------- /src/pages/HomePage/jsx.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { Row , Col , Menu , Button } from 'antd' 3 | import Layout from "./Layout" 4 | import Nav from './Nav'; 5 | import Side from './Side'; 6 | 7 | 8 | import { connect } from 'dva'; 9 | 10 | import styles from './styles.css'; 11 | 12 | 13 | /* 14 | import { Link } from 'dva/router'; 15 | import FileManager from '../../components/FileManager' 16 | 17 | import Side from '../../components/Side'; 18 | import { routerRedux } from 'dva/router'; 19 | */ 20 | 21 | 22 | 23 | 24 | 25 | const SubMenu = Menu.SubMenu; 26 | 27 | const MenuItem = Menu.Item; 28 | 29 | 30 | 31 | function HomePage(props){ 32 | return ( 33 | )} 35 | Side = {()} 36 | Render = {(props.children)} 37 | /> 38 | ); 39 | }; 40 | 41 | HomePage.propTypes = { 42 | }; 43 | 44 | function mapStateToProps({home}) { 45 | return {home}; 46 | } 47 | 48 | export default connect(mapStateToProps)(HomePage); 49 | 50 | 51 | 52 | 53 | 54 | /* 55 | 56 | function IndexPage({main,dispatch}) { 57 | 58 | const {filelist} = main 59 | 60 | 61 | setTimeout(()=>{ 62 | dispatch({ 63 | type:'main/fetch', 64 | payload:{} 65 | }); 66 | },500); 67 | 68 | 69 | 70 | const onMkdir = (path) => { 71 | 72 | console.log("ip mkdir success"); 73 | } 74 | 75 | const onChange = (event)=>{ 76 | dispatch({ 77 | type:'main/select_file', 78 | payload:event.data, 79 | }); 80 | } 81 | 82 | const onOperation = (event)=>{ 83 | if (event.type == 'reload') { 84 | dispatch({ 85 | type:'main/fetch', 86 | payload:{} 87 | }); 88 | } 89 | } 90 | 91 | return ( 92 | 93 | 94 | 95 | 96 | R2R-WEB 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | ); 113 | } 114 | 115 | 116 | IndexPage.propTypes = { 117 | }; 118 | 119 | 120 | function mapStateToProps({main}) { 121 | return {main}; 122 | } 123 | 124 | 125 | 126 | export default connect(mapStateToProps)(IndexPage); 127 | */ -------------------------------------------------------------------------------- /src/pages/HomePage/styles.css: -------------------------------------------------------------------------------- 1 | 2 | .test{ 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | .test2 { 8 | width: calc(100% - 10px); 9 | height: 100%; 10 | background: #fff; 11 | border: 1px solid #d8dfea; 12 | border-radius: 10px; 13 | } 14 | 15 | 16 | .test4 { 17 | width: 150px; 18 | position: relative; 19 | top: 50px; 20 | left: 50px; 21 | font-size: 12px; 22 | } 23 | 24 | 25 | 26 | .logo{ 27 | width: 100%; 28 | height: 100%; 29 | display: block; 30 | font-size: 24px; 31 | font-weight: bold; 32 | color: #4E97FF; 33 | text-align: center; 34 | margin-top: 15px; 35 | } 36 | 37 | 38 | .test3 { 39 | width: 100%; 40 | height: 100%; 41 | } 42 | 43 | 44 | .all { 45 | width: 100%; 46 | height: 100%; 47 | } 48 | 49 | .top { 50 | height: 62px; 51 | } 52 | 53 | .main { 54 | height: calc(100% - 62px ); 55 | } 56 | 57 | .side { 58 | height: 100%; 59 | } 60 | 61 | .content{ 62 | height: 100%; 63 | } 64 | 65 | 66 | 67 | .normal { 68 | font-family: Georgia, sans-serif; 69 | text-align: center; 70 | } 71 | 72 | .title { 73 | font-size: 2.5rem; 74 | font-weight: normal; 75 | letter-spacing: -1px; 76 | } 77 | 78 | .welcome { 79 | height: 328px; 80 | 81 | background-size: 388px 328px; 82 | } 83 | 84 | .list { 85 | font-size: 1.2em; 86 | margin-top: 1.8em; 87 | list-style: none; 88 | line-height: 1.5em; 89 | } 90 | 91 | .list code { 92 | background: #f7f7f7; 93 | } 94 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import HomePage from "./HomePage" 2 | import DiskPage from "./DiskPage" 3 | 4 | export { HomePage,DiskPage } -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { Router, Route, IndexRoute, Link } from 'dva/router'; 3 | 4 | 5 | import { HomePage,DiskPage } from './pages'; 6 | 7 | 8 | export default function({ history }) { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/services/DiskService/DiskService.js: -------------------------------------------------------------------------------- 1 | import request from '../../utils/request'; 2 | 3 | const PAGE_SIZE = 10 4 | 5 | const URL = "" 6 | 7 | export function fetch_list({ parent_id=0 }){ 8 | return request(URL+`/api/disk/fetch_list?parent_id=${parent_id}`); 9 | } 10 | 11 | export function fetch_tree(){ 12 | return request(URL+"/api/disk/fetch_tree?parent_id=0") 13 | } 14 | 15 | 16 | export function mkdir({ parent_id = 0 ,name }){ 17 | return request(URL+"/api/disk/mkdir",{ 18 | method : "POST", 19 | body : JSON.stringify({ parent_id,name }) 20 | }) 21 | } 22 | 23 | export function rename({ parent_id,source,target }){ 24 | return request(URL+"/api/disk/rename",{ 25 | method : "POST", 26 | body : JSON.stringify({ parent_id,source,target }) 27 | }); 28 | } 29 | 30 | export function remove({ id }){ 31 | return request(URL+"/api/disk/remove",{ 32 | method : "POST", 33 | body : JSON.stringify({id}) 34 | }); 35 | } 36 | 37 | export function move({ source_id,target_id }){ 38 | return request(URL+"/api/disk/move",{ 39 | method : "POST", 40 | body : JSON.stringify({ 41 | source_id : parseInt(source_id), 42 | target_id : parseInt(target_id), 43 | }) 44 | }); 45 | } 46 | 47 | export function copy({ source_id,target_id }){ 48 | return request(URL+"/api/disk/copy",{ 49 | method : "POST", 50 | body : JSON.stringify({ 51 | source_id : parseInt(source_id), 52 | target_id : parseInt(target_id), 53 | }) 54 | }); 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/services/DiskService/index.js: -------------------------------------------------------------------------------- 1 | import { fetch_list,fetch_tree,mkdir,rename,remove,move,copy } from "./DiskService" 2 | 3 | export default { fetch_list,fetch_tree,mkdir,rename,remove,move,copy } -------------------------------------------------------------------------------- /src/tests/models/example-test.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import example from '../../models/example'; 3 | 4 | describe('example', () => { 5 | 6 | describe('reducer', () => { 7 | it('it should save', () => { 8 | expect(example.reducers['example/save']({}, { payload: { a: 1 }})).toEqual({ a: 1 }); 9 | }); 10 | }) 11 | }); 12 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import fetch from 'dva/fetch'; 2 | 3 | function parseJSON(response) { 4 | return response.json(); 5 | } 6 | 7 | function checkStatus(response) { 8 | if (response.status >= 200 && response.status < 300) { 9 | return response; 10 | } 11 | 12 | const error = new Error(response.statusText); 13 | error.response = response; 14 | throw error; 15 | } 16 | 17 | /** 18 | * Requests a URL, returning a promise. 19 | * 20 | * @param {string} url The URL we want to request 21 | * @param {object} [options] The options we want to pass to "fetch" 22 | * @return {object} An object containing either "data" or "err" 23 | */ 24 | export default function request(url, options) { 25 | return fetch(url, options) 26 | .then(checkStatus) 27 | .then(parseJSON) 28 | .then((data) => ({ data })) 29 | .catch((err) => ({ err })); 30 | } 31 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('atool-build/lib/webpack'); 2 | 3 | module.exports = function(webpackConfig, env) { 4 | webpackConfig.babel.plugins.push('transform-runtime'); 5 | 6 | // Support hmr 7 | if (env === 'development') { 8 | webpackConfig.devtool = '#eval'; 9 | webpackConfig.babel.plugins.push('dva-hmr'); 10 | } else { 11 | webpackConfig.babel.plugins.push('dev-expression'); 12 | } 13 | 14 | // Don't extract common.js and common.css 15 | webpackConfig.plugins = webpackConfig.plugins.filter(function(plugin) { 16 | return !(plugin instanceof webpack.optimize.CommonsChunkPlugin); 17 | }); 18 | 19 | // Support CSS Modules 20 | // Parse all less files as css module. 21 | webpackConfig.module.loaders.forEach(function(loader, index) { 22 | if (typeof loader.test === 'function' && loader.test.toString().indexOf('\\.less$') > -1) { 23 | loader.include = /node_modules/; 24 | loader.test = /\.less$/; 25 | } 26 | if (loader.test.toString() === '/\\.module\\.less$/') { 27 | loader.exclude = /node_modules/; 28 | loader.test = /\.less$/; 29 | } 30 | if (typeof loader.test === 'function' && loader.test.toString().indexOf('\\.css$') > -1) { 31 | loader.include = /node_modules/; 32 | loader.test = /\.css$/; 33 | } 34 | if (loader.test.toString() === '/\\.module\\.css$/') { 35 | loader.exclude = /node_modules/; 36 | loader.test = /\.css$/; 37 | } 38 | }); 39 | 40 | return webpackConfig; 41 | }; 42 | --------------------------------------------------------------------------------