├── .env.development ├── .env.production ├── .eslintrc.js ├── .gitignore ├── README.md ├── babel.config.js ├── network_disk.sql ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── server ├── .gitignore ├── app.js ├── bin │ └── www ├── db │ ├── createTable │ │ ├── createUserList.js │ │ └── cteateFileList.js │ ├── fileList.js │ ├── index.js │ └── user_list.js ├── model │ ├── Result.js │ └── file.js ├── package-lock.json ├── package.json ├── public │ ├── images │ │ └── 新建文本文档.txt │ └── stylesheets │ │ └── style.css ├── routes │ ├── file.js │ ├── index.js │ ├── jwt.js │ ├── upload.js │ └── users.js ├── servers │ ├── dbOperation.js │ ├── file.js │ └── user.js ├── tool │ ├── constant.js │ ├── env.js │ ├── file.js │ ├── index.js │ ├── public.js │ ├── schedule.js │ └── send.js └── views │ ├── error.jade │ ├── index.jade │ └── layout.jade ├── src ├── App.vue ├── api │ ├── file.js │ └── user.js ├── assets │ ├── font │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ ├── images │ │ ├── 404.png │ │ ├── AVI.png │ │ ├── WORD.png │ │ ├── excel.png │ │ ├── img.png │ │ ├── mp3.png │ │ ├── no-data.png │ │ ├── ppt.png │ │ ├── qita.png │ │ └── zip.png │ ├── reset.css │ └── scss │ │ └── index.scss ├── components │ ├── DownloadList │ │ ├── DownloadList.vue │ │ └── cop.vue │ ├── Mark │ │ └── Mark.vue │ ├── NoData │ │ └── NoData.vue │ ├── Upload │ │ └── Upload.vue │ ├── funcTool │ │ └── funcTool.vue │ └── progress │ │ └── progress.vue ├── electron │ ├── icon │ │ └── logo.png │ ├── index.js │ └── lib │ │ └── ipcMain.js ├── main.js ├── pages │ ├── 404 │ │ └── 404.vue │ ├── CompleteTransfer │ │ └── CompleteTransfer.vue │ ├── Hide │ │ └── Hide.vue │ ├── Home │ │ ├── Home.vue │ │ └── components │ │ │ ├── FileButton.vue │ │ │ ├── FunctionColumn.vue │ │ │ └── fileUpload.vue │ ├── Index │ │ ├── Index.vue │ │ └── component │ │ │ ├── AppMain │ │ │ ├── AppMain.vue │ │ │ └── component │ │ │ │ └── HistoricalRecords.vue │ │ │ ├── IndexLeft │ │ │ └── IndexLeft.vue │ │ │ └── IndexTop │ │ │ └── IndexTop.vue │ ├── Login │ │ └── Login.vue │ ├── RecycleBin │ │ └── RecycleBin.vue │ ├── Redirect │ │ └── index.vue │ ├── Search │ │ └── Search.vue │ ├── Share │ │ └── Share.vue │ ├── TransferList │ │ ├── TransferList.vue │ │ └── components │ │ │ └── uploadList.vue │ └── applicationAccount │ │ └── applicationAccount.vue ├── permission.js ├── router │ ├── _import_development.js │ ├── _import_production.js │ └── index.js ├── store │ ├── getters.js │ ├── index.js │ └── modules │ │ ├── file.js │ │ ├── history.js │ │ ├── navList.js │ │ ├── upload.js │ │ └── user.js └── utils │ ├── ElementUI.js │ ├── FileTool.js │ ├── config.js │ ├── publicTool.js │ ├── request.js │ ├── tokne.js │ └── util.js ├── vue.config.js └── yarn.lock /.env.development: -------------------------------------------------------------------------------- 1 | 2 | # just a flag 3 | NODE_ENV = 'development' 4 | 5 | # base api 6 | # VUE_APP_BASE_API = '/dev-api' 7 | 8 | 9 | VUE_APP_BASE_API = 'http://192.168.1.64:3000' 10 | 11 | 12 | # vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, 13 | # to control whether the babel-plugin-dynamic-import-node plugin is enabled. 14 | # It only does one thing by converting all import() to require(). 15 | # This configuration can significantly increase the speed of hot updates, 16 | # when you have a large number of pages. 17 | # Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js 18 | 19 | VUE_CLI_BABEL_TRANSPILE_MODULES = true 20 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | NODE_ENV = 'production' 3 | 4 | # base api 5 | # VUE_APP_BASE_API = '/prod-api' 6 | 7 | VUE_APP_BASE_API = 'http://192.168.1.64:3000' 8 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | parser: 'babel-eslint', 5 | sourceType: 'module' 6 | }, 7 | env: { 8 | browser: true, 9 | node: true, 10 | es6: true, 11 | }, 12 | extends: ['plugin:vue/recommended', 'eslint:recommended'], 13 | 14 | // add your custom rules here 15 | //it is base on https://github.com/vuejs/eslint-config-vue 16 | rules: { 17 | "vue/max-attributes-per-line": [2, { 18 | "singleline": 10, 19 | "multiline": { 20 | "max": 1, 21 | "allowFirstLine": false 22 | } 23 | }], 24 | "vue/singleline-html-element-content-newline": "off", 25 | "vue/multiline-html-element-content-newline":"off", 26 | "vue/name-property-casing": ["error", "PascalCase"], 27 | "vue/no-v-html": "off", 28 | 'accessor-pairs': 2, 29 | 'arrow-spacing': [2, { 30 | 'before': true, 31 | 'after': true 32 | }], 33 | 'block-spacing': [2, 'always'], 34 | 'brace-style': [2, '1tbs', { 35 | 'allowSingleLine': true 36 | }], 37 | 'camelcase': [0, { 38 | 'properties': 'always' 39 | }], 40 | 'comma-dangle': [2, 'never'], 41 | 'comma-spacing': [2, { 42 | 'before': false, 43 | 'after': true 44 | }], 45 | 'comma-style': [2, 'last'], 46 | 'constructor-super': 2, 47 | 'curly': [2, 'multi-line'], 48 | 'dot-location': [2, 'property'], 49 | 'eol-last': 2, 50 | 'eqeqeq': ["error", "always", {"null": "ignore"}], 51 | 'generator-star-spacing': [2, { 52 | 'before': true, 53 | 'after': true 54 | }], 55 | 'handle-callback-err': [2, '^(err|error)$'], 56 | 'indent': [2, 2, { 57 | 'SwitchCase': 1 58 | }], 59 | 'jsx-quotes': [2, 'prefer-single'], 60 | 'key-spacing': [2, { 61 | 'beforeColon': false, 62 | 'afterColon': true 63 | }], 64 | 'keyword-spacing': [2, { 65 | 'before': true, 66 | 'after': true 67 | }], 68 | 'new-cap': [2, { 69 | 'newIsCap': true, 70 | 'capIsNew': false 71 | }], 72 | 'new-parens': 2, 73 | 'no-array-constructor': 2, 74 | 'no-caller': 2, 75 | 'no-console': 'off', 76 | 'no-class-assign': 2, 77 | 'no-cond-assign': 2, 78 | 'no-const-assign': 2, 79 | 'no-control-regex': 0, 80 | 'no-delete-var': 2, 81 | 'no-dupe-args': 2, 82 | 'no-dupe-class-members': 2, 83 | 'no-dupe-keys': 2, 84 | 'no-duplicate-case': 2, 85 | 'no-empty-character-class': 2, 86 | 'no-empty-pattern': 2, 87 | 'no-eval': 2, 88 | 'no-ex-assign': 2, 89 | 'no-extend-native': 2, 90 | 'no-extra-bind': 2, 91 | 'no-extra-boolean-cast': 2, 92 | 'no-extra-parens': [2, 'functions'], 93 | 'no-fallthrough': 2, 94 | 'no-floating-decimal': 2, 95 | 'no-func-assign': 2, 96 | 'no-implied-eval': 2, 97 | 'no-inner-declarations': [2, 'functions'], 98 | 'no-invalid-regexp': 2, 99 | 'no-irregular-whitespace': 2, 100 | 'no-iterator': 2, 101 | 'no-label-var': 2, 102 | 'no-labels': [2, { 103 | 'allowLoop': false, 104 | 'allowSwitch': false 105 | }], 106 | 'no-lone-blocks': 2, 107 | 'no-mixed-spaces-and-tabs': 2, 108 | 'no-multi-spaces': 2, 109 | 'no-multi-str': 2, 110 | 'no-multiple-empty-lines': [2, { 111 | 'max': 1 112 | }], 113 | 'no-native-reassign': 2, 114 | 'no-negated-in-lhs': 2, 115 | 'no-new-object': 2, 116 | 'no-new-require': 2, 117 | 'no-new-symbol': 2, 118 | 'no-new-wrappers': 2, 119 | 'no-obj-calls': 2, 120 | 'no-octal': 2, 121 | 'no-octal-escape': 2, 122 | 'no-path-concat': 2, 123 | 'no-proto': 2, 124 | 'no-redeclare': 2, 125 | 'no-regex-spaces': 2, 126 | 'no-return-assign': [2, 'except-parens'], 127 | 'no-self-assign': 2, 128 | 'no-self-compare': 2, 129 | 'no-sequences': 2, 130 | 'no-shadow-restricted-names': 2, 131 | 'no-spaced-func': 2, 132 | 'no-sparse-arrays': 2, 133 | 'no-this-before-super': 2, 134 | 'no-throw-literal': 2, 135 | 'no-trailing-spaces': 2, 136 | 'no-undef': 2, 137 | 'no-undef-init': 2, 138 | 'no-unexpected-multiline': 2, 139 | 'no-unmodified-loop-condition': 2, 140 | 'no-unneeded-ternary': [2, { 141 | 'defaultAssignment': false 142 | }], 143 | 'no-unreachable': 2, 144 | 'no-unsafe-finally': 2, 145 | 'no-unused-vars': [2, { 146 | 'vars': 'all', 147 | 'args': 'none' 148 | }], 149 | 'no-useless-call': 2, 150 | 'no-useless-computed-key': 2, 151 | 'no-useless-constructor': 2, 152 | 'no-useless-escape': 0, 153 | 'no-whitespace-before-property': 2, 154 | 'no-with': 2, 155 | 'one-var': [2, { 156 | 'initialized': 'never' 157 | }], 158 | 'operator-linebreak': [2, 'after', { 159 | 'overrides': { 160 | '?': 'before', 161 | ':': 'before' 162 | } 163 | }], 164 | 'padded-blocks': [2, 'never'], 165 | 'quotes': [2, 'single', { 166 | 'avoidEscape': true, 167 | 'allowTemplateLiterals': true 168 | }], 169 | 'semi': [2, 'never'], 170 | 'semi-spacing': [2, { 171 | 'before': false, 172 | 'after': true 173 | }], 174 | 'space-before-blocks': [2, 'always'], 175 | 'space-before-function-paren': [2, 'never'], 176 | 'space-in-parens': [2, 'never'], 177 | 'space-infix-ops': 2, 178 | 'space-unary-ops': [2, { 179 | 'words': true, 180 | 'nonwords': false 181 | }], 182 | 'spaced-comment': [2, 'always', { 183 | 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] 184 | }], 185 | 'template-curly-spacing': [2, 'never'], 186 | 'use-isnan': 2, 187 | 'valid-typeof': 2, 188 | 'wrap-iife': [2, 'any'], 189 | 'yield-star-spacing': [2, 'both'], 190 | 'yoda': [2, 'never'], 191 | 'prefer-const': 2, 192 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 193 | 'object-curly-spacing': [2, 'always', { 194 | objectsInObjects: false 195 | }], 196 | 'array-bracket-spacing': [2, 'never'] 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 安装项目 2 | ``` 3 | npm install 4 | ``` 5 | 6 | ### 启动web项目 7 | ``` 8 | npm run dev 9 | ``` 10 | 11 | ### 启用electron 12 | 13 | ``` 14 | npm run dev:exe 15 | ``` 16 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@vue/app', { 4 | useBuiltIns: 'entry' 5 | }], 6 | [ 7 | "@babel/preset-env", 8 | { 9 | "useBuiltIns": "entry" 10 | } 11 | ] 12 | ], 13 | "plugins": [ 14 | [ 15 | "component", 16 | { 17 | "libraryName": "element-ui", 18 | "styleLibraryName": "theme-chalk" 19 | } 20 | ] 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /network_disk.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : localhost_3306 5 | Source Server Type : MySQL 6 | Source Server Version : 80021 7 | Source Host : localhost:3306 8 | Source Schema : network_disk 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80021 12 | File Encoding : 65001 13 | 14 | Date: 27/05/2021 16:20:10 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for file_list 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `file_list`; 24 | CREATE TABLE `file_list` ( 25 | `f_id` int(0) NOT NULL AUTO_INCREMENT, 26 | `f_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, 27 | `f_size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, 28 | `f_dow_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, 29 | `f_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, 30 | `f_grouping` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, 31 | `f_transfer_state` int(0) DEFAULT 0, 32 | `f_history_state` int(0) DEFAULT 0, 33 | `u_id` int(0) DEFAULT NULL, 34 | `u_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, 35 | `version` bigint(0) DEFAULT NULL, 36 | `createdAt` datetime(0) NOT NULL, 37 | `updatedAt` datetime(0) NOT NULL, 38 | `deletedAt` datetime(0) DEFAULT NULL, 39 | PRIMARY KEY (`f_id`) USING BTREE, 40 | UNIQUE INDEX `f_id`(`f_id`) USING BTREE 41 | ) ENGINE = InnoDB AUTO_INCREMENT = 30 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 42 | 43 | -- ---------------------------- 44 | -- Records of file_list 45 | -- ---------------------------- 46 | INSERT INTO `file_list` VALUES (1, 'dkxt.rar', '106401468', 'public/upload/users/shanjianL/dkxt.rar', 'application/octet-stream', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2020-08-24 16:51:56', '2020-09-14 17:43:05', '2020-09-14 17:53:41'); 47 | INSERT INTO `file_list` VALUES (2, 'dkxt.rar', '106401468', 'public/upload/users/shanjianL/dkxt.rar', 'application/octet-stream', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2020-08-25 13:12:32', '2020-09-14 16:00:39', '2020-09-15 15:58:00'); 48 | INSERT INTO `file_list` VALUES (3, 'dkxt.rar', '106401468', 'public/upload/users/shanjianL/dkxt.rar', 'application/octet-stream', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2020-08-25 17:05:08', '2020-09-15 16:15:59', '2020-09-15 16:16:15'); 49 | INSERT INTO `file_list` VALUES (4, 'Office_2010_激活工具.zip', '19062796', 'public/upload/users/shanjianL/Office_2010_激活工具.zip', 'application/x-zip-compressed', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2020-08-31 22:25:03', '2020-09-15 16:17:28', '2020-09-15 16:17:35'); 50 | INSERT INTO `file_list` VALUES (5, 'Office_2010_激活工具.zip', '19062796', 'public/upload/users/shanjianL/Office_2010_激活工具.zip', 'application/x-zip-compressed', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2020-08-31 22:26:03', '2020-09-14 16:03:34', '2020-09-15 16:04:10'); 51 | INSERT INTO `file_list` VALUES (6, 'hodgepodge-master.zip', '131067', 'public/upload/users/shanjianL/hodgepodge-master.zip', 'application/x-zip-compressed', 'shanjianL', 0, 0, 1, 'shanjianL', 0, '2020-09-03 14:57:28', '2020-09-03 14:57:28', '2020-09-15 16:12:17'); 52 | INSERT INTO `file_list` VALUES (7, 'hodgepodge-master.zip', '131067', 'public/upload/users/shanjianL/hodgepodge-master.zip', 'application/x-zip-compressed', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2020-09-03 15:00:17', '2020-09-14 16:05:58', '2020-09-15 16:12:17'); 53 | INSERT INTO `file_list` VALUES (8, 'download.zip', '24063', 'public/upload/users/shanjianL / shanjianL/2/5/download.zip', 'application/x-zip-compressed', 'shanjianL/2/5', 0, 0, 1, 'shanjianL', 0, '2020-09-03 15:00:58', '2020-09-03 15:00:58', NULL); 54 | INSERT INTO `file_list` VALUES (9, 'dkxt.rar', '106401468', 'public/upload/users/shanjianL/2/dkxt.rar', 'application/octet-stream', 'shanjianL/2', 0, 0, 1, 'shanjianL', 0, '2020-09-03 15:03:47', '2020-09-03 15:03:47', NULL); 55 | INSERT INTO `file_list` VALUES (10, 'Interop.QuartzTypeLib.dll', '18944', 'public/upload/users/shanjianL/2/Interop.QuartzTypeLib.dll', 'application/x-msdownload', 'shanjianL/2', 0, 0, 1, 'shanjianL', 0, '2020-09-08 11:25:58', '2020-09-08 11:25:58', NULL); 56 | INSERT INTO `file_list` VALUES (11, 'content.txt', '0', 'public/upload/users/shanjianL/content.txt', 'text/plain', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2020-09-15 16:20:21', '2020-09-15 16:20:28', '2020-09-15 16:20:37'); 57 | INSERT INTO `file_list` VALUES (12, 'content.txt', '0', 'public/upload/users/shanjianL/content.txt', 'text/plain', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2020-09-15 16:21:10', '2020-09-15 16:21:23', '2020-09-15 16:21:28'); 58 | INSERT INTO `file_list` VALUES (13, 'personalBlog.sql', '45356', 'public/upload/users/shanjianL/personalBlog.sql', 'application/octet-stream', 'shanjianL', 0, 1, 1, 'shanjianL', 0, '2021-04-22 14:25:26', '2021-04-22 14:25:43', NULL); 59 | INSERT INTO `file_list` VALUES (14, 'user.png.png', '7702', 'public/upload/users/shanjianL/user.png.png', 'image/png', 'shanjianL', 0, 0, 1, 'shanjianL', 0, '2021-05-14 13:53:35', '2021-05-14 13:53:35', NULL); 60 | INSERT INTO `file_list` VALUES (15, 'user.png.png', '7702', 'public/upload/users/tourist/user.png.png', 'image/png', 'tourist', 1, 1, 9, 'tourist', 0, '2021-05-20 16:24:18', '2021-05-27 15:22:10', '2021-05-27 15:27:52'); 61 | INSERT INTO `file_list` VALUES (16, 'personalBlog.sql', '45356', 'public/upload/users/tourist/personalBlog.sql', 'application/octet-stream', 'tourist', 1, 1, 9, 'tourist', 0, '2021-05-20 16:25:58', '2021-05-27 15:29:58', '2021-05-27 15:30:03'); 62 | INSERT INTO `file_list` VALUES (17, 'vue2-elm-master.zip', '13153220', 'public/upload/users/tourist/vue2-elm-master.zip', 'application/x-zip-compressed', 'tourist', 1, 1, 9, 'tourist', 0, '2021-05-21 16:12:40', '2021-05-27 15:33:48', '2021-05-27 15:33:55'); 63 | INSERT INTO `file_list` VALUES (18, 'index.vue', '4793', 'public/upload/users/tourist/index.vue', 'application/octet-stream', 'tourist', 1, 1, 9, 'tourist', 0, '2021-05-21 17:03:33', '2021-05-27 15:32:27', '2021-05-27 15:32:35'); 64 | INSERT INTO `file_list` VALUES (19, '新建文件夹 (21).rar', '117056', 'public/upload/users/tourist/新建文件夹 (21).rar', 'application/octet-stream', 'tourist', 0, 0, 9, 'tourist', 0, '2021-05-25 13:58:05', '2021-05-27 15:42:58', '2021-05-27 15:43:02'); 65 | INSERT INTO `file_list` VALUES (20, 'vue-eleme-master.zip', '1096687', 'public/upload/users/tourist/vue-eleme-master.zip', 'application/x-zip-compressed', 'tourist', 0, 0, 9, 'tourist', 0, '2021-05-25 13:58:15', '2021-05-27 15:46:02', '2021-05-27 15:46:05'); 66 | INSERT INTO `file_list` VALUES (21, 'tianlula.github.io-main.zip', '2843', 'public/upload/users/tourist/tianlula.github.io-main.zip', 'application/x-zip-compressed', 'tourist', 1, 1, 9, 'tourist', 0, '2021-05-25 15:30:15', '2021-05-27 16:15:47', '2021-05-27 16:15:52'); 67 | INSERT INTO `file_list` VALUES (22, 'aoaoe-master.zip', '8348349', 'public/upload/users/tourist/aoaoe-master.zip', 'application/x-zip-compressed', 'tourist', 0, 0, 9, 'tourist', 0, '2021-05-25 15:32:32', '2021-05-25 15:32:32', NULL); 68 | INSERT INTO `file_list` VALUES (23, '模型评测原型_需先设计的副本.zip', '641970', 'public/upload/users/tourist/模型评测原型_需先设计的副本.zip', 'application/x-zip-compressed', 'tourist', 0, 0, 9, 'tourist', 0, '2021-05-25 15:47:58', '2021-05-27 15:48:04', '2021-05-27 15:48:07'); 69 | INSERT INTO `file_list` VALUES (24, '3q-api.pdf', '103762', 'public/upload/users/tourist/3q-api.pdf', 'application/pdf', 'tourist', 0, 1, 9, 'tourist', 0, '2021-05-25 16:27:45', '2021-05-27 16:15:06', '2021-05-27 16:15:09'); 70 | INSERT INTO `file_list` VALUES (25, 'eleme-master.zip', '755421', 'public/upload/users/tourist/eleme-master.zip', 'application/x-zip-compressed', 'tourist', 0, 0, 9, 'tourist', 0, '2021-05-25 16:30:23', '2021-05-25 16:30:23', NULL); 71 | INSERT INTO `file_list` VALUES (26, 'shop.rar', '1328016', 'public/upload/users/tourist/shop.rar', 'application/octet-stream', 'tourist', 0, 0, 9, 'tourist', 0, '2021-05-25 16:31:50', '2021-05-25 16:31:50', NULL); 72 | INSERT INTO `file_list` VALUES (27, 'l.rar', '95413', 'public/upload/users/tourist/l.rar', 'application/octet-stream', 'tourist', 0, 0, 9, 'tourist', 0, '2021-05-25 16:54:32', '2021-05-25 16:54:32', NULL); 73 | INSERT INTO `file_list` VALUES (28, 'l.rar', '95413', 'public/upload/users/tourist/l.rar', 'application/octet-stream', 'tourist', 0, 1, 9, 'tourist', 0, '2021-05-25 17:03:25', '2021-05-27 15:36:29', '2021-05-27 15:36:45'); 74 | INSERT INTO `file_list` VALUES (29, '新建文件夹 (21).rar', '117056', 'public/upload/users/tourist/新建文件夹 (21).rar', 'application/octet-stream', 'tourist', 0, 0, 9, 'tourist', 0, '2021-05-25 17:04:30', '2021-05-25 17:04:30', NULL); 75 | 76 | -- ---------------------------- 77 | -- Table structure for user_list 78 | -- ---------------------------- 79 | DROP TABLE IF EXISTS `user_list`; 80 | CREATE TABLE `user_list` ( 81 | `u_id` int(0) NOT NULL AUTO_INCREMENT, 82 | `u_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 83 | `u_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 84 | `u_capacity` int(0) NOT NULL DEFAULT 10, 85 | `u_password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '123456', 86 | `u_pic` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', 87 | `version` bigint(0) DEFAULT NULL, 88 | `createdAt` datetime(0) NOT NULL, 89 | `updatedAt` datetime(0) NOT NULL, 90 | `deletedAt` datetime(0) DEFAULT NULL, 91 | `u_used_capacity` tinyint(0) DEFAULT NULL, 92 | PRIMARY KEY (`u_id`) USING BTREE, 93 | UNIQUE INDEX `u_id`(`u_id`) USING BTREE 94 | ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 95 | 96 | -- ---------------------------- 97 | -- Records of user_list 98 | -- ---------------------------- 99 | INSERT INTO `user_list` VALUES (1, 'shanjianL', '272781702@qq.com', 10, '123456', 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', 0, '2020-08-11 17:30:49', '2020-08-11 17:30:49', NULL, 0); 100 | INSERT INTO `user_list` VALUES (9, 'tourist', '769840101@qq.com', 10, '123456', 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', 0, '2021-05-20 15:50:27', '2021-05-20 15:50:27', NULL, NULL); 101 | 102 | SET FOREIGN_KEY_CHECKS = 1; 103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "disc", 3 | "version": "0.1.0", 4 | "private": true, 5 | "main": "./src/electron/index.js", 6 | "scripts": { 7 | "dev": "vue-cli-service serve", 8 | "dev:exe": "./node_modules/.bin/electron ./", 9 | "build": "vue-cli-service build", 10 | "build:exe": "./node_modules/.bin/electron-builder build", 11 | "lint": "vue-cli-service lint" 12 | }, 13 | "dependencies": { 14 | "axios": "^0.19.2", 15 | "core-js": "^3.6.5", 16 | "element-ui": "^2.13.2", 17 | "js-cookie": "^2.2.1", 18 | "js-hodgepodge": "^1.0.2", 19 | "node-sass": "^4.14.1", 20 | "nprogress": "^0.2.0", 21 | "sass-loader": "^9.0.1", 22 | "vue": "^2.6.11", 23 | "vue-router": "^3.3.4", 24 | "vuex": "^3.5.1" 25 | }, 26 | "devDependencies": { 27 | "@babel/polyfill": "^7.11.5", 28 | "@vue/cli-plugin-babel": "~4.4.0", 29 | "@vue/cli-plugin-eslint": "~4.4.0", 30 | "@vue/cli-service": "~4.4.0", 31 | "babel-core": "^6.26.3", 32 | "babel-eslint": "^10.1.0", 33 | "babel-loader": "^7.1.5", 34 | "babel-plugin-component": "^1.1.1", 35 | "babel-preset-env": "^1.7.0", 36 | "eslint": "^6.7.2", 37 | "eslint-plugin-vue": "^6.2.2", 38 | "vue-template-compiler": "^2.6.11", 39 | "electron": "^12.0.2", 40 | "electron-builder": "^22.8.1" 41 | }, 42 | "eslintConfig": { 43 | "root": true, 44 | "env": { 45 | "node": true 46 | }, 47 | "extends": [ 48 | "plugin:vue/essential", 49 | "eslint:recommended" 50 | ], 51 | "parserOptions": { 52 | "parser": "babel-eslint" 53 | }, 54 | "rules": {} 55 | }, 56 | "browserslist": [ 57 | "> 1%", 58 | "last 2 versions", 59 | "not ie < 11" 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 模拟百度云盘 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | /public 6 | 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | const createError = require('http-errors') 2 | const express = require('express') 3 | const path = require('path') 4 | const cookieParser = require('cookie-parser') 5 | const logger = require('morgan') 6 | const cors = require('cors') 7 | const indexRouter = require('./routes/index') 8 | const { scheduleCronstyle } = require('./tool/schedule') 9 | 10 | const app = express() 11 | 12 | // 开启全局定时任务 13 | scheduleCronstyle() 14 | 15 | app.set('views', path.join(__dirname, 'views')) 16 | app.set('view engine', 'jade') 17 | 18 | app.use(logger('dev')) 19 | app.use(express.json()) 20 | app.use(express.urlencoded({ extended: false })) 21 | app.use(cookieParser()) 22 | app.use(express.static(path.join(__dirname, 'public'))) 23 | app.use(cors()) 24 | 25 | app.use('/public', express.static(path.join(__dirname, 'public'))) 26 | app.use('/', indexRouter) 27 | 28 | app.use(function(req, res, next) { 29 | next(createError(404)) 30 | }) 31 | 32 | app.use(function(err, req, res, next) { 33 | res.locals.message = err.message 34 | res.locals.error = req.app.get('env') === 'development' ? err : {} 35 | 36 | res.status(err.status || 500) 37 | res.render('error') 38 | }) 39 | 40 | module.exports = app 41 | -------------------------------------------------------------------------------- /server/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('server:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /server/db/createTable/createUserList.js: -------------------------------------------------------------------------------- 1 | 2 | const user_list = require('../user_list') 3 | 4 | user_list.sync({ 5 | force: true 6 | }) 7 | -------------------------------------------------------------------------------- /server/db/createTable/cteateFileList.js: -------------------------------------------------------------------------------- 1 | 2 | const fileList = require('../fileList') 3 | 4 | fileList.sync({ 5 | force: true 6 | }) 7 | -------------------------------------------------------------------------------- /server/db/fileList.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | const db = require('./index') 3 | 4 | module.exports = db.defineModel('file_list', { 5 | f_id: { type: Sequelize.INTEGER, allowNull: false, primaryKey: true, unique: true, autoIncrement: true }, 6 | f_name: { type: Sequelize.STRING() }, 7 | f_size: { type: Sequelize.STRING() }, 8 | f_dow_url: { type: Sequelize.STRING() }, 9 | f_type: { type: Sequelize.STRING() }, 10 | f_grouping: { type: Sequelize.STRING() }, 11 | f_transfer_state: { type: Sequelize.INTEGER, defaultValue: 0 }, // 0 未下载, 1 已下载 12 | f_history_state: { type: Sequelize.INTEGER, defaultValue: 0 }, // 0 未删除, 1 已删除 13 | u_id: { type: Sequelize.INTEGER }, 14 | u_name: { type: Sequelize.STRING } 15 | }) 16 | -------------------------------------------------------------------------------- /server/db/index.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | 3 | const sqlConfig = { // mysql 基本配置 4 | host: 'localhost', 5 | user: 'root', 6 | password: 'root', 7 | database: 'network_disk' 8 | } 9 | 10 | console.log('init sequelize...') 11 | 12 | const sequelize = new Sequelize(sqlConfig.database, sqlConfig.user, sqlConfig.password, { // 连接mysql 13 | host: sqlConfig.host, 14 | dialect: 'mysql', 15 | pool: { 16 | max: 10, 17 | min: 0, 18 | idle: 10000 19 | }, 20 | timezone: '+08:00' 21 | }) 22 | 23 | exports.sequelize = sequelize // 抛出sequelize实例 24 | 25 | exports.defineModel = (name, attributes) => { 26 | const attrs = {} 27 | for (const key in attributes) { // 循环表字段 28 | const val = attributes[key] 29 | if (typeof val === 'object' && val['type']) { 30 | val.allowNull = val.allowNull || true 31 | attrs[key] = val 32 | } else { 33 | attrs[key] = { 34 | type: val 35 | } 36 | } 37 | } 38 | attrs.version = { 39 | type: Sequelize.BIGINT 40 | } 41 | 42 | return sequelize.define(name, attrs, { // 表默认配置 43 | tableName: name, 44 | timestamps: true, 45 | paranoid: true, 46 | charset: 'utf8mb4', 47 | collate: 'utf8mb4_general_ci', 48 | hooks: { 49 | beforeBulkCreate: (obj) => { 50 | obj.version = 0 51 | }, 52 | beforeValidate: (obj) => { 53 | obj.isNewRecord ? obj.version = 0 : obj.version = obj.version + 1 54 | } 55 | } 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /server/db/user_list.js: -------------------------------------------------------------------------------- 1 | 2 | const Sequelize = require('sequelize') 3 | const db = require('./index') // 引入刚刚写的sequelize配置 4 | 5 | module.exports = db.defineModel('user_list', { 6 | u_id: { type: Sequelize.INTEGER, allowNull: false, primaryKey: true, unique: true, autoIncrement: true }, 7 | u_name: { type: Sequelize.STRING }, 8 | u_email: { type: Sequelize.STRING }, 9 | u_capacity: { type: Sequelize.INTEGER(), defaultValue: 10 }, // 容量 10 | u_password: { type: Sequelize.STRING, defaultValue: 123456 }, 11 | u_pic: { type: Sequelize.STRING(), defaultValue: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png' } // 用户头像 12 | }) 13 | -------------------------------------------------------------------------------- /server/model/Result.js: -------------------------------------------------------------------------------- 1 | const { 2 | CODE_SUCCESS, 3 | CODE_ERROR, 4 | CODE_TOKEN_ERROR 5 | } = require('../tool/constant') 6 | 7 | class Result { 8 | constructor(data, msg = '操作成功!', options) { 9 | this.data = null 10 | if (arguments.length === 0) { 11 | this.msg = '操作成功!' 12 | } else if (arguments.length === 1) { 13 | this.msg = data 14 | } else { 15 | this.data = data 16 | this.msg = msg 17 | if (options) this.options = options 18 | } 19 | } 20 | 21 | content() { 22 | if (!this.code) { 23 | this.code = CODE_SUCCESS 24 | } 25 | 26 | const base = { 27 | code: this.code, 28 | msg: this.msg 29 | } 30 | 31 | if (this.data) { 32 | base.data = this.data 33 | } 34 | 35 | if (this.options) { 36 | base.options = this.options 37 | } 38 | 39 | return base 40 | } 41 | 42 | success(res) { 43 | this.code = CODE_SUCCESS 44 | this.send(res) 45 | } 46 | 47 | fail(res) { 48 | this.code = CODE_ERROR 49 | this.send(res) 50 | } 51 | 52 | send(res) { 53 | res.send(this.content()) 54 | } 55 | 56 | tokenError(res) { 57 | this.code = CODE_TOKEN_ERROR 58 | this.send(res) 59 | } 60 | } 61 | 62 | module.exports = Result 63 | -------------------------------------------------------------------------------- /server/model/file.js: -------------------------------------------------------------------------------- 1 | // const {} = require('../tool/constant') 2 | 3 | // class File { 4 | // constructor(file, userId) { 5 | // this.fileProcessing(file, userId) 6 | // } 7 | 8 | // fileProcessing(file) { 9 | // console.log(file) 10 | 11 | // const fileObj = { 12 | // f_name: file.originalname, 13 | // f_size: file.size, 14 | // f_path: file.path, 15 | // f_type: file.mimetype 16 | // } 17 | // } 18 | // } 19 | 20 | // module.exports = File 21 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "nodemon ./bin/www" 7 | }, 8 | "dependencies": { 9 | "boom": "^7.3.0", 10 | "cookie-parser": "~1.4.4", 11 | "cors": "^2.8.5", 12 | "crypto": "^1.0.1", 13 | "debug": "~2.6.9", 14 | "express": "~4.16.1", 15 | "express-jwt": "^5.3.3", 16 | "express-validator": "^6.5.0", 17 | "http-errors": "~1.6.3", 18 | "jade": "^1.11.0", 19 | "jsonwebtoken": "^8.5.1", 20 | "morgan": "~1.9.1", 21 | "multer": "^1.4.2", 22 | "mysql": "^2.18.1", 23 | "mysql2": "^2.1.0", 24 | "node-schedule": "^2.0.0", 25 | "nodemailer": "^6.4.10", 26 | "nodemon": "^2.0.4", 27 | "sequelize": "^6.3.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /server/routes/file.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const { body } = require('express-validator') 4 | // const jwt = require('jsonwebtoken') 5 | const { errorChecking } = require('../tool/public') 6 | const Result = require('../model/Result') 7 | // const { md5, decoded } = require('../tool/index') 8 | // const { fs } = require('fs') 9 | const { mkdirFloader, getGroupingList } = require('../tool/file') 10 | const { UPLOAD_PATH } = require('../tool/constant') 11 | const { handleDelFile } = require('../servers/file') 12 | 13 | router.post('/create_folder', 14 | [ 15 | body('currentPath').isLength({ min: 0 }).withMessage('当前路径不能为空'), 16 | body('folderName').isLength({ min: 0 }).withMessage('新增文件名不能为空') 17 | ], (req, res, next) => { 18 | errorChecking(next, req, () => { 19 | const { currentPath, folderName } = req.body 20 | const path = `${UPLOAD_PATH}/${currentPath}` 21 | const flag = mkdirFloader(folderName, path) 22 | console.log(flag) 23 | if (!flag) { return new Result('创建失败').fail(res) } 24 | new Result('创建成功').success(res) 25 | }) 26 | }) 27 | 28 | router.post('/grouping-list', 29 | [ 30 | body('groupName').isLength({ min: 0 }).withMessage('当前路径不能为空') 31 | ], (req, res) => { 32 | getGroupingList(`${UPLOAD_PATH}/${req.body.groupName}`, list => { 33 | new Result(list, '获取成功').success(res) 34 | }) 35 | }) 36 | 37 | router.post('/del-file', [ 38 | body('cur_url').isLength({ min: 0 }).withMessage('当前路径不能为空'), 39 | body('file_id').isLength({ min: 0 }).withMessage('文件ID不能为空') 40 | ], (req, res, next) => { 41 | errorChecking(next, req, () => { 42 | const { cur_url, file_id } = req.body 43 | handleDelFile(cur_url, file_id, data => { 44 | new Result('删除成功').success(res) 45 | }) 46 | }) 47 | }) 48 | 49 | module.exports = router 50 | -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const boom = require('boom') 3 | const userRouter = require('./users') 4 | const uploadRouter = require('./upload') 5 | const fileRouter = require('./file') 6 | 7 | const { jwtAuth } = require('./jwt') 8 | const Result = require('../model/Result') 9 | const router = express.Router() 10 | router.use(jwtAuth) 11 | 12 | router.use('/user', userRouter) 13 | router.use('/file', uploadRouter) 14 | router.use('/folder', fileRouter) 15 | 16 | router.use((req, res, next) => { 17 | next(boom.notFound('接口不存在')) 18 | }) 19 | 20 | router.use((err, req, res, next) => { 21 | if (err.name && err.name === 'UnauthorizedError') { // 如果token验证失败 22 | const { status = 401, message } = err 23 | // 清楚用户cooick 24 | new Result(null, 'token验证失败', { 25 | error: status, 26 | errMsg: message 27 | }).tokenError(res.status(status)) 28 | } else { 29 | const msg = (err && err.message) || '系统错误' 30 | const statusCode = (err.output && err.output.statusCode) || 500 31 | const errMsg = (err.output && err.output.playload && err.output.playload.error) || err.message 32 | new Result(null, msg, { 33 | error: statusCode, 34 | errMsg 35 | }).tokenError(res.status(statusCode)) 36 | } 37 | }) 38 | module.exports = router 39 | -------------------------------------------------------------------------------- /server/routes/jwt.js: -------------------------------------------------------------------------------- 1 | const jwt = require('express-jwt') 2 | const { PRIVATE_KEY } = require('../tool/constant') 3 | 4 | const jwtAuth = jwt({ 5 | secret: PRIVATE_KEY, 6 | credentialRequired: true 7 | }).unless({ 8 | path: [ 9 | '/', 10 | '/user/login', 11 | '/user/sendcode', 12 | '/user/register', 13 | '/user/user_check', 14 | '/file/dow_file' 15 | ] 16 | }) 17 | 18 | module.exports = { 19 | jwtAuth 20 | } 21 | -------------------------------------------------------------------------------- /server/routes/upload.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const Result = require('../model/Result') 4 | const multer = require('multer') 5 | const { UPLOAD_PATH } = require('../tool/constant') 6 | const fs = require('fs') 7 | const { uploadFile, uploadList, historyFile, historyList, handDelFile } = require('../servers/file') 8 | const { decoded } = require('../tool') 9 | const { body } = require('express-validator') 10 | const { errorChecking } = require('../tool/public') 11 | 12 | router.post('/upload', 13 | multer({ dest: `${UPLOAD_PATH}` }).single('file'), (req, res) => { 14 | const decode = decoded(req) 15 | if (decode && decode.username) { 16 | const { groupingName } = req.body 17 | const path = `${UPLOAD_PATH}/${groupingName}/${req.file.originalname}` 18 | const obj = { 19 | file: req.file, 20 | username: decode.username, 21 | fileDow: path, 22 | groupingName 23 | } 24 | fs.rename(`${req.file.path}`, path, function(err) { // 剪切刚刚上传的文件至分组 25 | console.log(err) 26 | }) 27 | uploadFile(obj, data => { 28 | if (!data) return new Result('上传失败').fail(res) 29 | new Result('上传成功').success(res) 30 | }) 31 | } 32 | }) 33 | 34 | router.post('/upload-list', 35 | [ 36 | body('uId').isLength({ min: 0 }).withMessage('用户ID不能为空') 37 | ], (req, res, next) => { 38 | errorChecking(next, req, () => { 39 | const { uId, groupingName } = req.body 40 | uploadList(uId, groupingName, data => { 41 | data.length > 0 ? new Result(data, '获取成功').success(res) : new Result([], '暂无数据').success(res) 42 | }) 43 | }) 44 | }) 45 | 46 | router.post('/del-history', [ 47 | body('file-id').isLength({ min: 0 }).withMessage('文件id不能为空') 48 | ], (req, res, next) => { 49 | errorChecking(next, req, () => { 50 | const { f_id } = req.body 51 | historyFile({ f_id }, data => { 52 | data.length > 0 ? new Result(data, '删除成功').success(res) : new Result('删除失败').fail(res) 53 | }) 54 | }) 55 | }) 56 | 57 | router.post('/dow_file', [ 58 | body('file_obj').isLength({ min: 0 }).withMessage('文件路径不能为空') 59 | ], (req, res, next) => { 60 | errorChecking(next, req, () => { 61 | try { 62 | handDelFile(req.body.file_obj.f_id, data => new Result(data, '下载成功!').success(res)) 63 | } catch (e) { 64 | console.log(e) 65 | } 66 | // const realPath = `./${req.body.dow_url}` 67 | // res.writeHead(200, { 68 | // 'Content-Type': 'application/octet-stream', 69 | // 'Content-Disposition': 'attachment; filename=' + encodeURI(`${realPath.split('/')[realPath.split('/').length - 1]}`) 70 | // }) 71 | // const readStream = fs.createReadStream(realPath) 72 | // readStream.on('data', (chunk) => { 73 | // res.write(chunk, 'binary') 74 | // }) 75 | // readStream.on('end', () => { 76 | // res.end() 77 | // }) 78 | }) 79 | }) 80 | 81 | router.get('/history', (req, res) => { 82 | const decode = decoded(req) 83 | historyList({ u_name: decode.username }, data => { 84 | data.length > 0 ? new Result(data, '获取成功').success(res) : new Result([], '暂无数据').success(res) 85 | }) 86 | }) 87 | 88 | module.exports = router 89 | -------------------------------------------------------------------------------- /server/routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const { body } = require('express-validator') 4 | const jwt = require('jsonwebtoken') 5 | const sendYzm = require('../tool/send') 6 | const { errorChecking } = require('../tool/public') 7 | const Result = require('../model/Result') 8 | const { login, usernameIsRegister, register, findUser } = require('../servers/user') 9 | const { md5, decoded } = require('../tool/index') 10 | const { PWD_SALT, JWT_EXPIRED, PRIVATE_KEY } = require('../tool/constant') 11 | const { mkdirFloader } = require('../tool/file') 12 | 13 | let YZM_CODE = '' 14 | 15 | router.get('/getUserInfo', (req, res) => { 16 | const decode = decoded(req) 17 | if (decode && decode.username) { 18 | findUser({ u_name: decode.username }, user => { 19 | if (!user) return new Result('查询失败').fail(res) 20 | new Result(user, '用户信息查询成功').success(res) 21 | }) 22 | } 23 | }) 24 | 25 | router.post('/login', [ 26 | body('password').isLength({ min: 5 }).withMessage('密码长度太低'), 27 | body('username').isLength({ min: 4 }).withMessage('用户名长度太低') 28 | ], (req, res, next) => { 29 | errorChecking(next, req, () => { 30 | // eslint-disable-next-line prefer-const 31 | let { username, password } = req.body 32 | password = password === '123456' ? password : md5(`${password}${PWD_SALT}`) 33 | login({ u_name: username, u_password: password }, user => { 34 | if (!user || user.length === 0) { return new Result('登录失败').fail(res) } 35 | const token = jwt.sign( 36 | { username }, 37 | PRIVATE_KEY, 38 | { expiresIn: JWT_EXPIRED } 39 | ) 40 | new Result({ token }, '登录成功').success(res) 41 | }) 42 | }) 43 | }) 44 | 45 | router.post('/sendcode', [ 46 | body('email').isEmail().withMessage('请输入正确邮箱') 47 | ], (req, res, next) => { 48 | errorChecking(next, req, () => { 49 | YZM_CODE = '' 50 | const { email } = req.body 51 | for (let i = 0; i < 4; i++) { 52 | YZM_CODE += Math.floor(Math.random() * 10) 53 | } 54 | try { 55 | sendYzm.send(email, YZM_CODE) 56 | } catch (e) { 57 | console.log(e) 58 | } 59 | new Result(YZM_CODE, '验证码已发您邮件,请注意查收').success(res) 60 | }) 61 | }) 62 | 63 | router.post('/register', [ 64 | body('username').isLength({ min: 6 }).withMessage('用户名长度太低'), 65 | body('email').isEmail().withMessage('邮箱不正确'), 66 | body('code').isLength({ min: 4, max: 4 }).withMessage('验证码不正确') 67 | ], (req, res, next) => { 68 | errorChecking(next, req, () => { 69 | register(req.body, YZM_CODE, data => { 70 | const flag = !mkdirFloader(req.body.username) 71 | if (!data || !flag) { 72 | return new Result('验证码不正确或邮箱已注册').fail(res) 73 | } 74 | new Result(data, '申请账号成功,24小时会有管理员回复,请注意邮箱提示').success(res) 75 | }) 76 | }) 77 | }) 78 | 79 | router.post('/user_check', [ 80 | 81 | body('username').isLength({ min: 6 }).withMessage('不能为空') 82 | ], (req, res, next) => { 83 | errorChecking(next, req, () => { 84 | const { username } = req.body 85 | usernameIsRegister({ u_name: username }, data => { 86 | data ? new Result('用户已被注册').success(res) : new Result('').success(res) 87 | }) 88 | }) 89 | }) 90 | 91 | module.exports = router 92 | -------------------------------------------------------------------------------- /server/servers/dbOperation.js: -------------------------------------------------------------------------------- 1 | // const Op = require('sequelize').Op 2 | 3 | /* 4 | * 根据条件查找新数据 5 | * @params option 配置对象 6 | * */ 7 | function findOne(example, option, cb) { 8 | example.findOne({ 9 | where: option 10 | }).then(res => { 11 | cb && cb(res) 12 | }).catch(err => { 13 | cb && cb(err) 14 | }) 15 | } 16 | 17 | function findAll(example, option, cb) { 18 | example.findAll( 19 | option 20 | ).then(res => { 21 | cb && cb(res) 22 | }) 23 | } 24 | 25 | /* 26 | * 创建一条新数据 27 | * @params option 配置对象 28 | * */ 29 | function create(example, option, cb) { 30 | example.create( 31 | option 32 | ).then(list => { 33 | cb && cb(list) 34 | }).catch(() => { 35 | cb && cb(false) 36 | }) 37 | } 38 | 39 | /* 40 | * 根据ID查一条数据 41 | * @params option 配置对象 42 | * */ 43 | function findById(example, id, cb) { 44 | example.findById(id).then(list => { 45 | cb && cb(list) 46 | }).catch(err => { 47 | cb && cb(err) 48 | }) 49 | } 50 | 51 | /* 52 | * 根据条件更新数据 53 | * @params option 配置对象 54 | * 示例: 55 | * option = { firstName: "King" }, 56 | { 57 | where: { firstName: null } 58 | } 59 | * */ 60 | function update(example, option, cb) { 61 | example.update(...option).then(list => { 62 | cb && cb(list) 63 | }).catch(err => { 64 | cb && cb(err) 65 | }) 66 | } 67 | 68 | /* 69 | * 根据条件删除一条数据 70 | * @params option 配置对象 71 | * */ 72 | function destroy(example, option, cb) { 73 | example.destroy(option).then(list => { 74 | cb && cb(list) 75 | }).catch(err => { 76 | cb && cb(err) 77 | }) 78 | } 79 | 80 | module.exports = { 81 | findOne, 82 | create, 83 | findById, 84 | update, 85 | destroy, 86 | findAll 87 | } 88 | -------------------------------------------------------------------------------- /server/servers/file.js: -------------------------------------------------------------------------------- 1 | const FileList = require('../db/fileList') 2 | const UserList = require('../db/user_list') 3 | const { findOne, create, findAll, update, destroy } = require('./dbOperation') 4 | const { delServerFile } = require('../tool/file') 5 | 6 | async function uploadFile(options, cb) { 7 | const { file, username, fileDow, groupingName } = options 8 | let u_id = null 9 | if (!file || file.length === 0) { return cb && cb(false) } 10 | await findOne(UserList, { u_name: username }, data => { 11 | u_id = data.dataValues.u_id 12 | const createObj = { 13 | f_name: file.originalname, 14 | f_size: file.size, 15 | f_dow_url: fileDow, 16 | f_grouping: groupingName || username, 17 | f_type: file.mimetype, 18 | u_id, 19 | u_name: username 20 | } 21 | create(FileList, createObj, data => { 22 | cb && cb(data) 23 | }) 24 | }) 25 | } 26 | 27 | function uploadList(uId, groupingName, cb) { 28 | if (groupingName.split('/').length === 0) { 29 | findAll(FileList, { where: { u_id: uId, f_history_state: 0 }}, data => { 30 | cb && cb(data) 31 | }) 32 | } else { 33 | findAll(FileList, { where: { u_id: uId, f_history_state: 0, f_grouping: groupingName }}, data => { 34 | cb && cb(data) 35 | }) 36 | } 37 | } 38 | 39 | function historyFile(options, cb) { 40 | const { f_id } = options 41 | const option = [ 42 | { f_history_state: 1 }, 43 | { where: { f_id }} 44 | ] 45 | update(FileList, option, data => { 46 | cb && cb(data) 47 | }) 48 | } 49 | 50 | function historyList(options, cb) { 51 | findAll(FileList, { where: { ...options, f_history_state: 1 }}, data => { 52 | cb && cb(data) 53 | }) 54 | } 55 | 56 | function handleDelFile(url, f_id, cb) { 57 | destroy(FileList, { where: { f_id }}, data => { 58 | // if(!data) { return cb && cb(false) } 59 | delServerFile(url, flag => { 60 | cb && cb('删除成功') 61 | }) 62 | }) 63 | } 64 | 65 | function handDelFile(f_id, cb) { 66 | update(FileList, [ 67 | { f_transfer_state: 1 }, 68 | { where: { f_id }} 69 | ], data => { 70 | cb && cb(data) 71 | }) 72 | } 73 | 74 | module.exports = { 75 | uploadFile, 76 | uploadList, 77 | historyFile, 78 | historyList, 79 | handleDelFile, 80 | handDelFile 81 | } 82 | -------------------------------------------------------------------------------- /server/servers/user.js: -------------------------------------------------------------------------------- 1 | const UserList = require('../db/user_list') 2 | const { create, findOne } = require('./dbOperation') 3 | 4 | function login(options, cb) { 5 | findOne(UserList, options, data => { 6 | cb && cb(data) 7 | }) 8 | } 9 | 10 | function findUser(options, cb) { 11 | findOne(UserList, options, data => { 12 | cb && cb(data) 13 | }) 14 | } 15 | 16 | function usernameIsRegister(options, cb) { 17 | findOne(UserList, options, data => { 18 | cb && cb(data) 19 | }) 20 | } 21 | 22 | function register(body, YZM_CODE, cb) { 23 | const { username, email, code } = body 24 | if (YZM_CODE !== code) { return cb?.(false) } 25 | findOne(UserList, { u_email: email }, data => { 26 | if (data) { return cb?.(false) } 27 | create(UserList, { 28 | u_name: username, 29 | u_email: email 30 | }, res => { 31 | cb?.(res) 32 | }) 33 | }) 34 | } 35 | 36 | module.exports = { 37 | login, 38 | findUser, 39 | usernameIsRegister, 40 | register 41 | } 42 | -------------------------------------------------------------------------------- /server/tool/constant.js: -------------------------------------------------------------------------------- 1 | const { env } = require('./env') 2 | 3 | const UPLOAD_PATH = env === 'dev' ? 'public/upload/users' : 'public/upload/users' 4 | 5 | module.exports = { 6 | CODE_ERROR: -1, 7 | CODE_SUCCESS: 200, 8 | PWD_SALT: 'admin_imooc_node', 9 | PRIVATE_KEY: 'admin_imooc_node_shan', 10 | JWT_EXPIRED: 60 * 60, // TOKEN 失效时间 11 | CODE_TOKEN_ERROR: -2, 12 | UPLOAD_PATH, 13 | TOURIST_PATH: `${UPLOAD_PATH}/tourist` 14 | // UPLOAD_URL 15 | } 16 | -------------------------------------------------------------------------------- /server/tool/env.js: -------------------------------------------------------------------------------- 1 | 2 | const env = 'dev' 3 | 4 | 5 | module.exports = { 6 | env 7 | } 8 | -------------------------------------------------------------------------------- /server/tool/file.js: -------------------------------------------------------------------------------- 1 | const { UPLOAD_PATH } = require('./constant') 2 | const fs = require('fs') 3 | 4 | function mkdirFloader(folderName, currentPath = UPLOAD_PATH) { 5 | const floaderList = fs.readdirSync(`${currentPath}`) 6 | if (floaderList.includes(folderName)) { return false } 7 | fs.mkdirSync(`${currentPath}/${folderName}`) 8 | return true 9 | } 10 | 11 | // function fileList(username) { 12 | // const floaderList = fs.readdirSync(`${UPLOAD_PATH} / ${username}`) 13 | // } 14 | 15 | function getGroupingList(path, cb) { 16 | let result = [] 17 | list(path, data => { 18 | cb && cb(data) 19 | }) 20 | function list(path, callBack) { 21 | const floaderList = fs.readdirSync(`${path}`) 22 | floaderList.forEach(file => { 23 | if (file.split('.').length === 1) { 24 | result = [...result, file] 25 | // list(`${path}/${file}`) 26 | } 27 | }) 28 | callBack && callBack(result) 29 | } 30 | } 31 | 32 | function delServerFile(url, cb) { 33 | const path = `./${UPLOAD_PATH}/${url}` 34 | const flag = fs.unlinkSync(path) 35 | cb && cb(flag) 36 | } 37 | 38 | module.exports = { 39 | mkdirFloader, 40 | // fileList, 41 | getGroupingList, 42 | delServerFile 43 | } 44 | -------------------------------------------------------------------------------- /server/tool/index.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | const jwt = require('jsonwebtoken') 3 | const { PRIVATE_KEY } = require('./constant') 4 | 5 | function md5(s) { 6 | return crypto.createHash('md5').update(String(s)).digest('hex') 7 | } 8 | 9 | function decoded(req) { 10 | let token = req.get('Authorization') 11 | if(token.indexOf('Bearer') === 0) { 12 | token = token.replace('Bearer ', '') 13 | } 14 | return jwt.verify(token, PRIVATE_KEY) 15 | } 16 | 17 | module.exports = { 18 | md5, 19 | decoded 20 | } 21 | -------------------------------------------------------------------------------- /server/tool/public.js: -------------------------------------------------------------------------------- 1 | const boom = require('boom') 2 | const { validationResult } = require('express-validator') 3 | 4 | function errorChecking(next, req, cb) { 5 | const err = validationResult(req) 6 | if (!err.isEmpty()) { 7 | const [{ msg }] = err.errors 8 | next(boom.badRequest(msg)) 9 | } else { 10 | cb && cb() 11 | } 12 | } 13 | 14 | module.exports = { 15 | errorChecking 16 | } 17 | -------------------------------------------------------------------------------- /server/tool/schedule.js: -------------------------------------------------------------------------------- 1 | const schedule = require('node-schedule') 2 | const { 3 | TOURIST_PATH 4 | } = require('./constant') 5 | 6 | const fs = require('fs') 7 | 8 | function scheduleCronstyle() { 9 | schedule.scheduleJob('1 1 0 * * *', () => { // 每天的凌晨0点第一分钟的第一秒清空测试账号的数据 1 1 0 * * * 10 | delAllFile() 11 | }) 12 | } 13 | 14 | function delAllFile(url = `${TOURIST_PATH}`) { 15 | const touristFileList = fs.readdirSync(url) 16 | touristFileList.forEach(c => { 17 | const path = `${url}/${c}` 18 | const start = fs.statSync(path) 19 | if (start.isDirectory()) { 20 | delAllFile(path) 21 | } else { 22 | fs.unlinkSync(path) 23 | } 24 | }) 25 | url !== `${TOURIST_PATH}` && fs.rmdirSync(url) 26 | } 27 | 28 | module.exports = { 29 | scheduleCronstyle 30 | } 31 | -------------------------------------------------------------------------------- /server/tool/send.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require('nodemailer') 2 | 3 | const obj = { 4 | transporter: nodemailer.createTransport({ 5 | host: 'smtp.qq.com', // 默认是这个 6 | port: 465, 7 | auth: { 8 | user: '272781702@qq.com', 9 | pass: 'rxnjazwslqdkbgei' // rxnjazwslqdkbgei 10 | } 11 | }), 12 | 13 | send: function(mail, content) { 14 | const mailOptions = { 15 | // 发送方的邮箱地址 16 | from: '桌面版百度云注册验证码<272781702@qq.com>', 17 | to: mail, // 对方邮箱 18 | // cc : '' //抄送 用于多人邮件 19 | // bcc : '' //密送 20 | subject: '激活验证码', 21 | text: `您的注册验证码为:${content}, 24小时内有效,请谨慎保管`, 22 | html: ` 23 | 24 | 25 | 26 | 27 | 28 | 37 | 38 | 39 | 40 | 41 | 42 | 73 | 74 | 75 |
43 |
44 | 45 | 46 |
47 |
48 |
49 |
50 | 尊敬的用户:您好! 51 | 52 | 您正在进行ShanJDisc账号申请操作,请在验证码输入框中输入:${content},以完成操作。 53 | 54 |
55 |
56 | 57 |

58 | 注意:此操作可能会修改您的密码、登录邮箱或绑定手机。如非本人操作,请及时登录并修改密码以保证帐户安全 59 |
(工作人员不会向你索取此验证码,请勿泄漏!) 60 |

61 |
62 |
63 |
64 |
65 |
66 |

此为系统邮件,请勿回复
67 | 请保管好您的邮箱,避免账号被他人盗用 68 |

69 |

ShanJDisc

70 |
71 |
72 |
76 | 77 | ` 78 | } 79 | this.transporter.sendMail(mailOptions, (error, info) => { 80 | if (error) { 81 | return console.log(error) 82 | } 83 | console.log('Message sent: %s', info.messageId) 84 | }) 85 | } 86 | } 87 | 88 | // 抛出对象以接收 89 | 90 | module.exports = obj 91 | -------------------------------------------------------------------------------- /server/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /server/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /server/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /src/api/file.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request' 2 | import store from '../store' 3 | import router from '../router' 4 | 5 | export function uploadFile(file, path) { 6 | const { name, size, type } = file 7 | const formData = new FormData() 8 | formData.append('file', file) 9 | formData.append('groupingName', path) 10 | let stime = new Date().getTime() 11 | let sloaded = 0 12 | return request({ 13 | url: '/file/upload', 14 | headers: { 'Content-Type': 'multipart/form-data' }, 15 | method: 'post', 16 | data: formData, 17 | onUploadProgress: progressEvent => { 18 | router.currentRoute.fullPath !== '/transfer/2' && router.push('/transfer/2') 19 | const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total) 20 | const endTime = new Date().getTime() 21 | const dTime = (endTime - stime) / 1000 22 | const dloaded = progressEvent.loaded - sloaded 23 | let speed = dloaded / dTime 24 | stime = new Date().getTime() 25 | sloaded = progressEvent.loaded 26 | let unit = 'b/s' 27 | if (speed / 1024 > 1) { 28 | unit = 'kb/s' 29 | speed = speed / 1024 30 | } 31 | if (speed / 1024 > 1) { 32 | unit = 'mb/s' 33 | speed = speed / 1024 34 | } 35 | store.dispatch('onUploadProgress', 36 | { 37 | progressBar: percentCompleted, 38 | sloaded: dloaded, 39 | dloadedremainingTime: dTime, 40 | uploadSpeed: speed.toFixed(2) + unit, 41 | fileName: name, 42 | fileSize: size, 43 | fileType: type 44 | } 45 | ) 46 | } 47 | }) 48 | } 49 | 50 | export function uploadList(uId, groupingName) { 51 | return request({ 52 | url: '/file/upload-list', 53 | method: 'post', 54 | data: { 55 | uId, 56 | groupingName 57 | } 58 | }) 59 | } 60 | 61 | export function uploadHistory(f_id) { 62 | return request({ 63 | url: '/file/del-history', 64 | method: 'post', 65 | data: { 66 | f_id 67 | } 68 | }) 69 | } 70 | 71 | export function getHistory() { 72 | return request({ 73 | url: '/file/history', 74 | method: 'get' 75 | }) 76 | } 77 | 78 | export function createFloader(currentPath, folderName) { 79 | return request({ 80 | url: '/folder/create_folder', 81 | method: 'post', 82 | data: { 83 | currentPath, 84 | folderName 85 | } 86 | }) 87 | } 88 | 89 | export function groupingList(groupName) { 90 | return request({ 91 | url: '/folder/grouping-list', 92 | method: 'post', 93 | data: { 94 | groupName 95 | } 96 | }) 97 | } 98 | 99 | export function delFile(cur_url, file_id) { 100 | return request({ 101 | url: '/folder/del-file', 102 | method: 'post', 103 | data: { 104 | cur_url, 105 | file_id 106 | } 107 | }) 108 | } 109 | 110 | export function dowFile(file) { 111 | console.log('file', file) 112 | // return request({ 113 | // url: '/file/dow_file', 114 | // method: 'post', 115 | // data: file 116 | // }) 117 | } 118 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request' 2 | 3 | export function login(options) { 4 | return request({ 5 | url: '/user/login', 6 | method: 'post', 7 | data: options 8 | }) 9 | } 10 | 11 | export function getUserInfo() { 12 | return request({ 13 | url: '/user/getUserInfo', 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function applicationAccountNumber(formData) { 19 | const { username, email, yzm } = formData 20 | return request({ 21 | url: '/user/register', 22 | method: 'post', 23 | data: { 24 | username, 25 | email, 26 | code: yzm 27 | } 28 | }) 29 | } 30 | 31 | export function sendEmailCode(email) { 32 | return request({ 33 | url: '/user/sendcode', 34 | method: 'post', 35 | data: { 36 | email 37 | } 38 | }) 39 | } 40 | 41 | export function userCheck(username) { 42 | return request({ 43 | url: '/user/user_check', 44 | method: 'post', 45 | data: { 46 | username 47 | }, 48 | onUploadProgress: progressEvent => { 49 | console.log((progressEvent.loaded / progressEvent.total * 100 | 0) + '%') 50 | } 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /src/assets/font/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1594733024019'); /* IE9 */ 3 | src: url('iconfont.eot?t=1594733024019#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAZIAAsAAAAADBwAAAX6AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCENgqJQIdKATYCJAMsCxgABCAFhG0HgRobRAoRVaSxkv0ojBvuYilK9EceITQC7WL8FEss7vdD8DzpfX9uJtrJJgvwo7Kc6ksdpM6oIHXw++k96wXVTLmk04X0x0vPKo/gH5gDtKjTdoyvrn3Bs+/4An4VR4aQXFspHH+/0o2mXbAce7Yu1X1dgBVAB/TfRYRVCBXoidot4tl0FwpPzMnLIYAmGx+IRmk5JeBiwvQCbUa1t9aDm3Jg1vAE12yXHGoqZC5YuMY08gaw3P6+fKKCCAEDSwG7UruW1Caox2HuNBX+D+lFsRDi+YDJUaCA8gAmyIHSwC5QvlAeCr3Cm8wCSEYOhY31N+VyxVwp15BL5RhugNvOnf4fARiOzSQk80YgCoVzdf7hWdgIGgPEBmqI1qYFqaKGt1giRMFbBwQFb90QHHjrgeDC22QEE96mIljwlkGw4e0AgsDb7Yr9GI9BNwwAPH1IMkAhIH0AOQcztbI+B4NTlc72HUzMgM8X0rCn3KcFUrniiSkp31ojmlWJ5QHfOEpeVCYZFq9eD3a4wVW2bhNhVUkFEvBMTTiIQXbaRpcySlSi7oJLcQlSYtDSK56+EAgXYX+GmhD14oaOZHxnwS1NmFGAfKZPlja1MMMMrVmPgrUbrQT6Tbh7ee8YgysRglxiWxnxsKSZlg/tgplhOzFIWYI6m8UdqCyXcFQP7DNo+kaiG6FSz1gE7J7hDZrDIdOTwXB70d3sZfea7hmOm7EbjHil2lSVQHbSxTbVWn4Xo5GeEFVQElIpBn+qwp2+7UAay8q7TbB82ZFecaex4tw9Z14mNl269uih2Y65H8ajaA4PrT7iiluqdBvRIo+oTcfx4itZhtuZaOW1Tup1ezZo1u/deLh4w/VK0r03swXLbg1sRu7+6rwZrmvOprHr1u9HGnRXPGOdWq/TrTVbrLXbrdrsuK8WMouv41zrRrjWoqEmASOPLhalBeqOt5Ij1el36t7JGGeLq1OSRBDNrPYcFo+ZGaHqbII9qrHU8GHLbe+yFyYVdo+yb3p2mr9t11rw1tywjs/2MsC0QGlEIPk8p2DGfTWZSPVeUGZr+D9y07yXdLndJZ6OdlAN65MZEf+dEtoIx42L+WX7HRFJ4dTxNvIfnInsH9PbF/EVhxtW5tWsr1PfYoTv2FTQoD2lPUf8LsnMDI3f+vplnWmupd1l0xDQXkmStacdd+kXvp/8zhMXRSc5sk3gH/to/Vy9W1gxksR1h8BQ7KNAESQ4JmhO3LKq4JHv5LP+/vKWbXzEi/hx9Iec03lsHqT8Y9nkcenpAnLxdKJpcr9sa0BCU8GoQEUDtuYmwqkmE/WToXIYbv+glY2QaT88TTZbanzS8xXXpMvv0d4AKsTbtudQz7M8zAXZCT6isNh3TZfZjys0je8rNANxQrgpIrrdcu43p86Zl8V3usTc+qDd4iPsP82zijhMkPB631yL3IWREfvrx7VGGq1ByfxbfiLMrQMgclQ1p2IBUC55ZJlbrBZQSVbEqjVUSF2Y36WGUrngMvPLTFupvI1R3vFNVsn6H62NifkmxL+kRY+Cbz6LZP0UeC7c9z4o8B/pR2uByH9TN1UzBcbIJDlvwuq0oqfIowKCLxiARgP/jEg89/FNSa0N/B1NSTEYOGSBwqWQbNLLg4VHJbBxqQuacqQf7ZHAACjEFAGUZRIBIZZtYBBwGhSxXCGb9HtgkcxbsIkVCiQH/MrlKQ+5E3dwHwiFaqjW4BQPWrjlwLX9JDN2GJJqy/+lEF0aijTPlp9poICxR1yYUkSDDtzDE7kadR2DD9yQktSK+FmW6bInShX3kwPuCRCIQNQAFWsAh4IZ0Hp64SD3858QhpEOJNQMuZL8hQhE3NqBgpS8BfnZMLQa8lg6RxYYSoTmNICetDI9wBP1ER0vMIAv36iBUAgptkfOMyOjU+m2hnT/pv7xXgVo2P14YogSUyyxxRFXokSLJ9Fs1v99PYgWB6PsqD/nqObl0DHWievN1t/b58PG4//Jgp0iHWg+0qDW+2uMI1fIydmShsah1JmOrZU7mjvBgvpk03uZTAAA') format('woff2'), 5 | url('iconfont.woff?t=1594733024019') format('woff'), 6 | url('iconfont.ttf?t=1594733024019') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1594733024019#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 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-AVI:before { 19 | content: "\e672"; 20 | } 21 | 22 | .icon-shangchuan:before { 23 | content: "\e6bf"; 24 | margin-right: 5px; 25 | width: 30px; 26 | text-align: center; 27 | font-size: 24px; 28 | vertical-align: middle; 29 | } 30 | 31 | .icon-download-img:before { 32 | content: "\e620"; 33 | margin-right: 5px; 34 | width: 24px; 35 | text-align: center; 36 | font-size: 18px; 37 | vertical-align: middle; 38 | } 39 | 40 | .icon-WORD:before { 41 | content: "\e643"; 42 | } 43 | 44 | .icon-EX:before { 45 | content: "\e645"; 46 | } 47 | 48 | .icon-voicefrequency:before { 49 | content: "\e655"; 50 | } 51 | 52 | .icon-yasuobao-:before { 53 | content: "\e626"; 54 | } 55 | 56 | .icon-wenjianjiaguanbi:before { 57 | content: "\e628"; 58 | } 59 | 60 | .icon-qitawenjian-:before { 61 | content: "\e60b"; 62 | } 63 | 64 | .icon-ppt:before { 65 | content: "\e6b0"; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /src/assets/font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/font/iconfont.eot -------------------------------------------------------------------------------- /src/assets/font/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/assets/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/font/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/font/iconfont.woff -------------------------------------------------------------------------------- /src/assets/font/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/font/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/404.png -------------------------------------------------------------------------------- /src/assets/images/AVI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/AVI.png -------------------------------------------------------------------------------- /src/assets/images/WORD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/WORD.png -------------------------------------------------------------------------------- /src/assets/images/excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/excel.png -------------------------------------------------------------------------------- /src/assets/images/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/img.png -------------------------------------------------------------------------------- /src/assets/images/mp3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/mp3.png -------------------------------------------------------------------------------- /src/assets/images/no-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/no-data.png -------------------------------------------------------------------------------- /src/assets/images/ppt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/ppt.png -------------------------------------------------------------------------------- /src/assets/images/qita.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/qita.png -------------------------------------------------------------------------------- /src/assets/images/zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/assets/images/zip.png -------------------------------------------------------------------------------- /src/assets/reset.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/) 3 | * http://cssreset.com 4 | */ 5 | html, 6 | body, 7 | div, 8 | span, 9 | applet, 10 | object, 11 | iframe, 12 | p, 13 | blockquote, 14 | pre, 15 | a, 16 | abbr, 17 | acronym, 18 | address, 19 | big, 20 | cite, 21 | code, 22 | del, 23 | dfn, 24 | em, 25 | img, 26 | ins, 27 | kbd, 28 | q, 29 | s, 30 | samp, 31 | small, 32 | strike, 33 | strong, 34 | sub, 35 | sup, 36 | tt, 37 | var, 38 | b, 39 | u, 40 | i, 41 | center, 42 | dl, 43 | dt, 44 | dd, 45 | ol, 46 | ul, 47 | li, 48 | fieldset, 49 | form, 50 | label, 51 | legend, 52 | table, 53 | caption, 54 | tbody, 55 | tfoot, 56 | thead, 57 | tr, 58 | th, 59 | td, 60 | article, 61 | aside, 62 | canvas, 63 | details, 64 | embed, 65 | figure, 66 | figcaption, 67 | footer, 68 | header, 69 | menu, 70 | nav, 71 | output, 72 | ruby, 73 | section, 74 | summary, 75 | time, 76 | mark, 77 | audio, 78 | video, 79 | input { 80 | margin: 0; 81 | padding: 0; 82 | border: 0; 83 | font-size: 100%; 84 | font-weight: normal; 85 | vertical-align: baseline; 86 | box-sizing: border-box; 87 | } 88 | 89 | /* HTML5 display-role reset for older browsers */ 90 | article, 91 | aside, 92 | details, 93 | figcaption, 94 | figure, 95 | footer, 96 | header, 97 | menu, 98 | nav, 99 | section { 100 | display: block; 101 | } 102 | 103 | body { 104 | line-height: 1; 105 | } 106 | 107 | body, 108 | html { 109 | height: 100%; 110 | } 111 | 112 | blockquote, 113 | q { 114 | quotes: none; 115 | } 116 | 117 | blockquote:before, 118 | blockquote:after, 119 | q:before, 120 | q:after { 121 | content: none; 122 | } 123 | 124 | table { 125 | border-collapse: collapse; 126 | border-spacing: 0; 127 | } 128 | 129 | /* custom */ 130 | a { 131 | color: #999; 132 | text-decoration: none; 133 | -webkit-backface-visibility: hidden; 134 | } 135 | 136 | li { 137 | list-style: none; 138 | } 139 | 140 | ::-webkit-scrollbar { 141 | width: 5px; 142 | height: 5px; 143 | } 144 | 145 | ::-webkit-scrollbar-track-piece { 146 | background-color: rgba(0, 0, 0, 0.2); 147 | -webkit-border-radius: 6px; 148 | } 149 | 150 | ::-webkit-scrollbar-thumb:vertical { 151 | height: 5px; 152 | background-color: rgba(125, 125, 125, 0.7); 153 | -webkit-border-radius: 6px; 154 | } 155 | 156 | ::-webkit-scrollbar-thumb:horizontal { 157 | width: 5px; 158 | background-color: rgba(125, 125, 125, 0.7); 159 | -webkit-border-radius: 6px; 160 | } 161 | 162 | /*html, body {*/ 163 | /*width: 100%;*/ 164 | /*height: 100%;*/ 165 | /*}*/ 166 | 167 | body { 168 | -webkit-text-size-adjust: none; 169 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 170 | } 171 | 172 | /*显示省略号*/ 173 | .ellipsis { 174 | overflow: hidden; 175 | text-overflow: ellipsis; 176 | white-space: nowrap; 177 | } 178 | 179 | .ellipsis-one { 180 | display: -webkit-box; 181 | -webkit-box-orient: vertical; 182 | -webkit-line-clamp: 3; 183 | overflow: hidden; 184 | } 185 | 186 | /*常用的弹性布局*/ 187 | .dispaly-flex { 188 | display: flex; 189 | justify-content: space-between; 190 | align-items: center; 191 | } 192 | 193 | .dispaly-center { 194 | display: flex; 195 | align-items: center; 196 | } 197 | 198 | .dispaly-space { 199 | display: flex; 200 | justify-content: space-between; 201 | } 202 | 203 | .dispaly-content-center { 204 | display: flex; 205 | justify-content: center; 206 | } 207 | 208 | .d-c-c { 209 | display: flex; 210 | justify-content: center; 211 | align-items: center; 212 | } 213 | 214 | .dispaly { 215 | display: flex; 216 | } 217 | 218 | .warp { 219 | flex-wrap: wrap; 220 | } 221 | 222 | .text-center { 223 | text-align: center; 224 | } 225 | 226 | .f1 { 227 | flex: 1; 228 | } 229 | 230 | .p1 { 231 | padding: 10px; 232 | } 233 | 234 | .p2 { 235 | padding: 20px; 236 | } 237 | 238 | .p3 { 239 | padding: 30px; 240 | } 241 | 242 | .p4 { 243 | padding: 40px; 244 | } 245 | 246 | .p5 { 247 | padding: 50px; 248 | } 249 | 250 | .pl-1 { 251 | padding-left: 10px; 252 | } 253 | 254 | .pl-2 { 255 | padding-left: 20px; 256 | } 257 | 258 | .pl-3 { 259 | padding-left: 30px; 260 | } 261 | .pr-1 { 262 | padding-right: 10px; 263 | } 264 | 265 | .pr-2 { 266 | padding-right: 20px; 267 | } 268 | 269 | .pr-3 { 270 | padding-right: 30px; 271 | } 272 | 273 | .pt-1 { 274 | padding-top: 10px; 275 | } 276 | 277 | .pt-2 { 278 | padding-top: 20px; 279 | } 280 | 281 | .pt-3 { 282 | padding-top: 30px; 283 | } 284 | .pb-1 { 285 | padding-bottom: 10px; 286 | } 287 | 288 | .pb-2 { 289 | padding-bottom: 20px; 290 | } 291 | 292 | .pb-3 { 293 | padding-bottom: 30px; 294 | } 295 | 296 | .pb-5 { 297 | padding-bottom: 50px; 298 | } 299 | 300 | .ml-1 { 301 | margin-left: 10px; 302 | } 303 | 304 | .ml-2 { 305 | margin-left: 20px; 306 | } 307 | 308 | .ml-3 { 309 | margin-left: 30px; 310 | } 311 | 312 | .mr-1 { 313 | margin-right: 10px; 314 | } 315 | .mr-2 { 316 | margin-right: 20px; 317 | } 318 | 319 | .mr-3 { 320 | margin-right: 30px; 321 | } 322 | 323 | .mt-1 { 324 | margin-top: 10px; 325 | } 326 | .mt-2 { 327 | margin-top: 20px; 328 | } 329 | 330 | .mt-3 { 331 | margin-top: 30px; 332 | } 333 | 334 | .mb-1 { 335 | margin-bottom: 10px; 336 | } 337 | 338 | .mb-2 { 339 | margin-bottom: 20px; 340 | } 341 | 342 | .mb-3 { 343 | margin-bottom: 30px; 344 | } 345 | 346 | 347 | -------------------------------------------------------------------------------- /src/assets/scss/index.scss: -------------------------------------------------------------------------------- 1 | @mixin border-bottom ($color) { 2 | border-bottom: 1px solid $color; 3 | } 4 | 5 | @mixin dispaly-flex() { 6 | display: flex; 7 | justify-content: space-between; 8 | align-items: center; 9 | } 10 | 11 | @mixin dispaly-center() { 12 | display: flex; 13 | align-items: center; 14 | } 15 | 16 | @mixin dispaly-space() { 17 | display: flex; 18 | justify-content: space-between; 19 | } 20 | 21 | @mixin dispaly-content-center() { 22 | display: flex; 23 | justify-content: center; 24 | } 25 | 26 | @mixin d-c-c() { 27 | display: flex; 28 | justify-content: center; 29 | align-items: center; 30 | } 31 | 32 | @mixin ellipsis() { 33 | overflow: hidden; 34 | text-overflow: ellipsis; 35 | white-space: nowrap; 36 | } 37 | /* 38 | $size : 显示几行 39 | */ 40 | @mixin ellipsis-one($size) { 41 | display: -webkit-box; 42 | -webkit-box-orient: vertical; 43 | -webkit-line-clamp: $size; 44 | overflow: hidden; 45 | } 46 | 47 | @mixin text-center($size, $color) { 48 | font-size: $size; 49 | color: $color; 50 | text-align: center; 51 | } 52 | -------------------------------------------------------------------------------- /src/components/DownloadList/DownloadList.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 161 | 162 | 210 | 211 | 259 | -------------------------------------------------------------------------------- /src/components/DownloadList/cop.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 100 | 101 | 149 | -------------------------------------------------------------------------------- /src/components/Mark/Mark.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 47 | -------------------------------------------------------------------------------- /src/components/NoData/NoData.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /src/components/Upload/Upload.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 100 | 101 | 119 | -------------------------------------------------------------------------------- /src/components/funcTool/funcTool.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | 25 | 34 | -------------------------------------------------------------------------------- /src/components/progress/progress.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /src/electron/icon/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LIAOJIANS/ShanJ-disc/ae5efe55447100b67fabd5b2e7e8f50925e77684/src/electron/icon/logo.png -------------------------------------------------------------------------------- /src/electron/index.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow } = require('electron') 2 | 3 | // 获取单例锁 4 | const gotTheLock = app.requestSingleInstanceLock() 5 | 6 | if (!gotTheLock) { 7 | app.quit() 8 | } else { 9 | const { mainWindowIpcStart } = require('./lib/ipcMain') 10 | const path = require('path') 11 | global.appDirname = __dirname 12 | 13 | app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors') 14 | 15 | const winURL = path.resolve(__dirname, '../renderer/index.html') 16 | 17 | let mainWindow 18 | // eslint-disable-next-line no-inner-declarations 19 | function createWindow() { 20 | const win = new BrowserWindow({ 21 | width: 1580, 22 | height: 888, 23 | // resizable: false, 24 | // useContentSize: true, 25 | /* transparent: true, */ 26 | icon: path.resolve(__dirname, './icon/logo.png'), 27 | frame: false, 28 | show: false, 29 | webPreferences: { 30 | webSecurity: false, 31 | nodeIntegration: true, 32 | contextIsolation: false, 33 | webviewTag: true 34 | } 35 | }) 36 | /* win.maximize(); */ 37 | // console.log(app.isPackaged) 38 | if (app.isPackaged) { 39 | win.loadURL(`file://${winURL}`) 40 | } else { 41 | win.loadURL('http://localhost:8080') 42 | win.webContents.openDevTools() 43 | } 44 | 45 | win.on('closed', () => { mainWindow = null }) 46 | win.on('ready-to-show', () => { win.show() }) 47 | return win 48 | } 49 | 50 | app.on('ready', function() { 51 | // eslint-disable-next-line new-cap 52 | mainWindow = new createWindow() 53 | mainWindowIpcStart(mainWindow) 54 | }) 55 | 56 | app.on('second-instance', (event, commandLine, workingDirectory) => { 57 | // 当运行第二个实例时,将会聚焦到myWindow这个窗口 58 | if (mainWindow) { 59 | if (mainWindow.isMinimized()) mainWindow.restore() 60 | mainWindow.focus() 61 | } 62 | }) 63 | 64 | app.on('quit', () => { 65 | app.releaseSingleInstanceLock()// 释放所有的单例锁 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /src/electron/lib/ipcMain.js: -------------------------------------------------------------------------------- 1 | const { app, ipcMain, session, Notification, shell, dialog } = require('electron') 2 | const path = require('path') 3 | const fs = require('fs') 4 | const axios = require('axios') 5 | 6 | // 缓存下载项 7 | const cacheDownItem = {} 8 | 9 | const mainWindowIpcStart = function(win) { 10 | // 打开调试 11 | ipcMain.on('toggle_dev_tools', function(event, arg) { 12 | win.webContents.toggleDevTools() 13 | }) 14 | 15 | // 重启 16 | ipcMain.on('restart', function() { 17 | app.relaunch() 18 | app.exit(0) 19 | }) 20 | 21 | // 最小化 22 | ipcMain.on('min', function() { 23 | win.minimize() 24 | }) 25 | 26 | // 最大化 27 | ipcMain.on('max', function() { 28 | if (win.isMaximized()) { 29 | win.unmaximize() 30 | } else { 31 | win.maximize() 32 | } 33 | }) 34 | 35 | // 关闭程序 36 | ipcMain.on('close', function() { 37 | cacheDownItemClose() 38 | win.close() 39 | }) 40 | 41 | // 设置下载路径 42 | ipcMain.on('set_path', (e, data = {}) => { 43 | const { path } = data 44 | if (path) { 45 | if (path !== 'not') app.setPath('downloads', path) 46 | e.reply('set_path', app.getPath('downloads')) 47 | } else { 48 | dialog.showOpenDialog({ 49 | title: '选择下载目录', 50 | defaultPath: app.getPath('downloads'), 51 | properties: ['openDirectory'] 52 | }).then((files) => { 53 | if (!files.canceled) { // 如果有选中 54 | app.setPath('downloads', files.filePaths[0]) 55 | } 56 | e.reply('set_path', files) 57 | }) 58 | } 59 | }) 60 | 61 | // 在应用中打开文件 62 | ipcMain.on('check_path', (e, data = {}) => { 63 | const { path } = data 64 | fs.access(path, fs.constants.F_OK, (err) => { // 利用fs 读取文件路径 65 | if (!err) { 66 | shell.showItemInFolder(path) // 打开文件 67 | } 68 | e.reply('check_path' + path, err) 69 | }) 70 | }) 71 | 72 | // 下载 73 | ipcMain.on('down-file', function(e, data) { 74 | const { f_dow_url } = data 75 | if (!cacheDownItem[f_dow_url]) { 76 | cacheDownItem[f_dow_url] = { ...data } 77 | downfile(f_dow_url) 78 | } else { 79 | e.sender('down-file', '文件正在下载') 80 | } 81 | }) 82 | 83 | // 暂停 84 | ipcMain.on('down-file-pause', function(e, data) { 85 | const { url } = data 86 | const t = cacheDownItem[url] 87 | if (t) { 88 | t._downFileItem.pause() 89 | } 90 | e.reply('down-file-pause-' + url, '已暂停') 91 | }) 92 | 93 | // 继续 94 | ipcMain.on('down-file-resume', function(e, data) { 95 | const { url } = data 96 | const t = cacheDownItem[url] 97 | if (t) { 98 | t._downFileItem.resume() 99 | } 100 | e.reply('down-file-resume-' + url, '已恢复下载') 101 | }) 102 | 103 | // 取消下载 104 | ipcMain.on('down-file-cancel', function(e, data) { 105 | const { url } = data 106 | const t = cacheDownItem[url] 107 | if (t) { 108 | t._downFileItem.cancel() 109 | } else { 110 | // 删除未下在完成文件 111 | } 112 | e.reply('down-file-cancel-' + url, '已取消下载') 113 | }) 114 | 115 | // 断点恢复下载 116 | ipcMain.on('resume-download', function(e, data) { 117 | const { url } = data 118 | const t = cacheDownItem[url] 119 | if (t) { 120 | t._downFileItem.resume() 121 | } else { 122 | cacheDownItem[url] = { ...data } 123 | resumeDownload(data) 124 | } 125 | e.reply('down-file-resume-' + url, '已恢复下载') 126 | }) 127 | 128 | // 下载文件 129 | const downfile = (url) => { 130 | session.defaultSession.downloadURL(url) 131 | } 132 | 133 | // 恢复下载 134 | const resumeDownload = (obj = {}) => { 135 | const { path = '', urlChain = [], offset = 0, length = 0, lastModified, eTag, startTime } = obj 136 | if (!path || urlChain.length === 0 || length === 0) { 137 | return 138 | } 139 | session.defaultSession.createInterruptedDownload({ 140 | path, urlChain, offset, length, lastModified, eTag, startTime 141 | }) 142 | } 143 | 144 | // 监听下载 145 | session.defaultSession.on('will-download', (e, item) => { 146 | try { 147 | const url = item.getURL() 148 | let cacheItem = cacheDownItem[url] || { 149 | notSend: true 150 | } 151 | // 获取文件的总大小 152 | const totalBytes = item.getTotalBytes() 153 | // 设置下载路径 154 | const filePath = path.join(app.getPath('downloads'), item.getFilename()) 155 | item.setSavePath(filePath) 156 | 157 | // eslint-disable-next-line no-irregular-whitespace 158 | // 缓存downitem 159 | cacheItem._downFileItem = item 160 | cacheItem.path = item.getSavePath() 161 | cacheItem.eTag = item.getETag() 162 | cacheItem.urlChain = item.getURLChain() 163 | cacheItem.length = totalBytes 164 | cacheItem.lastModified = item.getLastModifiedTime() 165 | cacheItem.startTime = item.getStartTime() 166 | 167 | let lastBytes = 0 168 | 169 | // 监听下载过程,计算并设置进度条进度 170 | item.on('updated', (event, state) => { 171 | if (state === 'interrupted') { 172 | cacheItem.state = 'interrupted' 173 | } else if (state === 'progressing') { 174 | if (item.isPaused()) { 175 | cacheItem.state = 'paused' 176 | } else { 177 | const offset = item.getReceivedBytes() 178 | cacheItem.state = 'downing' 179 | cacheItem.speedBytes = offset - lastBytes 180 | cacheItem.progress = parseInt((offset / totalBytes) * 100) 181 | cacheItem.offset = offset 182 | lastBytes = offset 183 | } 184 | } 185 | !cacheItem.notSend && win.webContents.send('update-down-state', JSON.parse(JSON.stringify(cacheItem))) 186 | }) 187 | 188 | // 下载完成 189 | item.once('done', (event, state) => { 190 | cacheItem.done = 'end' 191 | switch (state) { 192 | case 'interrupted': 193 | cacheItem.state = 'interrupted-err' 194 | break 195 | case 'cancelle': 196 | cacheItem.state = 'cancelle' 197 | break 198 | default: 199 | cacheItem.state = 'completed' 200 | notification(cacheItem.path) 201 | break 202 | } 203 | 204 | !cacheItem.notSend && win.webContents.send('update-down-state', JSON.parse(JSON.stringify(cacheItem))) 205 | 206 | // 请求后端改变文件状态 207 | axios.post('http://192.168.1.64:3000/file/dow_file', 208 | { file_obj: cacheDownItem[url] }, 209 | { 210 | headers: { 211 | 'Content-Type': 'application/json;charset=UTF-8' 212 | } 213 | }) 214 | 215 | // 删除缓存 216 | delete cacheDownItem[url] 217 | cacheItem = null 218 | item = null 219 | }) 220 | 221 | // 恢复 222 | if (item.canResume) { 223 | item.resume() 224 | } 225 | } catch (error) { 226 | console.log(error) 227 | } 228 | }) 229 | 230 | // 暂停所有下载任务 231 | const cacheDownItemClose = () => { 232 | for (const key in cacheDownItem) { 233 | // eslint-disable-next-line no-prototype-builtins 234 | if (cacheDownItem.hasOwnProperty(key)) { 235 | const element = cacheDownItem[key] 236 | if (element._downFileItem) { 237 | element._downFileItem.pause() 238 | element._downFileItem = null 239 | } 240 | } 241 | } 242 | } 243 | 244 | app.on('gpu-process-crashed', function() { 245 | cacheDownItemClose() 246 | }) 247 | 248 | app.on('renderer-process-crashed', function() { 249 | cacheDownItemClose() 250 | }) 251 | 252 | let noti 253 | const notification = (url) => { 254 | noti = new Notification({ 255 | title: '下载成功', 256 | bodyString: url, 257 | silentBoolean: false, 258 | icon: url 259 | }) 260 | noti.show() 261 | noti.once('click', () => { 262 | shell.showItemInFolder(url) 263 | }) 264 | } 265 | } 266 | 267 | module.exports = { 268 | mainWindowIpcStart 269 | } 270 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import './permission' 5 | import '@/assets/reset.css' 6 | import '@/assets/font/iconfont.css' 7 | import Element from '@/utils/ElementUI' 8 | import store from '@/store' 9 | 10 | import { getTime, byte, getImgCollection, updImgCollection, getDownFiles, getDownDoneFiles, updDownFiles, updDownDoneFiles } from '@/utils/util' 11 | import { downFile, updateDownState, initPath } from '@/utils/FileTool' 12 | 13 | import 'core-js/stable' 14 | import 'regenerator-runtime/runtime' 15 | 16 | import 'element-ui/lib/theme-chalk/base.css' 17 | import 'element-ui/lib/theme-chalk/index.css' 18 | Vue.use(Element) 19 | 20 | Vue.filter('byte', byte) 21 | 22 | Vue.config.productionTip = false 23 | 24 | initPath() 25 | 26 | new Vue({ 27 | data() { 28 | return { 29 | // 收藏 30 | collections: getImgCollection(), 31 | // 下载列表 32 | downFiles: getDownFiles(), 33 | downDoneFiles: getDownDoneFiles() 34 | } 35 | }, 36 | watch: { 37 | collections: { 38 | deep: true, 39 | handler(val) { 40 | updImgCollection(val) 41 | } 42 | }, 43 | downFiles: { 44 | deep: true, 45 | handler(val) { 46 | updDownFiles(val) 47 | } 48 | }, 49 | downDoneFiles: { 50 | deep: true, 51 | handler(val) { 52 | updDownDoneFiles(val) 53 | } 54 | } 55 | }, 56 | created() { 57 | updateDownState(this.updateDownState) 58 | }, 59 | methods: { 60 | // 添加收藏 61 | AddCollection(obj) { 62 | if (obj) this.collections.splice(0, 0, obj) 63 | }, 64 | // 移除收藏 65 | removeCollection(obj) { 66 | const index = this.collections.findIndex(item => item.id === obj.id) 67 | if (index > -1) { 68 | this.collections.splice(index, 1) 69 | } 70 | }, 71 | // 下载文件 72 | addDownFile(obj) { 73 | const index = this.downFiles.findIndex(item => item.f_id === obj.f_id) 74 | if (index === -1) { 75 | obj.progress = 0 76 | obj.speedBytes = 0 77 | obj.state = 'wait' 78 | obj.done = 'downing' 79 | this.downFiles.splice(0, 0, obj) 80 | console.log(obj) 81 | downFile(obj) 82 | } 83 | }, 84 | // 更新状态 85 | updateDownState(data) { 86 | this.$nextTick(() => { 87 | const { f_id, done, progress } = data 88 | const index = this.downFiles.findIndex(item => item.f_id === f_id) 89 | if (done === 'end') { 90 | if (progress === 100) { 91 | const { f_id, path } = data 92 | this.downDoneFiles.splice(0, 0, { f_id, path, downloadtime: getTime() }) 93 | if (index > -1) this.downFiles.splice(index, 1) 94 | } 95 | } else { 96 | if (index > -1) this.$set(this.downFiles, index, data) 97 | } 98 | }) 99 | }, 100 | // 删除下载列表 101 | removeDownFile(id, downing) { 102 | if (downing) { 103 | const index = this.downFiles.findIndex(item => item.f_id === id) 104 | if (index > -1) { 105 | this.downFiles.splice(index, 1) 106 | } 107 | } else { 108 | const index = this.downDoneFiles.findIndex(item => item.f_id === id) 109 | if (index > -1) { 110 | this.downDoneFiles.splice(index, 1) 111 | } 112 | } 113 | } 114 | }, 115 | router, 116 | store, 117 | render: h => h(App) 118 | }).$mount('#app') 119 | -------------------------------------------------------------------------------- /src/pages/404/404.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 29 | -------------------------------------------------------------------------------- /src/pages/CompleteTransfer/CompleteTransfer.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /src/pages/Hide/Hide.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | 20 | -------------------------------------------------------------------------------- /src/pages/Home/Home.vue: -------------------------------------------------------------------------------- 1 | 123 | 124 | 346 | 347 | 456 | -------------------------------------------------------------------------------- /src/pages/Home/components/FileButton.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 46 | 47 | 69 | -------------------------------------------------------------------------------- /src/pages/Home/components/FunctionColumn.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 96 | 97 | 152 | -------------------------------------------------------------------------------- /src/pages/Home/components/fileUpload.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 49 | 50 | 52 | -------------------------------------------------------------------------------- /src/pages/Index/Index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | 23 | 32 | -------------------------------------------------------------------------------- /src/pages/Index/component/AppMain/AppMain.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 32 | 33 | 44 | -------------------------------------------------------------------------------- /src/pages/Index/component/AppMain/component/HistoricalRecords.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 135 | 136 | 208 | -------------------------------------------------------------------------------- /src/pages/Index/component/IndexLeft/IndexLeft.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 144 | 145 | 160 | -------------------------------------------------------------------------------- /src/pages/Index/component/IndexTop/IndexTop.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 104 | 105 | 150 | -------------------------------------------------------------------------------- /src/pages/Login/Login.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 118 | 119 | 195 | -------------------------------------------------------------------------------- /src/pages/RecycleBin/RecycleBin.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 43 | 44 | 47 | -------------------------------------------------------------------------------- /src/pages/Redirect/index.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /src/pages/Search/Search.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/pages/Share/Share.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /src/pages/TransferList/TransferList.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 60 | 61 | 86 | -------------------------------------------------------------------------------- /src/pages/TransferList/components/uploadList.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 56 | 97 | -------------------------------------------------------------------------------- /src/pages/applicationAccount/applicationAccount.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 188 | 189 | 198 | -------------------------------------------------------------------------------- /src/permission.js: -------------------------------------------------------------------------------- 1 | import NProgress from 'nprogress' 2 | import 'nprogress/nprogress.css' 3 | import router from './router' 4 | import { getToken } from './utils/tokne' 5 | import store from './store' 6 | 7 | const whiteList = ['/login', '/app-account'] 8 | router.beforeEach(async(to, form, next) => { 9 | const token = getToken() 10 | NProgress.start() 11 | if (token) { 12 | if (to.path === '/login') { return next({ path: '/' }) } 13 | if (JSON.stringify(store.getters.userInfo) !== '{}') { return next() } 14 | try { 15 | await store.dispatch('getInfo') 16 | store.dispatch('setCurrentPath', { 17 | path: store.getters.userInfo.u_name, 18 | u_id: store.getters.userInfo.u_id 19 | }) 20 | next() 21 | } catch (e) { 22 | await store.dispatch('resetToken') 23 | this.$message({ 24 | type: 'error', 25 | message: 'token失效' 26 | }) 27 | next(`/login?redirect=${to.path}`) 28 | } 29 | } else { 30 | whiteList.indexOf(to.path) !== -1 ? next() : next(`/login?redirect=${to.path}`) 31 | } 32 | NProgress.done() 33 | }) 34 | 35 | router.afterEach(() => { 36 | NProgress.done() 37 | }) 38 | -------------------------------------------------------------------------------- /src/router/_import_development.js: -------------------------------------------------------------------------------- 1 | module.exports = file => require('@/pages/' + file + '.vue').default 2 | -------------------------------------------------------------------------------- /src/router/_import_production.js: -------------------------------------------------------------------------------- 1 | module.exports = file => () => import('@/pages/' + file + '.vue') 2 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Router from 'vue-router' 2 | import Vue from 'vue' 3 | const _import = require('./_import_' + process.env.NODE_ENV) 4 | 5 | Vue.use(Router) 6 | 7 | import Index from '@/pages/Index/Index' 8 | 9 | export default new Router({ 10 | routes: [ 11 | { 12 | path: '/redirect', 13 | component: Index, 14 | children: [ 15 | { 16 | path: '/redirect/:path(.*)', 17 | component: _import('Redirect/index') 18 | } 19 | ] 20 | }, 21 | 22 | { 23 | path: '/', 24 | component: Index, 25 | children: [ 26 | { 27 | path: '/', 28 | component: _import('Home/Home'), 29 | name: 'Index', 30 | meta: { 31 | title: '我的网盘' 32 | } 33 | } 34 | ] 35 | }, 36 | 37 | { 38 | path: '/transfer', 39 | component: Index, 40 | children: [ 41 | { 42 | path: '/transfer/:key', 43 | component: _import('TransferList/TransferList'), 44 | name: 'TransferList', 45 | meta: { 46 | title: '传输列表' 47 | } 48 | } 49 | ] 50 | }, 51 | 52 | { 53 | path: '/complete-transfer', 54 | component: Index, 55 | children: [ 56 | { 57 | path: '/complete-transfer', 58 | component: _import('CompleteTransfer/CompleteTransfer'), 59 | name: 'CompleteTransfer', 60 | meta: { 61 | title: '完成传输' 62 | } 63 | } 64 | ] 65 | }, 66 | 67 | { 68 | path: '/search', 69 | component: Index, 70 | children: [ 71 | { 72 | path: '/search', 73 | component: _import('Search/Search'), 74 | name: 'Search', 75 | meta: { 76 | title: '找资源' 77 | } 78 | } 79 | ] 80 | }, 81 | 82 | { 83 | path: '/hide', 84 | component: Index, 85 | children: [ 86 | { 87 | path: '/hide', 88 | component: _import('Hide/Hide'), 89 | name: 'Hide', 90 | meta: { 91 | title: '隐藏空间' 92 | } 93 | } 94 | ] 95 | }, 96 | 97 | { 98 | path: '/share', 99 | component: Index, 100 | children: [ 101 | { 102 | path: '/share', 103 | component: _import('Share/Share'), 104 | name: 'Share', 105 | meta: { 106 | title: '分享空间' 107 | } 108 | } 109 | ] 110 | }, 111 | 112 | { 113 | path: '/recycle', 114 | component: Index, 115 | children: [ 116 | { 117 | path: '/recycle', 118 | component: _import('RecycleBin/RecycleBin'), 119 | name: 'RecycleBin', 120 | meta: { 121 | title: '回收站' 122 | } 123 | } 124 | ] 125 | }, 126 | 127 | { 128 | path: '/login', 129 | name: 'login', 130 | component: _import('Login/Login') 131 | }, 132 | 133 | { 134 | path: '/app-account', 135 | name: 'applicationAccount', 136 | component: _import('applicationAccount/applicationAccount') 137 | }, 138 | 139 | { 140 | path: '*', 141 | component: _import('404/404') 142 | } 143 | ] 144 | }) 145 | 146 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | token: state => state.user.token, 3 | userInfo: state => state.user.userInfo, 4 | historyList: state => state.history.historyList, 5 | cachedViews: state => state.history.cachedViews, 6 | uploadInfo: state => state.upload.uploadList, 7 | fileList: state => state.file.fileList, 8 | fileCurrentPath: state => state.file.fileCurrentPath 9 | } 10 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | import getters from './getters' 7 | 8 | const modulesFiles = require.context('./modules', true, /\.js$/) 9 | 10 | const modules = modulesFiles.keys().reduce((modules, modulePath) => { 11 | const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') 12 | const value = modulesFiles(modulePath) 13 | modules[moduleName] = value.default 14 | return modules 15 | }, {}) 16 | 17 | export default new Vuex.Store({ 18 | modules, 19 | getters 20 | }) 21 | -------------------------------------------------------------------------------- /src/store/modules/file.js: -------------------------------------------------------------------------------- 1 | import { uploadList, groupingList, getHistory } from '@/api/file' 2 | const state = { 3 | fileList: [], 4 | fileHistory: [], 5 | fileCurrentPath: '' 6 | } 7 | 8 | const actions = { 9 | getFileList({ commit, state }, u_id) { 10 | return new Promise(resolve => { 11 | uploadList(u_id, state.fileCurrentPath).then(res => { 12 | commit('GET_FILE_LIST', res.data) 13 | resolve(res.data) 14 | }).catch(() => { 15 | commit('GET_FILE_LIST', []) 16 | }) 17 | }) 18 | }, 19 | 20 | getHistory({ commit }) { 21 | return new Promise(resolve => { 22 | getHistory().then(res => { 23 | commit('GET_HISTORY', res.data) 24 | resolve(res.data) 25 | }) 26 | }) 27 | }, 28 | 29 | setCurrentPath({ commit, dispatch }, options) { 30 | const { path, u_id } = options 31 | commit('SET_CURRENT_PATH', path) 32 | dispatch('getFileList', u_id) 33 | // dispatch('setBackPath', options) 34 | }, 35 | 36 | setBackPath({ commit, dispatch }, options) { 37 | return new Promise(resolve => { 38 | const { path, u_id } = options 39 | commit('SET_BACK_PATH', path) 40 | dispatch('getGroupList').then(list => { 41 | resolve(list) 42 | }) 43 | dispatch('getFileList', u_id) 44 | }) 45 | }, 46 | 47 | getGroupList({ state }) { 48 | return new Promise(resolve => { 49 | groupingList(state.fileCurrentPath).then(res => { 50 | resolve(res.data) 51 | }) 52 | }) 53 | }, 54 | 55 | filterFile({ commit }, list) { 56 | commit('FILTER_FILE_LIST', list) 57 | } 58 | } 59 | 60 | const mutations = { 61 | GET_FILE_LIST: (state, list) => { 62 | state.fileList = list 63 | }, 64 | 65 | GET_HISTORY: (state, list) => { 66 | state.fileHistory = list 67 | }, 68 | 69 | SET_BACK_PATH: (state, path) => { 70 | state.fileCurrentPath = path 71 | }, 72 | 73 | FILTER_FILE_LIST: (state, list) => { 74 | state.fileList = list 75 | }, 76 | 77 | SET_CURRENT_PATH: (state, path) => { 78 | state.fileCurrentPath = !state.fileCurrentPath ? path : `${state.fileCurrentPath}/${path}` 79 | } 80 | } 81 | 82 | export default { 83 | state, 84 | actions, 85 | mutations 86 | } 87 | -------------------------------------------------------------------------------- /src/store/modules/history.js: -------------------------------------------------------------------------------- 1 | 2 | const state = { 3 | historyList: [], 4 | cachedViews: [] 5 | } 6 | 7 | const actions = { 8 | addRouter({ dispatch }, router) { 9 | dispatch('setHistory', router) 10 | dispatch('addCachedView', router) 11 | }, 12 | 13 | delRouter({ dispatch, state }, router) { 14 | return new Promise((resolve) => { 15 | dispatch('delCachedView', router) 16 | dispatch('delHistory', router) 17 | resolve({ 18 | historyList: [...state.historyList], 19 | cachedViews: [...state.cachedViews] 20 | }) 21 | }) 22 | }, 23 | 24 | setHistory({ commit }, router) { 25 | commit('SET_HISTORY', router) 26 | }, 27 | 28 | addCachedView({ commit }, router) { 29 | commit('ADD_CACHED_VIEW', router) 30 | }, 31 | 32 | delCachedView({ commit, state }, router) { 33 | return new Promise((resolve) => { 34 | commit('DEL_CACHED_VIEW', router) 35 | resolve([...state.cachedViews]) 36 | }) 37 | }, 38 | 39 | delHistory({ commit, state }, router) { 40 | return new Promise((resolve) => { 41 | commit('DEL_HISTORY', router) 42 | resolve([...state.historyList]) 43 | }) 44 | }, 45 | 46 | delOtherHistory({ dispatch }, router) { 47 | dispatch('delOtherHistoryList', router) 48 | dispatch('delOtherCachedViews', router) 49 | }, 50 | 51 | delOtherHistoryList({ commit }, router) { 52 | commit('DEL_OTHER_HISTORY_LIST', router) 53 | }, 54 | 55 | delOtherCachedViews({ commit }, router) { 56 | commit('DEL_OTHER_CACHED_VIEW', router) 57 | }, 58 | 59 | delAllHistory({ commit, state }) { 60 | return new Promise(resolve => { 61 | commit('DEL_ALL_HISTORY') 62 | resolve({ 63 | historyList: [...state.historyList] 64 | }) 65 | }) 66 | } 67 | 68 | } 69 | 70 | const mutations = { 71 | SET_HISTORY: (state, router) => { 72 | !state.historyList.some(r => r.path === router.path) && (state.historyList = [...state.historyList, router]) 73 | }, 74 | 75 | DEL_HISTORY: (state, router) => { 76 | state.historyList.forEach((v, i) => { 77 | console.log(1) 78 | if (v.path === router.path) { 79 | return state.historyList.splice(i, 1) 80 | } 81 | }) 82 | }, 83 | 84 | ADD_CACHED_VIEW: (state, router) => { 85 | !state.cachedViews.includes(router.name) && (state.cachedViews = [...state.cachedViews, router.name]) 86 | }, 87 | 88 | DEL_CACHED_VIEW: (state, router) => { 89 | const index = state.cachedViews.indexOf(router.name) 90 | index > -1 && state.cachedViews.splice(index, 1) 91 | }, 92 | 93 | DEL_OTHER_HISTORY_LIST: (state, router) => { 94 | state.historyList = state.historyList.filter(r => (r.path === router.path)) 95 | }, 96 | 97 | DEL_OTHER_CACHED_VIEW: (state, router) => { 98 | const index = state.cachedViews.indexOf(router.name) 99 | state.cachedViews = index > -1 ? state.cachedViews.slice(index, index + 1) : [] 100 | }, 101 | 102 | DEL_ALL_HISTORY: state => { 103 | state.cachedViews = [] 104 | state.historyList = [] 105 | } 106 | 107 | } 108 | 109 | export default { 110 | state, 111 | actions, 112 | mutations 113 | } 114 | -------------------------------------------------------------------------------- /src/store/modules/navList.js: -------------------------------------------------------------------------------- 1 | 2 | const state = { 3 | navType: null 4 | } 5 | 6 | const actions = { 7 | switchMode({ commit }, type) { 8 | commit('SWITCH_MODE', type) 9 | } 10 | } 11 | 12 | const mutations = { 13 | SWITCH_MODE: (state, type) => { 14 | state.navType = type 15 | } 16 | } 17 | 18 | export default { 19 | state, 20 | actions, 21 | mutations 22 | } 23 | -------------------------------------------------------------------------------- /src/store/modules/upload.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | initFile: { 3 | progressBar: 0, 4 | sloaded: 0, 5 | dloadedremainingTime: null, 6 | uploadSpeed: null, 7 | fileName: '', 8 | fileSize: 0, 9 | fileType: '' 10 | }, 11 | uploadList: [] 12 | } 13 | 14 | const mutations = { 15 | ON_UPLOAD_PROGRESS: (state, options) => { 16 | state.uploadList.map((c, i) => { 17 | c.fileName === options.fileName ? ( 18 | state.uploadList[i] = { ...c, ...options } 19 | ) : ( 20 | state.uploadList = [ 21 | ...state.uploadList, 22 | options 23 | ] 24 | ) 25 | }) 26 | }, 27 | 28 | RESET_PROGRESS: (state) => { 29 | state.uploadList = [] 30 | } 31 | } 32 | 33 | const actions = { 34 | onUploadProgress({ commit }, options) { 35 | commit('ON_UPLOAD_PROGRESS', options) 36 | }, 37 | 38 | resetProgress({ commit }) { 39 | commit('RESET_PROGRESS') 40 | } 41 | } 42 | 43 | export default { 44 | state, 45 | mutations, 46 | actions 47 | } 48 | -------------------------------------------------------------------------------- /src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | import { login, getUserInfo } from '../../api/user' 2 | import { setToken, removeToken, getToken } from '../../utils/tokne' 3 | 4 | const state = { 5 | token: getToken(), 6 | userInfo: {} 7 | } 8 | 9 | const actions = { 10 | login({ commit }, options) { 11 | return new Promise((resolve, reject) => { 12 | login(options).then(res => { 13 | const { data } = res 14 | commit('SET_TOKEN', data.token) 15 | setToken(data.token) 16 | resolve() 17 | }).catch(error => { 18 | reject(error) 19 | }) 20 | }) 21 | }, 22 | 23 | getInfo({ commit }) { 24 | return new Promise(resolve => { 25 | getUserInfo().then(res => { 26 | commit('SET_USER_INFO', res.data) 27 | resolve(res) 28 | }) 29 | }) 30 | }, 31 | 32 | resetToken({ commit }) { 33 | return new Promise(resolve => { 34 | commit('SET_TOKEN', '') 35 | commit('SET_USER_INFO', {}) 36 | removeToken() 37 | resolve() 38 | }) 39 | } 40 | } 41 | 42 | const mutations = { 43 | SET_TOKEN: (state, token) => { 44 | state.token = token 45 | }, 46 | 47 | SET_USER_INFO(state, userInfo) { 48 | state.userInfo = userInfo 49 | } 50 | } 51 | 52 | export default { 53 | state, 54 | actions, 55 | mutations 56 | } 57 | -------------------------------------------------------------------------------- /src/utils/ElementUI.js: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | Message, 4 | Button, 5 | Input, 6 | Form, 7 | FormItem, 8 | Step, 9 | Steps, 10 | Menu, 11 | MenuItem, 12 | MenuItemGroup, 13 | Submenu, 14 | Avatar, 15 | Checkbox, 16 | CheckboxGroup, 17 | Col, 18 | Row, 19 | Progress, 20 | Upload, 21 | Table, 22 | CheckboxButton, 23 | TableColumn, 24 | Radio, 25 | Link 26 | } from 'element-ui' 27 | 28 | const element = { 29 | install(Vue) { 30 | Vue.use(Button) 31 | Vue.use(Input) 32 | Vue.use(Form) 33 | Vue.use(FormItem) 34 | Vue.use(Step) 35 | Vue.use(Steps) 36 | Vue.use(Menu) 37 | Vue.use(MenuItem) 38 | Vue.use(Submenu) 39 | Vue.use(Avatar) 40 | Vue.use(MenuItemGroup) 41 | Vue.use(Checkbox) 42 | Vue.use(CheckboxGroup) 43 | Vue.use(Col) 44 | Vue.use(Row) 45 | Vue.use(Progress) 46 | Vue.use(Table) 47 | Vue.use(CheckboxButton) 48 | Vue.use(TableColumn) 49 | Vue.use(Upload) 50 | Vue.use(Radio) 51 | Vue.use(Link) 52 | Vue.prototype.$message = Message 53 | } 54 | } 55 | 56 | export default element 57 | -------------------------------------------------------------------------------- /src/utils/FileTool.js: -------------------------------------------------------------------------------- 1 | import store from '../store' 2 | import config from './config' 3 | 4 | // eslint-disable-next-line no-undef 5 | const { ipcRenderer } = __non_webpack_require__('electron') 6 | 7 | export function fileTraversal(list, cb) { 8 | list.forEach((item, index) => { 9 | cb && cb(item, index) 10 | }) 11 | } 12 | 13 | export function dowUrl(url) { 14 | const path = 'http://localhost:3000/' 15 | return `${path}${url}` 16 | } 17 | 18 | export async function fileTypeFilter(type) { 19 | await store.dispatch('getFileList', store.getters.userInfo.u_id).then(data => { 20 | let filterFileList = [] 21 | if (type === 'qita') { 22 | const wilteList = ['rar', 'ptf', 'zip'] 23 | const typeList = Object.keys(config.FILE_TYPE) 24 | data.forEach(f => { 25 | const fileType = f.f_name.split('.')[1] 26 | ;(wilteList.includes(fileType) || !typeList.includes(fileType)) && ( 27 | filterFileList = [ 28 | ...filterFileList, 29 | f 30 | ] 31 | ) 32 | }) 33 | } else { 34 | filterFileList = data.filter(file => type.includes(file.f_name.split('.')[1])) 35 | } 36 | store.dispatch('filterFile', filterFileList) 37 | }) 38 | } 39 | 40 | export function fomartPath(pathArr) { 41 | let path = '' 42 | if (pathArr.length > 1) { 43 | pathArr.forEach(item => { 44 | path += `${item}/` 45 | }) 46 | path = path.substring(0, path.length - 1) 47 | } else { 48 | path = pathArr[0] 49 | } 50 | return path 51 | } 52 | 53 | /** 54 | * 下载文件 55 | * @param {*} obj 56 | */ 57 | export const downFile = (obj) => { 58 | ipcRenderer.send('down-file', obj) 59 | 60 | /* return new Promise((resolve, reject) => { 61 | ipcRenderer.once(`down-file-${obj.id}`, (e, data) => resolve(data)) 62 | }) */ 63 | } 64 | 65 | /** 66 | * 更新下载状态 67 | * @param {*} cb 68 | */ 69 | export const updateDownState = (cb) => { 70 | ipcRenderer.on('update-down-state', function(e, data) { 71 | cb(data) 72 | }) 73 | } 74 | 75 | /** 76 | * 暂停下载 77 | * @param {*} url 78 | */ 79 | export const pause = (url) => { 80 | ipcRenderer.send('down-file-pause', { url }) 81 | } 82 | 83 | /** 84 | * 取消下载 85 | * @param {*} url 86 | */ 87 | export const cancel = (url) => { 88 | return new Promise((resolve, reject) => { 89 | ipcRenderer.send('down-file-cancel', { url }) 90 | ipcRenderer.once(`down-file-cancel-${url}`, (e, data) => resolve(data)) 91 | }) 92 | } 93 | 94 | /** 95 | * 恢复下载 96 | * @param {*} url 97 | */ 98 | export const resume = (url) => { 99 | return new Promise((resolve, reject) => { 100 | ipcRenderer.send('down-file-resume', { url }) 101 | ipcRenderer.once(`down-file-resume-${url}`, (e, data) => resolve(data)) 102 | }) 103 | } 104 | 105 | /** 106 | * 断点续下载 107 | * @param {*} obj 108 | */ 109 | export const nextresume = (obj) => { 110 | return new Promise((resolve, reject) => { 111 | ipcRenderer.send('resume-download', obj) 112 | ipcRenderer.once(`resume-download-${obj.id}`, (e, data) => resolve(data)) 113 | }) 114 | } 115 | 116 | /** 117 | * 初始化下载地址 118 | * @param {*} path 下载路径 119 | */ 120 | export const initPath = () => { 121 | const path = localStorage.getItem('downloads') || 'not' 122 | ipcRenderer.send('set_path', { path }) 123 | ipcRenderer.once(`set_path`, (e, data) => { 124 | localStorage.setItem('downloads', data) 125 | }) 126 | } 127 | -------------------------------------------------------------------------------- /src/utils/config.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | FILE_TYPE: { 4 | zip: require('@/assets/images/zip.png'), 5 | rar: require('@/assets/images/zip.png'), 6 | png: require('@/assets/images/img.png'), 7 | jpg: require('@/assets/images/img.png'), 8 | jpeg: require('@/assets/images/img.png'), 9 | avi: require('@/assets/images/avi.png'), 10 | mp3: require('@/assets/images/mp3.png'), 11 | ptf: require('@/assets/images/ppt.png'), 12 | word: require('@/assets/images/word.png'), 13 | excel: require('@/assets/images/excel.png'), 14 | mp4: require('@/assets/images/mp3.png'), 15 | qita: require('@/assets/images/qita.png') 16 | }, 17 | 18 | transfer: [ 19 | { key: '1', icon: 'el-icon-download', title: '正在下载' }, 20 | { key: '2', icon: 'el-icon-upload2', title: '正在上传' }, 21 | { key: '3', icon: 'el-icon-circle-check', title: '完成传输' } 22 | ], 23 | 24 | home: [ 25 | { key: '1', icon: 'el-icon-time', title: '最近文件' }, 26 | { key: '2', icon: 'el-icon-tickets', title: '全部文件', children: [ 27 | { key: '2-1', title: '图片' }, 28 | { key: '2-2', title: '视频' }, 29 | { key: '2-3', title: '文档' }, 30 | { key: '2-4', title: '表格' }, 31 | { key: '2-5', title: '音乐' }, 32 | { key: '2-6', title: '其他' } 33 | ] }, 34 | { key: '3', icon: 'el-icon-lock', title: '隐藏空间' }, 35 | { key: '4', icon: 'el-icon-share', title: '我的分享' }, 36 | { key: '5', icon: 'el-icon-delete', title: '回收站' } 37 | ], 38 | 39 | share: '' 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/publicTool.js: -------------------------------------------------------------------------------- 1 | import config from './config' 2 | 3 | export function goRouter(Vue, path, params = null) { 4 | const routerPath = params ? `${path}/${params}` : path; 5 | (Vue.$router.history.current.path !== path) && Vue.$router.push(routerPath) 6 | } 7 | 8 | export function fileType(fileName) { 9 | const fileType = fileName.split('.')[1] 10 | const iconKey = Object.keys(config.FILE_TYPE).find(key => key === fileType) || 'qita' 11 | return config.FILE_TYPE[iconKey] 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import store from '@/store' 3 | import { Message } from 'element-ui' 4 | import { getToken } from './tokne' 5 | 6 | const service = axios.create({ 7 | baseURL: process.env.VUE_APP_BASE_API, 8 | timeout: 1000000 9 | }) 10 | 11 | service.interceptors.request.use( 12 | config => { 13 | config.headers['Content-Type'] = 'application/json;charset=UTF-8' 14 | store.getters.token && (config.headers['Authorization'] = `Bearer ${getToken()}`) 15 | return config 16 | }, 17 | error => { 18 | Message({ 19 | message: error || '网络错误', 20 | duration: 1000, 21 | type: 'error' 22 | }) 23 | return Promise.reject(error) 24 | } 25 | ) 26 | 27 | service.interceptors.response.use( 28 | response => { 29 | const res = response.data || response 30 | const errMsg = res.msg || '请求失败!' 31 | if (res.code && res.code !== 200) { 32 | Message({ 33 | message: errMsg || '请求失败', 34 | duration: 1000, 35 | type: 'error' 36 | }) 37 | return Promise.reject('error') 38 | } else { 39 | return Promise.resolve(res) 40 | } 41 | }, 42 | error => { 43 | if (error.request.status === 401) { store.dispatch('resetToken') } 44 | Message({ 45 | message: error || '请求失败', 46 | duration: 1000, 47 | type: 'error' 48 | }) 49 | return Promise.reject(error) 50 | } 51 | ) 52 | 53 | export default service 54 | -------------------------------------------------------------------------------- /src/utils/tokne.js: -------------------------------------------------------------------------------- 1 | 2 | import Cookies from 'js-cookie' 3 | 4 | const TOKEN_KEY = 'TOKEN_KEY' 5 | 6 | export function getToken() { 7 | return Cookies.get(TOKEN_KEY) 8 | } 9 | 10 | export function removeToken() { 11 | return Cookies.remove(TOKEN_KEY) 12 | } 13 | 14 | export function setToken(token) { 15 | return Cookies.set(TOKEN_KEY, token) 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取时间 yyyy-MM-dd hh:mm:ss 3 | */ 4 | export const getTime = () => { 5 | const date = new Date() 6 | return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}` 7 | } 8 | 9 | /** 10 | * 存储单位换算 11 | * @param {*} bytes 12 | */ 13 | export const byte = (bytes) => { 14 | if (bytes === 0) return '0 B' 15 | var k = 1000 // or 1024 16 | var sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] 17 | var i = Math.floor(Math.log(bytes) / Math.log(k)) 18 | 19 | return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i] 20 | } 21 | 22 | /** 23 | * 防抖 24 | * @param {*} fn 25 | * @param {*} wait 延迟时间 默认1秒 26 | */ 27 | export const debounce = (fn, wait = 1000) => { 28 | let timeout = null 29 | return function(key, val) { 30 | if (timeout !== null) clearTimeout(timeout) 31 | timeout = setTimeout(() => { 32 | fn(key, val) 33 | }, wait) 34 | } 35 | } 36 | 37 | /** 38 | * 获取 localStorage 39 | * @param {*} key key 40 | * @param {*} val 默认值 41 | */ 42 | export const getData = (key, val = []) => { 43 | const data = localStorage.getItem(key) 44 | if (data) { 45 | return JSON.parse(data) 46 | } 47 | return val 48 | } 49 | 50 | /** 51 | * 设置 localStorage 52 | * @param {*} key key 53 | * @param {*} val value 54 | */ 55 | export const setData = (key, val) => localStorage.setItem(key, JSON.stringify(val)) 56 | 57 | /** 58 | * 获取收藏数据 59 | */ 60 | export const getImgCollection = () => getData('ImgCollection') 61 | 62 | /** 63 | * 更新收藏列表 64 | * @param {*} arr 65 | */ 66 | export const updImgCollection = (arr) => setData('ImgCollection', arr) 67 | 68 | /** 69 | * 获取下载数据 70 | */ 71 | export const getDownFiles = () => getData('DownFiles') 72 | 73 | /** 74 | * 更新下载列表 75 | * @param {*} arr 76 | */ 77 | const _updDownFiles = debounce(setData, 1000) 78 | export const updDownFiles = (arr) => { 79 | _updDownFiles('DownFiles', arr) 80 | } 81 | 82 | /** 83 | * 获取下载完成数据 84 | */ 85 | export const getDownDoneFiles = () => getData('DownDoneFiles') 86 | 87 | /** 88 | * 更新下载完成列表 89 | * @param {*} arr 90 | */ 91 | const _updDownDoneFiles = debounce(setData, 1000) 92 | export const updDownDoneFiles = (arr) => _updDownDoneFiles('DownDoneFiles', arr) 93 | 94 | /** 95 | * obj 转 url 96 | * @param {*} obj 97 | */ 98 | export const objToUrl = (obj = {}) => { 99 | let str = '' 100 | for (const key in obj) { 101 | // eslint-disable-next-line no-prototype-builtins 102 | if (obj.hasOwnProperty(key)) { 103 | if (key === 'purity') { 104 | if (obj[key].length === 2) { 105 | str += `&${key}=111` 106 | } else if (obj[key][0] === 'SFW') { 107 | str += `&${key}=100` 108 | } else { 109 | str += `&${key}=010` 110 | } 111 | } else if (obj[key] !== '') { 112 | str += `&${key}=${obj[key]}` 113 | } 114 | } 115 | } 116 | return str 117 | } 118 | 119 | /** 120 | * 图片宽高比适应 121 | * @param {*} w 容器宽 122 | * @param {*} h 容器高 123 | * @param {*} r 图片宽高比 124 | */ 125 | export const aspectRatioToWH = (w, h, r, iw, ih) => { 126 | const _r = w / h 127 | if (iw < w && ih < h) { 128 | return { w: iw, h: ih } 129 | } 130 | // 容器宽度比 大=于 内容 宽高比 以高度为基准 131 | if (_r > r) { 132 | return { 133 | w: h * r, h 134 | } 135 | } else if (_r < r) { 136 | return { 137 | w, h: w / r 138 | } 139 | } else { 140 | return { 141 | w, h 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | publicPath: './', 4 | transpileDependencies: [/node_modules[/\\\\](element-ui|vuex|)[/\\\\]/], 5 | } 6 | --------------------------------------------------------------------------------