Compare commits

...
117 Commits
Author SHA1 Message Date
seaheart 7c4a1431c4 upgrade version 2026-02-14 23:10:07 +08:00
seaheart 81712dd62d Support chunk upload and breakpoint resume feature 2026-02-08 16:45:08 +08:00
seaheart 8371133228 merge v4.1.0 2025-02-23 16:49:20 +08:00
yinyuchen d7fe343455 upgrade version 2024-10-19 14:57:53 +08:00
yinyuchen 0533dd2c56 change log 2024-07-27 23:54:38 +08:00
yinyuchen 447eb14200 feat: search file dfs 2024-07-27 23:39:32 +08:00
yinyuchen b0b5a5ca35 feat: add space bin 2024-07-27 18:05:11 +08:00
yinyuchen 19e6503da0 feat: add space recycle bin 2024-07-24 22:58:51 +08:00
yinyuchen bc7737cd0e feat: space member batch create 2024-07-20 15:25:06 +08:00
yinyuchen db31b4f204 fix: USER role file size error 2024-07-20 12:41:25 +08:00
Simba fbfc9b7c83 Merge pull request #49 from shih6/bugfix/upload-progress-freeze
fix upload Progress freeze
2024-06-30 22:12:37 +08:00
yinyuchen dd7fbe762e update dingding qrcode 2024-06-26 22:50:51 +08:00
yinyuchen 2a78050f6b fix: share overview problem 2024-04-29 22:57:05 +08:00
yinyuchen 16e79ac4cf fix: dir problrm 2024-03-26 23:15:50 +08:00
seaheart db2efc5ab7 change version 2023-11-12 15:35:09 +08:00
seaheart dd29b890a8 fix: search file bug 2023-11-12 15:33:58 +08:00
seaheart 1809bc4e09 fix some safety problem and add copy share feature 2023-11-09 23:40:44 +08:00
seaheart 59a4bfcbb3 feat: remove matter dir copy link change 2023-11-09 22:43:45 +08:00
zhenbin e92b1f374b fix upload Progress freeze 2023-10-27 11:06:34 +08:00
seaheart 7c1c379bcb add previewer change 2023-08-12 21:22:14 +08:00
seaheart 714c506b49 fix: space list bug 2023-08-12 21:12:42 +08:00
seaheart 83d3db1000 change preference scan edit 2023-08-12 21:09:08 +08:00
seaheart 648eb9429a feat: change img previewer 2023-08-12 16:36:17 +08:00
seaheart 35b0c8194b feat: add preview style change 2023-08-12 14:33:20 +08:00
seaheart 2787c95916 upgrade image previewer 2023-08-12 12:42:00 +08:00
seaheart 46957d317e add space role handle limit 2023-07-22 20:28:45 +08:00
lishuang 51d4ae089f feat: change the new Space 2023-07-22 20:17:55 +08:00
seaheart 9c79130cea change style 2023-07-22 19:51:22 +08:00
seaheart e29943f8ad space list ui change 2023-07-22 19:48:35 +08:00
seaheart cb386d484d add permission 2023-07-22 19:36:00 +08:00
seaheart bb124b171c Merge branch 'v3.2.0' of github.com:eyebluecn/tank-front into v3.2.0 2023-07-22 19:05:56 +08:00
seaheart 6f9938100d fix some matter panel logic 2023-07-22 19:05:42 +08:00
lishuang 9a637a8edd feat: add the upload user info. 2023-07-22 18:51:53 +08:00
seaheart d64231f9cb add bin matter panel 2023-07-22 18:24:30 +08:00
seaheart a4191e7d77 add crawl description 2023-07-22 16:25:04 +08:00
seaheart e77efc4613 add crawl file 2023-07-22 16:07:45 +08:00
seaheart 1e89f8f706 add paste upload file 2023-07-11 23:03:30 +08:00
seaheart 48deb4cda0 fix share matter bug 2023-07-08 21:29:17 +08:00
seaheart 85aefeaa4b Merge branch 'v3.2.0' of github.com:eyebluecn/tank-front into v3.2.0 2023-07-08 20:32:10 +08:00
seaheart 66182572a6 fix share problem 2023-07-08 20:31:54 +08:00
lishuang ce734f6809 feat: fix the tag for space. 2023-07-08 19:56:56 +08:00
seaheart 1cd930fa08 change share preview api 2023-07-08 18:55:53 +08:00
seaheart 475fc05e72 change matter share preview and download 2023-07-08 18:51:46 +08:00
seaheart 3a07e6e42b change share matter page 2023-07-08 17:26:15 +08:00
seaheart b7fdf0d0c1 remove space share feature 2023-07-08 16:33:21 +08:00
seaheart 1e1c1c1fc9 add space upload matter map 2023-07-08 16:19:29 +08:00
seaheart 1f120407d7 add user space edit change 2023-07-08 14:59:37 +08:00
seaheart cd33a867fc add input size widget change 2023-07-08 13:53:03 +08:00
seaheart 6c33075e44 feat: add move api upgrade 2023-06-29 23:11:59 +08:00
seaheart ff159a7669 upgrade modal style 2023-06-28 23:06:20 +08:00
seaheart 2b07588a2f add space empty hint 2023-06-28 22:47:43 +08:00
seaheart 173bce9474 add delete batch add soft delete batch api change 2023-06-26 23:59:34 +08:00
seaheart 29da312337 matter delete api change 2023-06-26 23:16:22 +08:00
seaheart a1a1b03e14 Merge branch 'v3.2.0' of github.com:eyebluecn/tank-front into v3.2.0 2023-06-25 23:33:51 +08:00
seaheart c927a78e95 upgrade matter detail 2023-06-25 23:33:30 +08:00
lishuang b5acd8d167 Merge remote-tracking branch 'origin/master' into v3.2.0 2023-06-25 22:36:13 +08:00
seaheart 43c3a8b936 upload update 2023-06-25 22:26:28 +08:00
seaheart 498f101352 fix create matter directory 2023-06-25 22:01:33 +08:00
seaheart 792bb5ddaf add space file list 2023-06-16 23:03:07 +08:00
seaheart 73122e8c9b change dingding qrcode 2023-06-11 15:46:51 +08:00
seaheart 57237c254a feat: add space member 2023-06-01 23:24:29 +08:00
seaheart b553c82083 add space del logic 2023-05-27 14:33:18 +08:00
seaheart 641ebcbd33 add space crud 2023-05-27 12:33:30 +08:00
lishuang 03c6b3a572 style: endOfLine. 2023-05-13 19:21:52 +08:00
seaheart 6c019c6e03 prettier files 2023-05-13 19:13:24 +08:00
seaheart 4f25004331 add simple 2023-05-13 19:09:06 +08:00
seaheart 90b96456c1 fix: drag style 2023-05-13 15:31:34 +08:00
seaheart b39b36a8bc remove about menu in pc ua 2023-05-13 12:45:24 +08:00
lishuang 67aa157d10 style: reformat the codes. 2023-05-13 12:38:12 +08:00
simba 68ec19ebf7 change dingtalk qrcode 2022-06-30 23:26:16 +08:00
Simba 74a764b870 format util 2022-05-12 10:39:18 +08:00
simba 93b6d82770 3.1.6 2022-05-01 18:18:17 +08:00
simba 46a03a1877 remove prettier 2022-05-01 18:00:10 +08:00
simba bf595384d8 v3.1.6 2022-05-01 18:00:10 +08:00
lishuang fb8d60c09f Refine the format. 2022-04-28 18:38:32 +08:00
lishuang 5ad5cf04d6 Add support for jetbrains. 2022-04-28 18:38:32 +08:00
simba 1dd369fbb1 display capacity of exist 2022-04-10 22:27:58 +08:00
simba 16c84631fe fix mobile style 2022-04-10 20:58:53 +08:00
simba 0baa50ca30 fix some preview bug 2022-04-10 20:55:53 +08:00
simba 90603d5566 optimize the error tip of upload over limit size 2022-04-10 20:41:22 +08:00
simba 7d80a99e0e beautify the error tip of upload directory 2022-04-10 19:37:40 +08:00
lishuang a5a4062a9f Fix the demo host. 2022-04-04 21:48:56 +08:00
lishuang 711c7f89e8 Release v 3.1.5 2022-03-21 15:26:31 +08:00
lishuang 34e15b7634 Fix bug of TAG. ready to release v3.1.5 2022-03-21 15:25:31 +08:00
赋苏 c9b03ca8da Fix the bug of date-cell. 2022-03-20 18:50:02 +08:00
lishuang aeb7eff8bc Add default port and LICENSE. 2022-03-20 18:16:29 +08:00
lishuang 42377193db Fix the layout bug. 2022-03-20 16:05:40 +08:00
lishuang 4cedeaeb9b Ready to release version 3.1.4 2022-03-20 16:01:01 +08:00
simba 42e9c5ad2e fix: craco less warning 2022-03-19 20:24:40 +08:00
lishuang 98f2d3885a Fix the svg image cannot show bug. 2022-03-19 18:36:26 +08:00
lishuang f3d81e0739 Fix the svg image cannot show bug. 2022-03-19 18:35:15 +08:00
lishuang 08a0178d7b Update the webpack template. 2022-03-19 17:57:05 +08:00
lishuang 51ec423c9c Merge remote-tracking branch 'origin/master' into v3.1.4
# Conflicts:
#	README.md
2022-03-19 09:57:27 +08:00
eyeblue 267b106d1f Merge pull request #43 from eyebluecn/dependabot/npm_and_yarn/follow-redirects-1.14.9
Bump follow-redirects from 1.13.2 to 1.14.9
2022-03-19 09:55:52 +08:00
dependabot[bot] 231478738d Bump follow-redirects from 1.13.2 to 1.14.9
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.13.2 to 1.14.9.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.13.2...v1.14.9)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-19 01:45:22 +00:00
eyeblue 7eeee7e4df Merge pull request #42 from eyebluecn/dependabot/npm_and_yarn/color-string-1.9.0
Bump color-string from 1.5.4 to 1.9.0
2022-03-19 09:45:15 +08:00
eyeblue 1ff7de0878 Merge pull request #41 from eyebluecn/dependabot/npm_and_yarn/url-parse-1.5.10
Bump url-parse from 1.4.7 to 1.5.10
2022-03-19 09:44:54 +08:00
eyeblue c2ee104581 Merge pull request #37 from eyebluecn/dependabot/npm_and_yarn/path-parse-1.0.7
Bump path-parse from 1.0.6 to 1.0.7
2022-03-19 09:44:36 +08:00
eyeblue b21c05c6fa Merge pull request #36 from eyebluecn/dependabot/npm_and_yarn/merge-deep-3.0.3
Bump merge-deep from 3.0.2 to 3.0.3
2022-03-19 09:44:25 +08:00
eyeblue 510b1fbcfe Merge pull request #35 from eyebluecn/dependabot/npm_and_yarn/ws-6.2.2
Bump ws from 6.2.1 to 6.2.2
2022-03-19 09:44:01 +08:00
dependabot[bot] d3ec091d64 Bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-19 01:43:50 +00:00
eyeblue f9c05133cd Merge pull request #34 from eyebluecn/dependabot/npm_and_yarn/dns-packet-1.3.4
Bump dns-packet from 1.3.1 to 1.3.4
2022-03-19 09:43:41 +08:00
dependabot[bot] a094c2c46e Bump ws from 6.2.1 to 6.2.2
Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/commits)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-19 01:43:32 +00:00
eyeblue 9d075514a5 Merge pull request #32 from eyebluecn/dependabot/npm_and_yarn/ssri-6.0.2
Bump ssri from 6.0.1 to 6.0.2
2022-03-19 09:43:16 +08:00
dependabot[bot] 0cad8d6bb3 Bump dns-packet from 1.3.1 to 1.3.4
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-19 01:43:09 +00:00
dependabot[bot] 741493c012 Bump color-string from 1.5.4 to 1.9.0
Bumps [color-string](https://github.com/Qix-/color-string) from 1.5.4 to 1.9.0.
- [Release notes](https://github.com/Qix-/color-string/releases)
- [Changelog](https://github.com/Qix-/color-string/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Qix-/color-string/compare/1.5.4...1.9.0)

---
updated-dependencies:
- dependency-name: color-string
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-19 01:42:53 +00:00
eyeblue 61e05602c2 Merge pull request #29 from eyebluecn/dependabot/npm_and_yarn/react-dev-utils-11.0.4
Bump react-dev-utils from 10.2.1 to 11.0.4
2022-03-19 09:42:37 +08:00
eyeblue 71b677337e Merge pull request #28 from eyebluecn/dependabot/npm_and_yarn/axios-0.21.1
Bump axios from 0.19.2 to 0.21.1
2022-03-19 09:42:21 +08:00
lishuang 756904082e Change the version in readme. 2022-03-18 22:21:14 +08:00
lishuang aa5dfe53ec Finish the sqlite feature. 2022-03-18 22:18:44 +08:00
lishuang 205056591f Upgrade the gorm version. 2022-03-15 02:18:13 +08:00
赋苏 d2ae467685 Change the dingding. 2022-03-10 11:48:25 +08:00
dependabot[bot] c9f28294db Bump url-parse from 1.4.7 to 1.5.10
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.10.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.10)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-27 11:25:04 +00:00
dependabot[bot] 284fe35bad Bump merge-deep from 3.0.2 to 3.0.3
Bumps [merge-deep](https://github.com/jonschlinkert/merge-deep) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/jonschlinkert/merge-deep/releases)
- [Commits](https://github.com/jonschlinkert/merge-deep/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: merge-deep
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-08 12:59:06 +00:00
dependabot[bot] ce5a66a15b Bump ssri from 6.0.1 to 6.0.2
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-20 02:47:17 +00:00
dependabot[bot] e5b9f7c3db Bump react-dev-utils from 10.2.1 to 11.0.4
Bumps [react-dev-utils](https://github.com/facebook/create-react-app/tree/HEAD/packages/react-dev-utils) from 10.2.1 to 11.0.4.
- [Release notes](https://github.com/facebook/create-react-app/releases)
- [Changelog](https://github.com/facebook/create-react-app/blob/master/CHANGELOG-1.x.md)
- [Commits](https://github.com/facebook/create-react-app/commits/HEAD/packages/react-dev-utils)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-12 15:08:40 +00:00
dependabot[bot] 42f0aa921c Bump axios from 0.19.2 to 0.21.1
Bumps [axios](https://github.com/axios/axios) from 0.19.2 to 0.21.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.19.2...v0.21.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-04 16:00:23 +00:00
235 changed files with 19958 additions and 17343 deletions
-15
View File
@@ -1,15 +0,0 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 eyebluecn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+23 -6
View File
@@ -2,12 +2,14 @@
[English Version](https://tank-doc.eyeblue.cn/en)
# 蓝眼云盘(3.1.2)
[在线Demo](https://tank.eyeblue.cn) (体验账号: demo 密码:123456)
# 蓝眼云盘(4.2.0)
[在线Demo](https://tank.eyeblue.cn) (体验账号:demo 密码:123456)
[更新文档 CHANGELOG](https://github.com/eyebluecn/tank/blob/master/CHANGELOG)
蓝眼云盘是蓝眼开源系列代表作品之一,致力于打造精致,优雅,简约的云盘。核心功能如下:
1. 文件列表
2. 文件/文件夹上传 + 拖拽上传
2. 文件/文件夹上传 + 拖拽上传 + 分片/断点续传
3. 文件分享
4. 文件监控统计
5. 回收站
@@ -15,6 +17,7 @@
7. [WebDav](https://tank-doc.eyeblue.cn/advance/webdav.html)
8. [扫描磁盘任务](https://tank-doc.eyeblue.cn/advance/scan.html)
9. [在线预览及自定义配置预览引擎](https://tank-doc.eyeblue.cn/advance/preview.html)
10. 无需安装数据库,支持文件数据库sqlite.
[更多内容请移步至文档](https://tank-doc.eyeblue.cn/)
@@ -35,17 +38,31 @@
## [安装文档](https://tank-doc.eyeblue.cn/basic/install.html)
## 启动本项目
```shell
# if not install yarn
# npm install -g yarn
yarn --registry=https://registry.npmmirror.com
yarn start
```
### Contribution
感谢所有蓝眼云盘的贡献者 [@zicla](https://github.com/zicla),[@seaheart](https://github.com/seaheart),[@yemuhe](https://github.com/yemuhe),[@hxsherry](https://github.com/hxsherry)
如果您也想参与进来,请尽情的fork, star, post issue, pull requests
当然你可以加入钉钉群一起直接交流,在钉钉群中可以获取最新beta版本。
当然你可以加入钉钉群(一群已满员)一起直接交流,在钉钉群中可以获取最新beta版本。
![](./doc/img/dingding.jpg)
![](./doc/img/dingding.jpeg)
群号:23156361
群号:44754005
### Support
IDE for this project is supported by [Jetbrains](https://jb.gg/OpenSourceSupport).
[![](./doc/img/jb_beam.png)](https://jb.gg/OpenSourceSupport)
### License
-101
View File
@@ -1,101 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const paths = require('./paths');
// Make sure that including paths.js after env.js will read .env variables.
delete require.cache[require.resolve('./paths')];
const NODE_ENV = process.env.NODE_ENV;
if (!NODE_ENV) {
throw new Error(
'The NODE_ENV environment variable is required but was not specified.'
);
}
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
const dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.local`,
`${paths.dotenv}.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
paths.dotenv,
].filter(Boolean);
// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) {
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
})
);
}
});
// We support resolving modules according to `NODE_PATH`.
// This lets you use absolute paths in imports inside large monorepos:
// https://github.com/facebook/create-react-app/issues/253.
// It works similar to `NODE_PATH` in Node itself:
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
// We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd());
process.env.NODE_PATH = (process.env.NODE_PATH || '')
.split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder))
.map(folder => path.resolve(appDirectory, folder))
.join(path.delimiter);
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
// injected into the application via DefinePlugin in webpack configuration.
const REACT_APP = /^REACT_APP_/i;
function getClientEnvironment(publicUrl) {
const raw = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key];
return env;
},
{
// Useful for determining whether we’re running in production mode.
// Most importantly, it switches React into the correct mode.
NODE_ENV: process.env.NODE_ENV || 'development',
// Useful for resolving the correct path to static assets in `public`.
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: publicUrl,
// We support configuring the sockjs pathname during development.
// These settings let a developer run multiple simultaneous projects.
// They are used as the connection `hostname`, `pathname` and `port`
// in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
// and `sockPort` options in webpack-dev-server.
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
}
);
// Stringify all values so we can feed into webpack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
return { raw, stringified };
}
module.exports = getClientEnvironment;
-66
View File
@@ -1,66 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const chalk = require('react-dev-utils/chalk');
const paths = require('./paths');
// Ensure the certificate and key provided are valid and if not
// throw an easy to debug error
function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
let encrypted;
try {
// publicEncrypt will throw an error with an invalid cert
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
} catch (err) {
throw new Error(
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
);
}
try {
// privateDecrypt will throw an error with an invalid key
crypto.privateDecrypt(key, encrypted);
} catch (err) {
throw new Error(
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
err.message
}`
);
}
}
// Read file and throw an error if it doesn't exist
function readEnvFile(file, type) {
if (!fs.existsSync(file)) {
throw new Error(
`You specified ${chalk.cyan(
type
)} in your env, but the file "${chalk.yellow(file)}" can't be found.`
);
}
return fs.readFileSync(file);
}
// Get the https config
// Return cert files if provided in env, otherwise just true or false
function getHttpsConfig() {
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
const isHttps = HTTPS === 'true';
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
const config = {
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
};
validateKeyAndCerts({ ...config, keyFile, crtFile });
return config;
}
return isHttps;
}
module.exports = getHttpsConfig;
-14
View File
@@ -1,14 +0,0 @@
'use strict';
// This is a custom Jest transformer turning style imports into empty objects.
// http://facebook.github.io/jest/docs/en/webpack.html
module.exports = {
process() {
return 'module.exports = {};';
},
getCacheKey() {
// The output is always the same.
return 'cssTransform';
},
};
-40
View File
@@ -1,40 +0,0 @@
'use strict';
const path = require('path');
const camelcase = require('camelcase');
// This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/en/webpack.html
module.exports = {
process(src, filename) {
const assetFilename = JSON.stringify(path.basename(filename));
if (filename.match(/\.svg$/)) {
// Based on how SVGR generates a component name:
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
const pascalCaseFilename = camelcase(path.parse(filename).name, {
pascalCase: true,
});
const componentName = `Svg${pascalCaseFilename}`;
return `const React = require('react');
module.exports = {
__esModule: true,
default: ${assetFilename},
ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
return {
$$typeof: Symbol.for('react.element'),
type: 'svg',
ref: ref,
key: null,
props: Object.assign({}, props, {
children: ${assetFilename}
})
};
}),
};`;
}
return `module.exports = ${assetFilename};`;
},
};
-141
View File
@@ -1,141 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const paths = require('./paths');
const chalk = require('react-dev-utils/chalk');
const resolve = require('resolve');
/**
* Get additional module paths based on the baseUrl of a compilerOptions object.
*
* @param {Object} options
*/
function getAdditionalModulePaths(options = {}) {
const baseUrl = options.baseUrl;
// We need to explicitly check for null and undefined (and not a falsy value) because
// TypeScript treats an empty string as `.`.
if (baseUrl == null) {
// If there's no baseUrl set we respect NODE_PATH
// Note that NODE_PATH is deprecated and will be removed
// in the next major release of create-react-app.
const nodePath = process.env.NODE_PATH || '';
return nodePath.split(path.delimiter).filter(Boolean);
}
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
// the default behavior.
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
return null;
}
// Allow the user set the `baseUrl` to `appSrc`.
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
return [paths.appSrc];
}
// If the path is equal to the root directory we ignore it here.
// We don't want to allow importing from the root directly as source files are
// not transpiled outside of `src`. We do allow importing them with the
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
// an alias.
if (path.relative(paths.appPath, baseUrlResolved) === '') {
return null;
}
// Otherwise, throw an error.
throw new Error(
chalk.red.bold(
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
' Create React App does not support other values at this time.'
)
);
}
/**
* Get webpack aliases based on the baseUrl of a compilerOptions object.
*
* @param {*} options
*/
function getWebpackAliases(options = {}) {
const baseUrl = options.baseUrl;
if (!baseUrl) {
return {};
}
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
if (path.relative(paths.appPath, baseUrlResolved) === '') {
return {
src: paths.appSrc,
};
}
}
/**
* Get jest aliases based on the baseUrl of a compilerOptions object.
*
* @param {*} options
*/
function getJestAliases(options = {}) {
const baseUrl = options.baseUrl;
if (!baseUrl) {
return {};
}
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
if (path.relative(paths.appPath, baseUrlResolved) === '') {
return {
'^src/(.*)$': '<rootDir>/src/$1',
};
}
}
function getModules() {
// Check if TypeScript is setup
const hasTsConfig = fs.existsSync(paths.appTsConfig);
const hasJsConfig = fs.existsSync(paths.appJsConfig);
if (hasTsConfig && hasJsConfig) {
throw new Error(
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
);
}
let config;
// If there's a tsconfig.json we assume it's a
// TypeScript project and set up the config
// based on tsconfig.json
if (hasTsConfig) {
const ts = require(resolve.sync('typescript', {
basedir: paths.appNodeModules,
}));
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
// Otherwise we'll check if there is jsconfig.json
// for non TS projects.
} else if (hasJsConfig) {
config = require(paths.appJsConfig);
}
config = config || {};
const options = config.compilerOptions || {};
const additionalModulePaths = getAdditionalModulePaths(options);
return {
additionalModulePaths: additionalModulePaths,
webpackAliases: getWebpackAliases(options),
jestAliases: getJestAliases(options),
hasTsConfig,
};
}
module.exports = getModules();
-72
View File
@@ -1,72 +0,0 @@
'use strict';
const path = require('path');
const fs = require('fs');
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
// "public path" at which the app is served.
// webpack needs to know it to put the right <script> hrefs into HTML even in
// single-page apps that may serve index.html for nested URLs like /todos/42.
// We can't use a relative path in HTML because we don't want to load something
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
const publicUrlOrPath = getPublicUrlOrPath(
process.env.NODE_ENV === 'development',
require(resolveApp('package.json')).homepage,
process.env.PUBLIC_URL
);
const moduleFileExtensions = [
'web.mjs',
'mjs',
'web.js',
'js',
'web.ts',
'ts',
'web.tsx',
'tsx',
'json',
'web.jsx',
'jsx',
];
// Resolve file paths in the same order as webpack
const resolveModule = (resolveFn, filePath) => {
const extension = moduleFileExtensions.find(extension =>
fs.existsSync(resolveFn(`${filePath}.${extension}`))
);
if (extension) {
return resolveFn(`${filePath}.${extension}`);
}
return resolveFn(`${filePath}.js`);
};
// config after eject: we're in ./config/
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('config/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
publicUrlOrPath,
};
module.exports.moduleFileExtensions = moduleFileExtensions;
-35
View File
@@ -1,35 +0,0 @@
'use strict';
const { resolveModuleName } = require('ts-pnp');
exports.resolveModuleName = (
typescript,
moduleName,
containingFile,
compilerOptions,
resolutionHost
) => {
return resolveModuleName(
moduleName,
containingFile,
compilerOptions,
resolutionHost,
typescript.resolveModuleName
);
};
exports.resolveTypeReferenceDirective = (
typescript,
moduleName,
containingFile,
compilerOptions,
resolutionHost
) => {
return resolveModuleName(
moduleName,
containingFile,
compilerOptions,
resolutionHost,
typescript.resolveTypeReferenceDirective
);
};
-17
View File
@@ -1,17 +0,0 @@
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
//为了解决前端开发时跨域问题,配置后台接口代理。
app.use(proxy(
'/api',
{
target: 'http://localhost:6010',
changeOrigin: true,
pathRewrite: {
'^/api': '/api'
}
}
));
};
-646
View File
@@ -1,646 +0,0 @@
"use strict";
const fs = require("fs");
const path = require("path");
const webpack = require("webpack");
const resolve = require("resolve");
const PnpWebpackPlugin = require("pnp-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const safePostCssParser = require("postcss-safe-parser");
const ManifestPlugin = require("webpack-manifest-plugin");
const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin");
const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin");
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent");
const paths = require("./paths");
const modules = require("./modules");
const getClientEnvironment = require("./env");
const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin");
const ForkTsCheckerWebpackPlugin = require("react-dev-utils/ForkTsCheckerWebpackPlugin");
const typescriptFormatter = require("react-dev-utils/typescriptFormatter");
const postcssNormalize = require("postcss-normalize");
const appPackageJson = require(paths.appPackageJson);
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false";
const imageInlineSizeLimit = parseInt(
process.env.IMAGE_INLINE_SIZE_LIMIT || "10000"
);
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === "development";
const isEnvProduction = webpackEnv === "production";
// Variable used for enabling profiling in Production
// passed into alias object. Uses a flag if passed into the build command
const isEnvProductionProfile =
isEnvProduction && process.argv.includes("--profile");
// We will provide `paths.publicUrlOrPath` to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
// Get environment variables to inject into our app.
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve("style-loader"),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
// css is located in `static/css`, use '../../' to locate index.html folder
// in production `paths.publicUrlOrPath` can be a relative path
options: paths.publicUrlOrPath.startsWith(".")
? { publicPath: "../../" }
: {},
},
{
loader: require.resolve("css-loader"),
options: cssOptions,
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve("postcss-loader"),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: "postcss",
plugins: () => [
require("postcss-flexbugs-fixes"),
require("postcss-preset-env")({
autoprefixer: {
flexbox: "no-2009",
},
stage: 3,
}),
// Adds PostCSS Normalize as the reset css with default options,
// so that it honors browserslist config in package.json
// which in turn let's users customize the target behavior as per their needs.
postcssNormalize(),
],
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push(
{
loader: require.resolve("resolve-url-loader"),
options: {
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
preProcessor
);
}
return loaders;
};
return {
mode: isEnvProduction ? "production" : isEnvDevelopment && "development",
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? "source-map"
: false
: isEnvDevelopment && "cheap-module-source-map",
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
entry: [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
isEnvDevelopment &&
require.resolve("react-dev-utils/webpackHotDevClient"),
// Finally, this is your app's code:
paths.appIndexJs,
// We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh.
].filter(Boolean),
output: {
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
// Add /* filename */ comments to generated require()s in the output.
pathinfo: isEnvDevelopment,
// There will be one main bundle, and one file per asynchronous chunk.
// In development, it does not produce real files.
filename: isEnvProduction
? "static/js/[name].[contenthash:8].js"
: isEnvDevelopment && "static/js/bundle.js",
// TODO: remove this when upgrading to webpack 5
futureEmitAssets: true,
// There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction
? "static/js/[name].[contenthash:8].chunk.js"
: isEnvDevelopment && "static/js/[name].chunk.js",
// webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: paths.publicUrlOrPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? (info) =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, "/")
: isEnvDevelopment &&
((info) =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")),
// Prevents conflicts when multiple webpack runtimes (from different apps)
// are used on the same page.
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
// this defaults to 'window', but by setting it to 'this' then
// module chunks which are built will work in web workers as well.
globalObject: "this",
},
optimization: {
minimize: isEnvProduction,
minimizer: [
// This is only used in production mode
new TerserPlugin({
terserOptions: {
parse: {
// We want terser to parse ecma 8 code. However, we don't want it
// to apply any minification steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
// Disabled because of an issue with Terser breaking valid code:
// https://github.com/facebook/create-react-app/issues/5250
// Pending further investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
//保持类名 by zicla
keep_fnames: true,
},
mangle: {
safari10: true,
//保持类名 by zicla
keep_fnames: true,
},
// Added for profiling in devtools
keep_classnames: isEnvProductionProfile,
keep_fnames: isEnvProductionProfile,
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},
sourceMap: shouldUseSourceMap,
}),
// This is only used in production mode
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
// `inline: false` forces the sourcemap to be output into a
// separate file
inline: false,
// `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap
annotation: true,
}
: false,
},
cssProcessorPluginOptions: {
preset: ["default", { minifyFontValues: { removeQuotes: false } }],
},
}),
],
// Automatically split vendor and commons
// https://twitter.com/wSokra/status/969633336732905474
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: {
chunks: "all",
name: false,
},
// Keep the runtime chunk separated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
// https://github.com/facebook/create-react-app/issues/5358
runtimeChunk: {
name: (entrypoint) => `runtime-${entrypoint.name}`,
},
},
resolve: {
// This allows you to set a fallback for where webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
modules: ["node_modules", paths.appNodeModules].concat(
modules.additionalModulePaths || []
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: paths.moduleFileExtensions
.map((ext) => `.${ext}`)
.filter((ext) => useTypeScript || !ext.includes("ts")),
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
"react-native": "react-native-web",
// Allows for better profiling with ReactDevTools
...(isEnvProductionProfile && {
"react-dom$": "react-dom/profiling",
"scheduler/tracing": "scheduler/tracing-profiling",
}),
...(modules.webpackAliases || {}),
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
// guards against forgotten dependencies and such.
PnpWebpackPlugin,
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data URLs to avoid requests.
// A missing `test` is equivalent to a match.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"),
options: {
limit: imageInlineSizeLimit,
name: "static/media/[name].[hash:8].[ext]",
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve("babel-loader"),
options: {
customize: require.resolve(
"babel-preset-react-app/webpack-overrides"
),
plugins: [
[
require.resolve("babel-plugin-named-asset-import"),
{
loaderMap: {
svg: {
ReactComponent:
"@svgr/webpack?-svgo,+titleProp,+ref![path]",
},
},
},
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
compact: isEnvProduction,
},
},
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
{
test: /\.(js|mjs)$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve("babel-loader"),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve("babel-preset-react-app/dependencies"),
{ helpers: true },
],
],
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
// Babel sourcemaps are needed for debugging into node_modules
// code. Without the options below, debuggers like VSCode
// show incorrect code and set breakpoints on the wrong lines.
sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use MiniCSSExtractPlugin to extract that CSS
// to a file, but in development "style" loader enables hot editing
// of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
}),
},
// less-loader by zicla
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
{
loader: "less-loader",
options: {
sourceMap: true,
lessOptions: {
modifyVars: {
'primary-color': '#215891',
},
javascriptEnabled: true,
}
},
}
),
sideEffects: true,
},
// less-loader by zicla
{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
{
loader: "less-loader",
options: {
sourceMap: true,
lessOptions: {
modifyVars: {
'primary-color': '#215891',
},
javascriptEnabled: true,
}
},
}
),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve("file-loader"),
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: "static/media/[name].[hash:8].[ext]",
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
// https://github.com/facebook/create-react-app/issues/5358
isEnvProduction &&
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
// It will be an empty string unless you specify "homepage"
// in `package.json`, in which case it will be the pathname of that URL.
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// This gives some necessary context to module not found errors, such as
// the requesting resource.
new ModuleNotFoundPlugin(paths.appPath),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV is set to production
// during a production build.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// This is necessary to emit hot updates (currently CSS only):
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebook/create-react-app/issues/240
isEnvDevelopment && new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebook/create-react-app/issues/186
isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvProduction &&
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "static/css/[name].[contenthash:8].css",
chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
}),
// Generate an asset manifest file with the following content:
// - "files" key: Mapping of all asset filenames to their corresponding
// output file so that tools can pick it up without having to parse
// `index.html`
// - "entrypoints" key: Array of files which are included in `index.html`,
// can be used to reconstruct the HTML if necessary
new ManifestPlugin({
fileName: "asset-manifest.json",
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
(fileName) => !fileName.endsWith(".map")
);
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync("typescript", {
basedir: paths.appNodeModules,
}),
async: isEnvDevelopment,
useTypescriptIncrementalApi: true,
checkSyntacticErrors: true,
resolveModuleNameModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
resolveTypeReferenceDirectiveModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
tsconfig: paths.appTsConfig,
reportFiles: [
"**",
"!**/__tests__/**",
"!**/?(*.)(spec|test).*",
"!**/src/setupProxy.*",
"!**/src/setupTests.*",
],
silent: true,
// The formatter is invoked directly in WebpackDevServerUtils during development
formatter: isEnvProduction ? typescriptFormatter : undefined,
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser.
// Tell webpack to provide empty mocks for them so importing them works.
node: {
module: "empty",
dgram: "empty",
dns: "mock",
fs: "empty",
http2: "empty",
net: "empty",
tls: "empty",
child_process: "empty",
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};
};
-130
View File
@@ -1,130 +0,0 @@
'use strict';
const fs = require('fs');
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
const paths = require('./paths');
const getHttpsConfig = require('./getHttpsConfig');
const host = process.env.HOST || '0.0.0.0';
const sockHost = process.env.WDS_SOCKET_HOST;
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
const sockPort = process.env.WDS_SOCKET_PORT;
module.exports = function(proxy, allowedHost) {
return {
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
// websites from potentially accessing local content through DNS rebinding:
// https://github.com/webpack/webpack-dev-server/issues/887
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
// However, it made several existing use cases such as development in cloud
// environment or subdomains in development significantly more complicated:
// https://github.com/facebook/create-react-app/issues/2271
// https://github.com/facebook/create-react-app/issues/2233
// While we're investigating better solutions, for now we will take a
// compromise. Since our WDS configuration only serves files in the `public`
// folder we won't consider accessing them a vulnerability. However, if you
// use the `proxy` feature, it gets more dangerous because it can expose
// remote code execution vulnerabilities in backends like Django and Rails.
// So we will disable the host check normally, but enable it if you have
// specified the `proxy` setting. Finally, we let you override it if you
// really know what you're doing with a special environment variable.
disableHostCheck:
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
// Enable gzip compression of generated files.
compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful.
// It will still show compile warnings and errors with this setting.
clientLogLevel: 'none',
// By default WebpackDevServer serves physical files from current directory
// in addition to all the virtual build products that it serves from memory.
// This is confusing because those files won’t automatically be available in
// production build folder unless we copy them. However, copying the whole
// project directory is dangerous because we may expose sensitive files.
// Instead, we establish a convention that only files in `public` directory
// get served. Our build script will copy `public` into the `build` folder.
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
// Note that we only recommend to use `public` folder as an escape hatch
// for files like `favicon.ico`, `manifest.json`, and libraries that are
// for some reason broken when imported through webpack. If you just want to
// use an image, put it in `src` and `import` it from JavaScript instead.
contentBase: paths.appPublic,
contentBasePublicPath: paths.publicUrlOrPath,
// By default files from `contentBase` will not trigger a page reload.
watchContentBase: true,
// Enable hot reloading server. It will provide WDS_SOCKET_PATH endpoint
// for the WebpackDevServer client so it can learn when the files were
// updated. The WebpackDevServer client is included as an entry point
// in the webpack development configuration. Note that only changes
// to CSS are currently hot reloaded. JS changes will refresh the browser.
hot: true,
// Use 'ws' instead of 'sockjs-node' on server since we're using native
// websockets in `webpackHotDevClient`.
transportMode: 'ws',
// Prevent a WS client from getting injected as we're already including
// `webpackHotDevClient`.
injectClient: false,
// Enable custom sockjs pathname for websocket connection to hot reloading server.
// Enable custom sockjs hostname, pathname and port for websocket connection
// to hot reloading server.
sockHost,
sockPath,
sockPort,
// It is important to tell WebpackDevServer to use the same "publicPath" path as
// we specified in the webpack config. When homepage is '.', default to serving
// from the root.
// remove last slash so user can land on `/test` instead of `/test/`
publicPath: paths.publicUrlOrPath.slice(0, -1),
// WebpackDevServer is noisy by default so we emit custom message instead
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
quiet: true,
// Reportedly, this avoids CPU overload on some systems.
// https://github.com/facebook/create-react-app/issues/293
// src/node_modules is not ignored to support absolute imports
// https://github.com/facebook/create-react-app/issues/1065
watchOptions: {
ignored: ignoredFiles(paths.appSrc),
},
https: getHttpsConfig(),
host,
overlay: false,
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebook/create-react-app/issues/387.
disableDotRule: true,
index: paths.publicUrlOrPath,
},
public: allowedHost,
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
proxy,
before(app, server) {
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
// middlewares before `redirectServedPath` otherwise will not have any effect
// This lets us fetch source contents from webpack for the error overlay
app.use(evalSourceMapMiddleware(server));
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
if (fs.existsSync(paths.proxySetup)) {
// This registers user provided middleware for proxy reasons
require(paths.proxySetup)(app);
}
},
after(app) {
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
app.use(redirectServedPath(paths.publicUrlOrPath));
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
},
};
};
+38
View File
@@ -0,0 +1,38 @@
const CracoLessPlugin = require('craco-less');
module.exports = {
devServer: {
port: 6015,
// proxy for local development.
proxy: {
'/api': {
target: 'http://localhost:6010',
changeOrigin: true,
pathRewrite: {
'^/api': '/api',
},
},
},
},
plugins: [
{
// less loader.
plugin: CracoLessPlugin,
options: {
// resolve-url-loader只对sass生效,craco-less默认使用sass配置,所以这里手动过滤掉resolve-url-loader
modifyLessRule: (lessRule) => {
lessRule.use = lessRule.use.filter(
(i) => !i.loader.includes('resolve-url-loader')
);
return lessRule;
},
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#215891' },
javascriptEnabled: true,
},
},
},
},
],
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

+1
View File
@@ -0,0 +1 @@
<code_scheme name="Project" version="173" />
+39 -75
View File
@@ -1,37 +1,36 @@
{
"name": "tank-front",
"version": "3.1.2",
"version": "4.1.1",
"private": true,
"dependencies": {
"@ant-design/icons": "4.2.1",
"pretty-format": "25.5.0",
"@types/echarts": "^4.6.1",
"@types/photoswipe": "^4.1.0",
"antd": "4.4.3",
"axios": "^0.19.2",
"camelcase": "^5.3.1",
"@ant-design/icons": "4.7.0",
"@craco/craco": "6.4.3",
"@types/echarts": "4.6.1",
"@types/node": "16.7.13",
"@types/qs": "^6.9.7",
"@types/react": "17.0.2",
"@types/react-dom": "17.0.2",
"@types/react-router-dom": "^5.3.3",
"antd": "4.19.2",
"axios": "^0.26.1",
"comma-separated-values": "^3.6.4",
"dotenv": "8.2.0",
"dotenv-expand": "5.1.0",
"echarts": "^4.8.0",
"echarts-for-react": "^2.0.16",
"fs-extra": "^8.1.0",
"identity-obj-proxy": "3.0.0",
"photoswipe": "^4.1.3",
"qs": "^6.9.4",
"react": "^16.13.1",
"react-app-polyfill": "^1.0.6",
"react-dev-utils": "^10.2.1",
"react-dom": "^16.13.1",
"craco-less": "2.0.0",
"echarts": "4.8.0",
"echarts-for-react": "2.0.16",
"qs": "^6.10.3",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-photo-view": "^1.2.3",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"semver": "6.3.0",
"ts-pnp": "1.1.6",
"react-scripts": "5.0.0",
"spark-md5": "^3.0.2",
"typescript": "4.4.2",
"velocity-react": "^1.4.3"
},
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js"
"start": "craco start",
"build": "craco build"
},
"browserslist": {
"production": [
@@ -45,57 +44,22 @@
"last 1 safari version"
]
},
"babel": {
"presets": [
"react-app"
],
"plugins": [
[
"import",
{
"libraryName": "antd",
"style": true
},
"antd"
]
"devDependencies": {
"@types/spark-md5": "^3.0.5",
"husky": "^8.0.3",
"lint-staged": "^13.2.2",
"prettier": "^2.8.8"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"git add"
]
},
"devDependencies": {
"@babel/core": "7.9.0",
"@svgr/webpack": "4.3.3",
"@types/node": "^12.0.0",
"@types/qs": "^6.9.3",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"@types/react-router-dom": "^5.1.5",
"@types/react-transition-group": "^4.4.0",
"babel-loader": "8.1.0",
"babel-plugin-import": "^1.13.0",
"babel-plugin-named-asset-import": "^0.3.6",
"babel-preset-react-app": "^9.1.2",
"case-sensitive-paths-webpack-plugin": "2.3.0",
"css-loader": "3.4.2",
"file-loader": "4.3.0",
"html-webpack-plugin": "4.0.0-beta.11",
"less": "^3.11.2",
"less-loader": "^6.1.0",
"mini-css-extract-plugin": "0.9.0",
"optimize-css-assets-webpack-plugin": "5.0.3",
"pnp-webpack-plugin": "1.6.4",
"postcss-flexbugs-fixes": "4.1.0",
"postcss-loader": "3.0.0",
"postcss-normalize": "8.0.1",
"postcss-preset-env": "6.7.0",
"postcss-safe-parser": "4.0.1",
"prettier": "^2.0.5",
"resolve": "1.15.0",
"resolve-url-loader": "3.1.1",
"style-loader": "0.23.1",
"terser-webpack-plugin": "2.3.5",
"typescript": "~3.7.2",
"url-loader": "2.3.0",
"webpack": "4.42.0",
"webpack-dev-server": "3.10.3",
"webpack-manifest-plugin": "2.2.0"
}
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
}
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
tabWidth: 2,
singleQuote: true,
endOfLine: 'auto',
};
-211
View File
@@ -1,211 +0,0 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const path = require('path');
const chalk = require('react-dev-utils/chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
const configFactory = require('../config/webpack.config');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// Generate configuration
const config = configFactory('production');
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
return measureFileSizesBeforeBuild(paths.appBuild);
})
.then(previousFileSizes => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
);
console.log(
'To ignore, add ' +
chalk.cyan('// eslint-disable-next-line') +
' to the line before.\n'
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
}
console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
console.log();
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrlOrPath;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
},
err => {
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
if (tscCompileOnError) {
console.log(
chalk.yellow(
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
)
);
printBuildError(err);
} else {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
}
)
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
// We used to support resolving modules according to `NODE_PATH`.
// This now has been deprecated in favor of jsconfig/tsconfig.json
// This lets you use absolute paths in imports inside large monorepos:
if (process.env.NODE_PATH) {
console.log(
chalk.yellow(
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
)
);
console.log();
}
console.log('Creating an optimized production build...');
const compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
if (err) {
if (!err.message) {
return reject(err);
}
let errMessage = err.message;
// Add additional information for postcss errors
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
errMessage +=
'\nCompileError: Begins at CSS selector ' +
err['postcssNode'].selector;
}
messages = formatWebpackMessages({
errors: [errMessage],
warnings: [],
});
} else {
messages = formatWebpackMessages(
stats.toJson({ all: false, warnings: true, errors: true })
);
}
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(new Error(messages.errors.join('\n\n')));
}
if (
process.env.CI &&
(typeof process.env.CI !== 'string' ||
process.env.CI.toLowerCase() !== 'false') &&
messages.warnings.length
) {
console.log(
chalk.yellow(
'\nTreating warnings as errors because process.env.CI = true.\n' +
'Most CI servers set it automatically.\n'
)
);
return reject(new Error(messages.warnings.join('\n\n')));
}
return resolve({
stats,
previousFileSizes,
warnings: messages.warnings,
});
});
});
}
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml,
});
}
-166
View File
@@ -1,166 +0,0 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const fs = require('fs');
const chalk = require('react-dev-utils/chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const {
choosePort,
createCompiler,
prepareProxy,
prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const paths = require('../config/paths');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// Tools like Cloud9 rely on this.
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 6015;
const HOST = process.env.HOST || '0.0.0.0';
if (process.env.HOST) {
console.log(
chalk.cyan(
`Attempting to bind to HOST environment variable: ${chalk.yellow(
chalk.bold(process.env.HOST)
)}`
)
);
console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
);
console.log(
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
);
console.log();
}
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
return choosePort(HOST, DEFAULT_PORT);
})
.then(port => {
if (port == null) {
// We have not found a port.
return;
}
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const useTypeScript = fs.existsSync(paths.appTsConfig);
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
const urls = prepareUrls(
protocol,
HOST,
port,
paths.publicUrlOrPath.slice(0, -1)
);
const devSocket = {
warnings: warnings =>
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
errors: errors =>
devServer.sockWrite(devServer.sockets, 'errors', errors),
};
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler({
appName,
config,
devSocket,
urls,
useYarn,
useTypeScript,
tscCompileOnError,
webpack,
});
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(
proxySetting,
paths.appPublic,
paths.publicUrlOrPath
);
// Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig(
proxyConfig,
urls.lanUrlForConfig
);
const devServer = new WebpackDevServer(compiler, serverConfig);
// Launch WebpackDevServer.
devServer.listen(port, HOST, err => {
if (err) {
return console.log(err);
}
if (isInteractive) {
clearConsole();
}
// We used to support resolving modules according to `NODE_PATH`.
// This now has been deprecated in favor of jsconfig/tsconfig.json
// This lets you use absolute paths in imports inside large monorepos:
if (process.env.NODE_PATH) {
console.log(
chalk.yellow(
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
)
);
console.log();
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
['SIGINT', 'SIGTERM'].forEach(function(sig) {
process.on(sig, function() {
devServer.close();
process.exit();
});
});
if (isInteractive || process.env.CI !== 'true') {
// Gracefully exit when stdin ends
process.stdin.on('end', function() {
devServer.close();
process.exit();
});
process.stdin.resume();
}
})
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
-53
View File
@@ -1,53 +0,0 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'test';
process.env.NODE_ENV = 'test';
process.env.PUBLIC_URL = '';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const jest = require('jest');
const execSync = require('child_process').execSync;
let argv = process.argv.slice(2);
function isInGitRepository() {
try {
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
function isInMercurialRepository() {
try {
execSync('hg --cwd . root', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
// Watch unless on CI or explicitly running all tests
if (
!process.env.CI &&
argv.indexOf('--watchAll') === -1 &&
argv.indexOf('--watchAll=false') === -1
) {
// https://github.com/facebook/create-react-app/issues/5210
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
argv.push(hasSourceControl ? '--watch' : '--watchAll');
}
jest.run(argv);
+1
View File
@@ -1,2 +1,3 @@
//import global style.
@import './assets/css/index.less';
@import '~antd/dist/antd.less';
+4 -4
View File
@@ -1,7 +1,7 @@
import React from 'react';
import {BrowserRouter as Router} from "react-router-dom";
import {ConfigProvider} from 'antd';
import Frame from "./pages/Frame";
import { BrowserRouter as Router } from 'react-router-dom';
import { ConfigProvider } from 'antd';
import Frame from './pages/Frame';
import en_US from 'antd/es/locale/en_US';
// global less 须放在Frame组件之后,不然会出现按需加载的less文件覆盖样式问题
@@ -11,7 +11,7 @@ function App() {
return (
<Router>
<ConfigProvider locale={en_US}>
<Frame/>
<Frame />
</ConfigProvider>
</Router>
);
File diff suppressed because it is too large Load Diff
+27 -25
View File
@@ -23,28 +23,28 @@
}
.btn-action {
margin: 0 3px;
display: inline-block;
opacity: 0.85;
-webkit-transition: all 0.1s;
-o-transition: all 0.1s;
transition: all 0.1s;
cursor: pointer;
margin: 0 3px;
display: inline-block;
opacity: 0.85;
-webkit-transition: all 0.1s;
-o-transition: all 0.1s;
transition: all 0.1s;
cursor: pointer;
outline: none;
&:hover {
text-decoration: none;
opacity: 1;
-moz-transform: scale(1.2);
-webkit-transform: scale(1.2);
-o-transform: scale(1.2);
-ms-transform: scale(1.2);
transform: scale(1.2);
}
&:focus {
outline: none;
&:hover {
text-decoration: none;
opacity: 1;
-moz-transform: scale(1.2);
-webkit-transform: scale(1.2);
-o-transform: scale(1.2);
-ms-transform: scale(1.2);
transform: scale(1.2);
}
&:focus {
outline: none;
}
}
}
.btn-primary {
@@ -52,12 +52,14 @@
background-color: #337ab7;
border-color: #2e6da4;
}
.btn-primary:focus,
.btn-primary.focus {
color: #fff;
background-color: #286090;
border-color: #122b40;
}
.btn-primary:hover {
color: #fff;
background-color: #286090;
@@ -73,11 +75,11 @@
//编辑区域按钮,鼠标一上去按钮变大
.action-buttons {
a {
.btn-action;
}
a {
.btn-action;
}
}
.cursor {
cursor: pointer;
cursor: pointer;
}
+371 -93
View File
@@ -1,113 +1,391 @@
@import "./variables.less";
.bg-primary{ background-color: @brand-primary; color: white;}
.bg-success{ background-color: @brand-success; color: white; }
.bg-info{ background-color: @brand-info; color: white; }
.bg-warning{ background-color: @brand-warning; color: white; }
.bg-danger{ background-color: @brand-danger; color: white; }
.bg-gray{ background-color: @brand-gray; color: white; }
.bg-laxative{ background-color: @brand-laxative; color: white; }
.bg-primary {
background-color: @brand-primary;
color: white;
}
.bg-success {
background-color: @brand-success;
color: white;
}
.bg-info {
background-color: @brand-info;
color: white;
}
.bg-warning {
background-color: @brand-warning;
color: white;
}
.bg-danger {
background-color: @brand-danger;
color: white;
}
.bg-gray {
background-color: @brand-gray;
color: white;
}
.bg-laxative {
background-color: @brand-laxative;
color: white;
}
.text-primary{ color: @brand-primary;}
.text-success{ color: @brand-success; }
.text-info{ color: @brand-info; }
.text-warning{ color: @brand-warning; }
.text-danger{ color: @brand-danger; }
.text-gray{ color: @brand-gray; }
.text-laxative{ color: @brand-laxative; }
.text-theme { color: @primary-color; }
.text-primary {
color: @brand-primary;
}
.text-success {
color: @brand-success;
}
.text-info {
color: @brand-info;
}
.text-warning {
color: @brand-warning;
}
.text-danger {
color: @brand-danger;
}
.text-gray {
color: @brand-gray;
}
.text-laxative {
color: @brand-laxative;
}
.text-theme {
color: @primary-color;
}
.bg-navy {
background-color: #001F3F;
}
.bg-blue {
background-color: #0074D9;
}
.bg-aqua {
background-color: #7FDBFF;
}
.bg-aliceblue {
background-color: aliceblue;
}
.bg-pink {
background-color: pink;
}
.bg-azure {
background-color: azure;
}
.bg-teal {
background-color: #39CCCC;
}
.bg-olive {
background-color: #3D9970;
}
.bg-green {
background-color: #2ECC40;
}
.bg-lime {
background-color: #01FF70;
}
.bg-yellow {
background-color: #FFDC00;
}
.bg-pink {
color: pink;
}
.bg-orange {
background-color: #FF851B;
}
.bg-red {
background-color: #FF4136;
}
.bg-fuchsia {
background-color: #F012BE;
}
.bg-purple {
background-color: #B10DC9;
}
.bg-maroon {
background-color: #85144B;
}
.bg-white {
background-color: #FFFFFF;
}
.bg-gray {
background-color: #AAAAAA;
}
.bg-silver {
background-color: #DDDDDD;
}
.bg-navy { background-color: #001F3F;}
.bg-blue { background-color: #0074D9;}
.bg-aqua { background-color: #7FDBFF;}
.bg-aliceblue { background-color: aliceblue;}
.bg-pink { background-color: pink;}
.bg-azure { background-color: azure;}
.bg-teal { background-color: #39CCCC;}
.bg-olive { background-color: #3D9970;}
.bg-green { background-color: #2ECC40;}
.bg-lime { background-color: #01FF70;}
.bg-yellow { background-color: #FFDC00;}
.bg-pink { color: pink; }
.bg-orange { background-color: #FF851B;}
.bg-red { background-color: #FF4136;}
.bg-fuchsia { background-color: #F012BE;}
.bg-purple { background-color: #B10DC9;}
.bg-maroon { background-color: #85144B;}
.bg-white { background-color: #FFFFFF;}
.bg-gray { background-color: #AAAAAA;}
.bg-silver { background-color: #DDDDDD;}
.bg-silver-white {
background-color: #EEEEEE;
}
.bg-black { background-color: #111111;}
.bg-111{ background-color:#111 }
.bg-222{ background-color:#222 }
.bg-333{ background-color:#333 }
.bg-444{ background-color:#444 }
.bg-555{ background-color:#555 }
.bg-666{ background-color:#666 }
.bg-777{ background-color:#777 }
.bg-888{ background-color:#888 }
.bg-999{ background-color:#999 }
.bg-aaa{ background-color:#aaa }
.bg-bbb{ background-color:#bbb }
.bg-ccc{ background-color:#ccc }
.bg-ddd{ background-color:#ddd }
.bg-eee{ background-color:#eee }
.bg-black {
background-color: #111111;
}
.bg-111 {
background-color: #111
}
.bg-222 {
background-color: #222
}
.bg-333 {
background-color: #333
}
.bg-444 {
background-color: #444
}
.bg-555 {
background-color: #555
}
.bg-666 {
background-color: #666
}
.bg-777 {
background-color: #777
}
.bg-888 {
background-color: #888
}
.bg-999 {
background-color: #999
}
.bg-aaa {
background-color: #aaa
}
.bg-bbb {
background-color: #bbb
}
.bg-ccc {
background-color: #ccc
}
.bg-ddd {
background-color: #ddd
}
.bg-eee {
background-color: #eee
}
/* Colors */
.navy { color: #001F3F;}
.blue { color: #0074D9;}
.aqua { color: #7FDBFF;}
.teal { color: #39CCCC;}
.olive { color: #3D9970;}
.green { color: #2ECC40;}
.lime { color: #01FF70;}
.yellow { color: #FFDC00;}
.pink { color: pink; }
.orange { color: #FF851B;}
.red { color: #FF4136;}
.fuchsia { color: #F012BE;}
.purple { color: #B10DC9;}
.maroon { color: #85144B;}
.white { color: #FFFFFF;}
.silver { color: #DDDDDD;}
.gray { color: #AAAAAA;}
.black { color: #111111;}
.navy {
color: #001F3F;
}
.blue {
color: #0074D9;
}
.aqua {
color: #7FDBFF;
}
.teal {
color: #39CCCC;
}
.olive {
color: #3D9970;
}
.green {
color: #2ECC40;
}
.lime {
color: #01FF70;
}
.yellow {
color: #FFDC00;
}
.pink {
color: pink;
}
.orange {
color: #FF851B;
}
.red {
color: #FF4136;
}
.fuchsia {
color: #F012BE;
}
.purple {
color: #B10DC9;
}
.maroon {
color: #85144B;
}
.white {
color: #FFFFFF;
}
.silver {
color: #DDDDDD;
}
.gray {
color: #AAAAAA;
}
.black {
color: #111111;
}
.color-111{color:#111}
.color-222{color:#222}
.color-333{color:#333}
.color-444{color:#444}
.color-555{color:#555}
.color-666{color:#666}
.color-777{color:#777}
.color-888{color:#888}
.color-999{color:#999}
.color-aaa{color:#aaa}
.color-bbb{color:#bbb}
.color-ccc{color:#ccc}
.color-ddd{color:#ddd}
.color-eee{color:#eee}
.color-111 {
color: #111
}
.color-222 {
color: #222
}
.color-333 {
color: #333
}
.color-444 {
color: #444
}
.color-555 {
color: #555
}
.color-666 {
color: #666
}
.color-777 {
color: #777
}
.color-888 {
color: #888
}
.color-999 {
color: #999
}
.color-aaa {
color: #aaa
}
.color-bbb {
color: #bbb
}
.color-ccc {
color: #ccc
}
.color-ddd {
color: #ddd
}
.color-eee {
color: #eee
}
.color-text { color: #660E7A;}
.color-doc { color: #295496;}
.color-xls { color: #1E6C41;}
.color-ppt { color: #D04324;}
.color-pdf { color: #E40B0B;}
.color-audio { color: #5bc0de;}
.color-video { color: #5cb85c;}
.color-image { color: #0074D9;}
.color-archive { color: #4437f2;}
.color-text {
color: #660E7A;
}
.color-light-active{ color: #ffc60c;}
.color-light-inactive{ color:#ccc;}
.color-doc {
color: #295496;
}
.color-xls {
color: #1E6C41;
}
.color-ppt {
color: #D04324;
}
.color-pdf {
color: #E40B0B;
}
.color-audio {
color: #5bc0de;
}
.color-video {
color: #5cb85c;
}
.color-image {
color: #0074D9;
}
.color-archive {
color: #4437f2;
}
.color-light-active {
color: #ffc60c;
}
.color-light-inactive {
color: #ccc;
}
+21 -21
View File
@@ -1,38 +1,38 @@
#font{
.f(@from,@end,@step){
.mX(@f,@e,@s) when (@e >= @f){
.f@{e}{
font-size:@e*1px!important;
#font {
.f(@from,@end,@step) {
.mX(@f,@e,@s) when (@e >= @f) {
.f@{e} {
font-size: @e*1px !important;
}
.mX(@f,@e - @s,@s);
.mX(@f, @e - @s, @s);
}
.mX(@from,@end,@step);
.mX(@from, @end, @step);
}
}
#font > .f(10,80,1);
#font > .f(10, 80, 1);
#font{
.ln(@from,@end,@step){
.mX(@f,@e,@s) when (@e >= @f){
.ln@{e}{
line-height:@e*1px!important;
#font {
.ln(@from,@end,@step) {
.mX(@f,@e,@s) when (@e >= @f) {
.ln@{e} {
line-height: @e*1px !important;
}
.mX(@f,@e - @s,@s);
.mX(@f, @e - @s, @s);
}
.mX(@from,@end,@step);
.mX(@from, @end, @step);
}
}
#font > .ln(10,100,1);
#font > .ln(10, 100, 1);
.bold{
font-weight:bold;
.bold {
font-weight: bold;
}
.italic{
font-style:italic;
.italic {
font-style: italic;
}
+7
View File
@@ -139,6 +139,7 @@ a {
.hot-area {
position: relative;
&:after {
position: absolute;
top: -10px;
@@ -148,3 +149,9 @@ a {
content: '';
}
}
.custom-handle-modal {
.ant-modal-confirm-btns {
display: none;
}
}
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

+10 -1
View File
@@ -1 +1,10 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189634329" class="icon" style="" viewBox="0 0 1219 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10648" xmlns:xlink="http://www.w3.org/1999/xlink" width="238.0859375" height="200"><defs><style type="text/css"></style></defs><path d="M1197.787429 327.192381a19.504762 19.504762 0 0 1 19.553523 19.407238v614.156191c0 34.913524-28.574476 63.24419-63.878095 63.24419H63.878095C28.623238 1024 0 995.718095 0 960.75581V346.599619a19.504762 19.504762 0 0 1 19.602286-19.358476h1178.185143z m-66.80381-213.089524C1179.550476 114.102857 1219.047619 153.209905 1219.047619 201.386667v80.213333H1.657905V88.259048C1.657905 39.497143 41.593905 0 90.794667 0h290.864762c17.408 0 33.987048 7.655619 45.104761 20.967619l78.214096 93.135238h626.005333z" fill="#1ab394" p-id="10649"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1514189634329" class="icon" style="" viewBox="0 0 1219 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="10648" width="238.0859375" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M1197.787429 327.192381a19.504762 19.504762 0 0 1 19.553523 19.407238v614.156191c0 34.913524-28.574476 63.24419-63.878095 63.24419H63.878095C28.623238 1024 0 995.718095 0 960.75581V346.599619a19.504762 19.504762 0 0 1 19.602286-19.358476h1178.185143z m-66.80381-213.089524C1179.550476 114.102857 1219.047619 153.209905 1219.047619 201.386667v80.213333H1.657905V88.259048C1.657905 39.497143 41.593905 0 90.794667 0h290.864762c17.408 0 33.987048 7.655619 45.104761 20.967619l78.214096 93.135238h626.005333z"
fill="#1ab394" p-id="10649"></path>
</svg>

Before

Width:  |  Height:  |  Size: 943 B

After

Width:  |  Height:  |  Size: 949 B

+14 -1
View File
@@ -1 +1,14 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189353878" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1197" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M9.367 0h992.97v341.333H9.366z" fill="#55C7F7" p-id="1198"></path><path d="M9.367 341.333h992.97v341.334H9.366z" fill="#F95F5D" p-id="1199"></path><path d="M9.367 682.667h992.97V1024H9.366z" fill="#7ECF3B" p-id="1200"></path><path d="M350.7 0h310.303v1024H350.7z" fill="#FDAF42" p-id="1201"></path><path d="M304.154 386.638V646.05h403.394V386.638H304.154z m356.849 215.97H350.7V429.46h310.303v173.15z" fill="#FFFFFF" p-id="1202"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1514189353878" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="1197" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M9.367 0h992.97v341.333H9.366z" fill="#55C7F7" p-id="1198"></path>
<path d="M9.367 341.333h992.97v341.334H9.366z" fill="#F95F5D" p-id="1199"></path>
<path d="M9.367 682.667h992.97V1024H9.366z" fill="#7ECF3B" p-id="1200"></path>
<path d="M350.7 0h310.303v1024H350.7z" fill="#FDAF42" p-id="1201"></path>
<path d="M304.154 386.638V646.05h403.394V386.638H304.154z m356.849 215.97H350.7V429.46h310.303v173.15z"
fill="#FFFFFF" p-id="1202"></path>
</svg>

Before

Width:  |  Height:  |  Size: 828 B

After

Width:  |  Height:  |  Size: 855 B

+14 -1
View File
@@ -1 +1,14 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543321997742" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3887" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M160 32c-12 0-24.8 4.8-33.6 14.4S112 68 112 80v864c0 12 4.8 24.8 14.4 33.6C136 987.2 148 992 160 992h704c12 0 24.8-4.8 33.6-14.4C907.2 968 912 956 912 944V304L640 32H160z" fill="#FF5562" p-id="3888"></path><path d="M912 304H688c-12 0-24.8-4.8-33.6-14.4-9.6-8.8-14.4-21.6-14.4-33.6V32l272 272z" fill="#FFBBC0" p-id="3889"></path><path d="M669.6 491.2c0-1.6 0.8-4 0-6.4V369.6c0-4.8-2.4-8.8-5.6-12-4-3.2-8-4-12.8-3.2l-250.4 70.4c-7.2 1.6-12 7.2-12 14.4v275.2c-9.6-4-20.8-6.4-32.8-6.4-8.8 0-17.6 0.8-26.4 3.2-40.8 11.2-66.4 43.2-58.4 72.8 6.4 23.2 30.4 37.6 60.8 37.6 8.8 0 17.6-1.6 26.4-3.2 36.8-10.4 60.8-36.8 60-63.2 0.8-1.6 0.8-3.2 0.8-5.6V570.4l220-61.6v136c-9.6-4-20.8-6.4-32.8-6.4-8.8 0-17.6 0.8-26.4 3.2-40.8 11.2-66.4 43.2-58.4 72.8C528 737.6 552 752 582.4 752c8.8 0 17.6-0.8 26.4-3.2 36-9.6 60-36 60-62.4 0.8-1.6 0.8-3.2 0.8-5.6V491.2z m-250.4 48v-53.6l220-61.6v53.6l-220 61.6z" fill="#FFFFFF" p-id="3890"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1543321997742" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="3887" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M160 32c-12 0-24.8 4.8-33.6 14.4S112 68 112 80v864c0 12 4.8 24.8 14.4 33.6C136 987.2 148 992 160 992h704c12 0 24.8-4.8 33.6-14.4C907.2 968 912 956 912 944V304L640 32H160z"
fill="#FF5562" p-id="3888"></path>
<path d="M912 304H688c-12 0-24.8-4.8-33.6-14.4-9.6-8.8-14.4-21.6-14.4-33.6V32l272 272z" fill="#FFBBC0"
p-id="3889"></path>
<path d="M669.6 491.2c0-1.6 0.8-4 0-6.4V369.6c0-4.8-2.4-8.8-5.6-12-4-3.2-8-4-12.8-3.2l-250.4 70.4c-7.2 1.6-12 7.2-12 14.4v275.2c-9.6-4-20.8-6.4-32.8-6.4-8.8 0-17.6 0.8-26.4 3.2-40.8 11.2-66.4 43.2-58.4 72.8 6.4 23.2 30.4 37.6 60.8 37.6 8.8 0 17.6-1.6 26.4-3.2 36.8-10.4 60.8-36.8 60-63.2 0.8-1.6 0.8-3.2 0.8-5.6V570.4l220-61.6v136c-9.6-4-20.8-6.4-32.8-6.4-8.8 0-17.6 0.8-26.4 3.2-40.8 11.2-66.4 43.2-58.4 72.8C528 737.6 552 752 582.4 752c8.8 0 17.6-0.8 26.4-3.2 36-9.6 60-36 60-62.4 0.8-1.6 0.8-3.2 0.8-5.6V491.2z m-250.4 48v-53.6l220-61.6v53.6l-220 61.6z"
fill="#FFFFFF" p-id="3890"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

+12 -1
View File
@@ -1 +1,12 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543300132402" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2107" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M533.947184 38.36489h65.880106v88.119185c117.859409 0.660894 235.60867-1.211639 353.35793 0.660894 25.102953-2.533427 44.81962 16.962943 42.506492 42.07691 1.872533 216.993492-0.440596 434.097133 1.112505 651.090626-1.112505 22.250094 2.191965 46.923466-10.695466 66.640133-16.081751 11.455494-36.899909 10.023557-55.625236 10.904749-110.25913-0.550745-220.408111-0.330447-330.656225-0.330447v88.119185h-68.843113c-167.657764-30.621417-335.745108-58.709407-503.722304-88.119185-0.110149-256.977572 0-513.955144 0-770.822567 168.858388-29.519927 337.826924-58.268811 506.685311-88.339483z" fill="#2A5699" p-id="2108"></path><path d="M599.816275 159.528769h363.491637v704.953477h-363.491637v-88.119185h286.38735v-44.059592h-286.38735v-55.07449h286.38735v-44.059593h-286.38735v-55.07449h286.38735v-44.059592h-286.38735v-55.074491h286.38735v-44.059592h-286.38735v-55.074491h286.38735v-44.059592h-286.38735v-55.07449h286.38735v-44.059593h-286.38735v-77.104286zM254.829667 354.162018c20.928306-1.211639 41.856613-2.092831 62.784919-3.19432 14.649814 74.350562 29.619061 148.590975 45.491529 222.61109 12.446835-76.443393 26.215457-152.666487 39.543484-228.999731 22.029796-0.771043 44.059592-1.982682 65.97924-3.30447-24.89367 106.734362-46.703168 214.349917-73.909966 320.423385-18.39488 9.582961-45.932125-0.440596-67.741623 1.10149-14.649814-72.90761-31.722906-145.38564-44.819621-218.645727-12.898446 71.156242-29.641091 141.651589-44.390039 212.367235a4187.754101 4187.754101 0 0 1-63.666111-3.866229c-18.284731-96.931103-39.763782-193.190297-56.836874-290.341698 18.835476-0.881192 37.792115-1.652235 56.627591-2.313129 11.33433 70.164901 24.221761 139.999355 34.146184 210.274404 15.508976-72.037433 31.37043-144.074867 46.791287-216.1123z" fill="#FFFFFF" p-id="2109"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1543300132402" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="2107" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M533.947184 38.36489h65.880106v88.119185c117.859409 0.660894 235.60867-1.211639 353.35793 0.660894 25.102953-2.533427 44.81962 16.962943 42.506492 42.07691 1.872533 216.993492-0.440596 434.097133 1.112505 651.090626-1.112505 22.250094 2.191965 46.923466-10.695466 66.640133-16.081751 11.455494-36.899909 10.023557-55.625236 10.904749-110.25913-0.550745-220.408111-0.330447-330.656225-0.330447v88.119185h-68.843113c-167.657764-30.621417-335.745108-58.709407-503.722304-88.119185-0.110149-256.977572 0-513.955144 0-770.822567 168.858388-29.519927 337.826924-58.268811 506.685311-88.339483z"
fill="#2A5699" p-id="2108"></path>
<path d="M599.816275 159.528769h363.491637v704.953477h-363.491637v-88.119185h286.38735v-44.059592h-286.38735v-55.07449h286.38735v-44.059593h-286.38735v-55.07449h286.38735v-44.059592h-286.38735v-55.074491h286.38735v-44.059592h-286.38735v-55.074491h286.38735v-44.059592h-286.38735v-55.07449h286.38735v-44.059593h-286.38735v-77.104286zM254.829667 354.162018c20.928306-1.211639 41.856613-2.092831 62.784919-3.19432 14.649814 74.350562 29.619061 148.590975 45.491529 222.61109 12.446835-76.443393 26.215457-152.666487 39.543484-228.999731 22.029796-0.771043 44.059592-1.982682 65.97924-3.30447-24.89367 106.734362-46.703168 214.349917-73.909966 320.423385-18.39488 9.582961-45.932125-0.440596-67.741623 1.10149-14.649814-72.90761-31.722906-145.38564-44.819621-218.645727-12.898446 71.156242-29.641091 141.651589-44.390039 212.367235a4187.754101 4187.754101 0 0 1-63.666111-3.866229c-18.284731-96.931103-39.763782-193.190297-56.836874-290.341698 18.835476-0.881192 37.792115-1.652235 56.627591-2.313129 11.33433 70.164901 24.221761 139.999355 34.146184 210.274404 15.508976-72.037433 31.37043-144.074867 46.791287-216.1123z"
fill="#FFFFFF" p-id="2109"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

+19 -1
View File
@@ -1 +1,19 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189511483" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6709" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M676.297143 0H145.609143C130.834286 0 118.857143 11.977143 118.857143 35.218286V1005.714286c0 6.308571 11.977143 18.285714 26.752 18.285714h732.781714c14.774857 0 26.752-11.977143 26.752-18.285714V237.312c0-12.726857-1.700571-16.822857-4.699428-19.84L687.670857 4.699429A16.164571 16.164571 0 0 0 676.297143 0z" fill="#E9E9E0" p-id="6710"></path><path d="M685.714286 2.761143V219.428571h216.667428z" fill="#D9D7CA" p-id="6711"></path><path d="M878.390857 1024H145.609143A26.752 26.752 0 0 1 118.857143 997.248V713.142857h786.285714v284.105143c0 14.774857-11.977143 26.752-26.752 26.752z" fill="#C8BDB8" p-id="6712"></path><path d="M338.285714 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z" fill="#FFFFFF" p-id="6713"></path><path d="M521.142857 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z" fill="#FFFFFF" p-id="6714"></path><path d="M704 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z" fill="#FFFFFF" p-id="6715"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1514189511483" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="6709" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M676.297143 0H145.609143C130.834286 0 118.857143 11.977143 118.857143 35.218286V1005.714286c0 6.308571 11.977143 18.285714 26.752 18.285714h732.781714c14.774857 0 26.752-11.977143 26.752-18.285714V237.312c0-12.726857-1.700571-16.822857-4.699428-19.84L687.670857 4.699429A16.164571 16.164571 0 0 0 676.297143 0z"
fill="#E9E9E0" p-id="6710"></path>
<path d="M685.714286 2.761143V219.428571h216.667428z" fill="#D9D7CA" p-id="6711"></path>
<path d="M878.390857 1024H145.609143A26.752 26.752 0 0 1 118.857143 997.248V713.142857h786.285714v284.105143c0 14.774857-11.977143 26.752-26.752 26.752z"
fill="#C8BDB8" p-id="6712"></path>
<path d="M338.285714 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z"
fill="#FFFFFF" p-id="6713"></path>
<path d="M521.142857 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z"
fill="#FFFFFF" p-id="6714"></path>
<path d="M704 859.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z"
fill="#FFFFFF" p-id="6715"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

+10 -1
View File
@@ -1 +1,10 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189634329" class="icon" style="" viewBox="0 0 1219 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10648" xmlns:xlink="http://www.w3.org/1999/xlink" width="238.0859375" height="200"><defs><style type="text/css"></style></defs><path d="M1197.787429 327.192381a19.504762 19.504762 0 0 1 19.553523 19.407238v614.156191c0 34.913524-28.574476 63.24419-63.878095 63.24419H63.878095C28.623238 1024 0 995.718095 0 960.75581V346.599619a19.504762 19.504762 0 0 1 19.602286-19.358476h1178.185143z m-66.80381-213.089524C1179.550476 114.102857 1219.047619 153.209905 1219.047619 201.386667v80.213333H1.657905V88.259048C1.657905 39.497143 41.593905 0 90.794667 0h290.864762c17.408 0 33.987048 7.655619 45.104761 20.967619l78.214096 93.135238h626.005333z" fill="#23B7E5" p-id="10649"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1514189634329" class="icon" style="" viewBox="0 0 1219 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="10648" width="238.0859375" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M1197.787429 327.192381a19.504762 19.504762 0 0 1 19.553523 19.407238v614.156191c0 34.913524-28.574476 63.24419-63.878095 63.24419H63.878095C28.623238 1024 0 995.718095 0 960.75581V346.599619a19.504762 19.504762 0 0 1 19.602286-19.358476h1178.185143z m-66.80381-213.089524C1179.550476 114.102857 1219.047619 153.209905 1219.047619 201.386667v80.213333H1.657905V88.259048C1.657905 39.497143 41.593905 0 90.794667 0h290.864762c17.408 0 33.987048 7.655619 45.104761 20.967619l78.214096 93.135238h626.005333z"
fill="#23B7E5" p-id="10649"></path>
</svg>

Before

Width:  |  Height:  |  Size: 943 B

After

Width:  |  Height:  |  Size: 949 B

+16 -1
View File
@@ -1 +1,16 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514190539077" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14917" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M128 0h597.3l227.6 227.9v739.2c0 31.4-25.5 56.9-56.9 56.9H128c-31.4 0-56.9-25.5-56.9-56.9V56.9C71.1 25.5 96.6 0 128 0z" fill="#13BE93" p-id="14918"></path><path d="M630.5 1024H128c-25.7 0-47.4-17-54.4-40.4l218.1-218.1c22.2-22.2 58.2-22.2 80.5 0L630.5 1024z" fill="#6BD8BD" p-id="14919"></path><path d="M952.9 858v109.1c0 31.4-25.5 56.9-56.9 56.9H322.4l-4.7-4.7 358-358c22.2-22.2 58.2-22.2 80.5 0L952.9 858z" fill="#9CF9E2" p-id="14920"></path><path d="M725.3 0l227.6 227.6H782.2c-31.4 0-56.9-25.5-56.9-56.9V0z" fill="#69DCC0" p-id="14921"></path><path d="M327.1 483.6m-85.3 0a85.3 85.3 0 1 0 170.6 0 85.3 85.3 0 1 0-170.6 0Z" fill="#E9FFFA" p-id="14922"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1514190539077" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="14917" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M128 0h597.3l227.6 227.9v739.2c0 31.4-25.5 56.9-56.9 56.9H128c-31.4 0-56.9-25.5-56.9-56.9V56.9C71.1 25.5 96.6 0 128 0z"
fill="#13BE93" p-id="14918"></path>
<path d="M630.5 1024H128c-25.7 0-47.4-17-54.4-40.4l218.1-218.1c22.2-22.2 58.2-22.2 80.5 0L630.5 1024z"
fill="#6BD8BD" p-id="14919"></path>
<path d="M952.9 858v109.1c0 31.4-25.5 56.9-56.9 56.9H322.4l-4.7-4.7 358-358c22.2-22.2 58.2-22.2 80.5 0L952.9 858z"
fill="#9CF9E2" p-id="14920"></path>
<path d="M725.3 0l227.6 227.6H782.2c-31.4 0-56.9-25.5-56.9-56.9V0z" fill="#69DCC0" p-id="14921"></path>
<path d="M327.1 483.6m-85.3 0a85.3 85.3 0 1 0 170.6 0 85.3 85.3 0 1 0-170.6 0Z" fill="#E9FFFA" p-id="14922"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+13 -1
View File
@@ -1 +1,13 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514284130163" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1551" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M915.114667 1024H108.885333V0h587.776v218.112H914.773333V1024z m-785.066667-20.821333h763.904V238.933333H675.84V20.821333H129.706667v982.357334z" fill="#F90E09" p-id="1552"></path><path d="M708.266667 10.581333L904.533333 206.848h-196.266666z" fill="#F90E09" p-id="1553"></path><path d="M282.965333 792.576c33.450667-23.210667 55.637333-57.344 76.8-91.136-30.378667 25.941333-59.050667 54.954667-76.8 91.136z m396.629334-169.642667c31.402667 15.018667 65.877333 31.061333 101.376 21.504-24.576-29.696-67.584-21.504-101.376-21.504z m-246.101334 23.210667c45.397333-16.725333 91.818667-29.696 138.922667-39.594667-30.378667-26.965333-55.978667-58.368-76.458667-93.525333-16.725333 46.08-38.912 90.112-62.464 133.12z m67.925334-243.370667c14.677333-37.546667 19.114667-83.285333-5.802667-117.418666-7.168 38.912-4.096 79.189333 5.802667 117.418666z m-45.397334-105.130666c4.096-24.917333 35.84-29.696 55.637334-22.186667 12.288 7.850667 18.432 22.869333 18.773333 37.205333 1.365333 44.032-6.485333 87.381333-12.629333 130.730667-3.413333 20.138667 8.874667 38.229333 17.749333 55.296 21.845333 40.277333 52.224 76.458667 92.16 99.669333 48.128-5.802667 98.645333-12.970667 145.749333 2.389334 35.157333 11.946667 26.965333 77.141333-11.264 77.824-53.589333 2.730667-102.058667-23.893333-150.186666-43.349334-66.218667 4.778667-130.730667 24.917333-194.218667 44.032-28.672 46.762667-54.613333 98.986667-101.034667 130.730667-23.210667 18.090667-76.458667 3.072-64.512-32.085333 20.138667-49.152 70.314667-76.117333 114.346667-100.693334 12.629333-6.485333 19.797333-18.773333 26.282667-31.061333 30.037333-57.685333 60.074667-115.370667 84.309333-175.786667-12.629333-56.661333-31.061333-114.688-21.162667-172.714666z" fill="#F90E09" p-id="1554"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1514284130163" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="1551" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M915.114667 1024H108.885333V0h587.776v218.112H914.773333V1024z m-785.066667-20.821333h763.904V238.933333H675.84V20.821333H129.706667v982.357334z"
fill="#F90E09" p-id="1552"></path>
<path d="M708.266667 10.581333L904.533333 206.848h-196.266666z" fill="#F90E09" p-id="1553"></path>
<path d="M282.965333 792.576c33.450667-23.210667 55.637333-57.344 76.8-91.136-30.378667 25.941333-59.050667 54.954667-76.8 91.136z m396.629334-169.642667c31.402667 15.018667 65.877333 31.061333 101.376 21.504-24.576-29.696-67.584-21.504-101.376-21.504z m-246.101334 23.210667c45.397333-16.725333 91.818667-29.696 138.922667-39.594667-30.378667-26.965333-55.978667-58.368-76.458667-93.525333-16.725333 46.08-38.912 90.112-62.464 133.12z m67.925334-243.370667c14.677333-37.546667 19.114667-83.285333-5.802667-117.418666-7.168 38.912-4.096 79.189333 5.802667 117.418666z m-45.397334-105.130666c4.096-24.917333 35.84-29.696 55.637334-22.186667 12.288 7.850667 18.432 22.869333 18.773333 37.205333 1.365333 44.032-6.485333 87.381333-12.629333 130.730667-3.413333 20.138667 8.874667 38.229333 17.749333 55.296 21.845333 40.277333 52.224 76.458667 92.16 99.669333 48.128-5.802667 98.645333-12.970667 145.749333 2.389334 35.157333 11.946667 26.965333 77.141333-11.264 77.824-53.589333 2.730667-102.058667-23.893333-150.186666-43.349334-66.218667 4.778667-130.730667 24.917333-194.218667 44.032-28.672 46.762667-54.613333 98.986667-101.034667 130.730667-23.210667 18.090667-76.458667 3.072-64.512-32.085333 20.138667-49.152 70.314667-76.117333 114.346667-100.693334 12.629333-6.485333 19.797333-18.773333 26.282667-31.061333 30.037333-57.685333 60.074667-115.370667 84.309333-175.786667-12.629333-56.661333-31.061333-114.688-21.162667-172.714666z"
fill="#F90E09" p-id="1554"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

+18 -1
View File
@@ -1 +1,18 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543300178225" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5733" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M537.372818 38.36489h62.454472v99.134083c117.738246 0.660894 235.60867-1.211639 353.236766 0.550745 25.334266-2.423278 45.172097 17.29339 42.627656 42.627655 1.872533 205.758296-0.440596 411.626741 1.10149 617.506201-1.10149 22.250094 2.20298 46.923466-10.574302 66.640134-16.081751 11.664777-36.999043 10.133706-55.834519 11.014898-110.148981-0.550745-220.297962-0.330447-330.557091-0.330447v110.148981h-68.523681c-167.856032-30.621417-336.06454-58.599258-504.041736-88.119185-0.110149-256.977572 0-513.955144 0-770.822567 169.970892-29.420793 339.93077-59.381316 510.110945-88.350498z" fill="#D24625" p-id="5734"></path><path d="M599.82729 170.543667h363.491636v671.908783h-363.491636v-88.119185h264.357554v-44.059592h-264.357554v-55.074491h264.357554v-44.059592h-264.247405c-0.110149-21.5892-0.110149-43.1784-0.231313-64.767601 43.630011 13.53731 93.307202 13.217878 132.299941-13.008594 42.18706-25.003819 64.216856-72.698327 67.851772-120.271672-48.366417-0.330447-96.710805-0.220298-144.956059-0.220298-0.121164-47.914807 0.550745-95.829613-0.991341-143.634271-18.075448 3.524767-36.128866 7.269833-54.083149 11.125047v-109.818534z" fill="#FFFFFF" p-id="5735"></path><path d="M677.041725 246.216017c76.553542 3.524767 140.880546 67.961921 144.956059 144.295165-48.366417 0.550745-96.710805 0.330447-145.066208 0.330447 0-48.245254-0.121164-96.490507 0.110149-144.625612z" fill="#D24625" p-id="5736"></path><path d="M203.940838 329.378497c59.810897 2.863874 132.288926-23.682031 181.855967 21.148605 47.485226 57.607917 34.917227 163.571236-35.467972 198.047867-25.003819 12.777282-53.642554 11.014898-80.849351 10.133706-0.110149 43.068251-0.220298 86.136503-0.110149 129.19374-21.820513-1.872533-43.74016-3.745065-65.538644-5.507449-0.980326-117.628097-1.211639-235.377357 0.110149-353.016469z" fill="#FFFFFF" p-id="5737"></path><path d="M269.380347 388.969096c21.699349-0.991341 48.575701-4.956704 63.214501 15.420857 12.567999 21.5892 11.89609 50.007637 1.442951 72.257732-12.567999 22.69069-40.986436 20.48771-62.895068 23.131286-2.313129-36.899909-2.092831-73.799817-1.762384-110.809875z" fill="#D24625" p-id="5738"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1543300178225" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="5733" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M537.372818 38.36489h62.454472v99.134083c117.738246 0.660894 235.60867-1.211639 353.236766 0.550745 25.334266-2.423278 45.172097 17.29339 42.627656 42.627655 1.872533 205.758296-0.440596 411.626741 1.10149 617.506201-1.10149 22.250094 2.20298 46.923466-10.574302 66.640134-16.081751 11.664777-36.999043 10.133706-55.834519 11.014898-110.148981-0.550745-220.297962-0.330447-330.557091-0.330447v110.148981h-68.523681c-167.856032-30.621417-336.06454-58.599258-504.041736-88.119185-0.110149-256.977572 0-513.955144 0-770.822567 169.970892-29.420793 339.93077-59.381316 510.110945-88.350498z"
fill="#D24625" p-id="5734"></path>
<path d="M599.82729 170.543667h363.491636v671.908783h-363.491636v-88.119185h264.357554v-44.059592h-264.357554v-55.074491h264.357554v-44.059592h-264.247405c-0.110149-21.5892-0.110149-43.1784-0.231313-64.767601 43.630011 13.53731 93.307202 13.217878 132.299941-13.008594 42.18706-25.003819 64.216856-72.698327 67.851772-120.271672-48.366417-0.330447-96.710805-0.220298-144.956059-0.220298-0.121164-47.914807 0.550745-95.829613-0.991341-143.634271-18.075448 3.524767-36.128866 7.269833-54.083149 11.125047v-109.818534z"
fill="#FFFFFF" p-id="5735"></path>
<path d="M677.041725 246.216017c76.553542 3.524767 140.880546 67.961921 144.956059 144.295165-48.366417 0.550745-96.710805 0.330447-145.066208 0.330447 0-48.245254-0.121164-96.490507 0.110149-144.625612z"
fill="#D24625" p-id="5736"></path>
<path d="M203.940838 329.378497c59.810897 2.863874 132.288926-23.682031 181.855967 21.148605 47.485226 57.607917 34.917227 163.571236-35.467972 198.047867-25.003819 12.777282-53.642554 11.014898-80.849351 10.133706-0.110149 43.068251-0.220298 86.136503-0.110149 129.19374-21.820513-1.872533-43.74016-3.745065-65.538644-5.507449-0.980326-117.628097-1.211639-235.377357 0.110149-353.016469z"
fill="#FFFFFF" p-id="5737"></path>
<path d="M269.380347 388.969096c21.699349-0.991341 48.575701-4.956704 63.214501 15.420857 12.567999 21.5892 11.89609 50.007637 1.442951 72.257732-12.567999 22.69069-40.986436 20.48771-62.895068 23.131286-2.313129-36.899909-2.092831-73.799817-1.762384-110.809875z"
fill="#D24625" p-id="5738"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

+16 -1
View File
@@ -1 +1,16 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1545064197737" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14227" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M759.725253 0H134.464646v1024h817.131314V191.870707z" fill="#02007A" p-id="14228"></path><path d="M72.40404 708.525253h687.838384v258.585858H72.40404z" fill="#230091" p-id="14229"></path><path d="M82.747475 718.868687h667.151515v237.89899H82.747475zM755.070707 5.171717l191.353535 191.353536h-191.353535z" fill="#FFFFFF" p-id="14230"></path><path d="M506.828283 791.272727v93.090909h25.858586c28.444444 0 41.373737-12.929293 41.373737-45.511111s-12.929293-47.579798-41.373737-47.579798h-25.858586z m-25.858586-25.858586h51.717172c42.408081 0 67.232323 29.99596 67.232323 74.989899S575.612121 910.222222 532.686869 910.222222H480.969697v-144.808081z m-132.39596 104.468687c1.034343 26.375758 22.755556 42.408081 55.337374 42.408081 34.650505 0 55.854545-17.066667 55.854546-43.959596 0-21.20404-12.412121-33.09899-40.856566-39.822222l-16.549495-3.620202c-17.583838-4.137374-24.824242-9.826263-24.824242-19.135354 0-11.894949 10.860606-19.652525 26.892929-19.652525s27.410101 7.757576 28.444444 21.204041h24.307071c-0.517172-24.824242-21.20404-41.890909-52.751515-41.89091S351.676768 782.480808 351.676768 807.822222c0 20.169697 12.412121 33.09899 38.787878 38.787879l18.618182 4.137374c18.10101 4.137374 25.341414 10.343434 25.341414 20.169697 0 11.894949-11.894949 20.169697-28.961616 20.169697s-30.513131-8.791919-32.064646-21.721212l-24.824243 0.517171zM258.585859 788.686869v53.785858h20.686868c18.10101 0 31.030303-9.826263 31.030303-26.892929s-12.412121-26.892929-31.030303-26.892929h-20.686868z m-25.858586-23.272728h56.888889c28.444444 0 46.545455 20.686869 46.545454 49.131314s-17.583838 49.131313-46.545454 49.131313h-31.030303v46.545454h-25.858586v-144.808081z" fill="#230091" p-id="14231"></path><path d="M415.806061 443.216162h-31.030303v137.567676H321.163636V196.525253h93.090909c91.022222 0 140.670707 35.684848 140.670708 122.052525 0.517172 103.434343-92.573737 124.638384-139.119192 124.638384z m-5.688889-192.905051c-6.206061 0-14.99798 0.517172-26.89293 1.551515V387.878788h36.202021C461.834343 387.878788 491.313131 366.157576 491.313131 312.888889c0-40.856566-32.064646-62.577778-81.195959-62.577778z m258.068687 337.19596c70.852525 0 96.711111-50.165657 96.711111-84.816162 0-54.820202-35.684848-67.749495-66.19798-87.40202-25.858586-17.066667-49.648485-23.272727-49.648485-40.856566 0-22.755556 15.515152-27.410101 36.20202-27.410101 25.858586 0 54.820202 2.068687 67.232323 16.032323V306.682828c-12.929293-10.343434-37.236364-13.446465-68.783838-13.446464-67.749495 0-93.608081 44.993939-93.608081 84.29899 0 50.682828 33.616162 65.680808 66.19798 83.781818 21.20404 11.894949 46.545455 20.686869 46.545455 44.476767 0 13.963636-8.791919 27.927273-37.753536 27.927273-26.892929 0-59.991919-10.343434-74.989899-23.789899v59.991919c13.446465 10.343434 40.339394 17.583838 78.09293 17.583839z" fill="#7AB7FF" p-id="14232"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1545064197737" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="14227" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M759.725253 0H134.464646v1024h817.131314V191.870707z" fill="#02007A" p-id="14228"></path>
<path d="M72.40404 708.525253h687.838384v258.585858H72.40404z" fill="#230091" p-id="14229"></path>
<path d="M82.747475 718.868687h667.151515v237.89899H82.747475zM755.070707 5.171717l191.353535 191.353536h-191.353535z"
fill="#FFFFFF" p-id="14230"></path>
<path d="M506.828283 791.272727v93.090909h25.858586c28.444444 0 41.373737-12.929293 41.373737-45.511111s-12.929293-47.579798-41.373737-47.579798h-25.858586z m-25.858586-25.858586h51.717172c42.408081 0 67.232323 29.99596 67.232323 74.989899S575.612121 910.222222 532.686869 910.222222H480.969697v-144.808081z m-132.39596 104.468687c1.034343 26.375758 22.755556 42.408081 55.337374 42.408081 34.650505 0 55.854545-17.066667 55.854546-43.959596 0-21.20404-12.412121-33.09899-40.856566-39.822222l-16.549495-3.620202c-17.583838-4.137374-24.824242-9.826263-24.824242-19.135354 0-11.894949 10.860606-19.652525 26.892929-19.652525s27.410101 7.757576 28.444444 21.204041h24.307071c-0.517172-24.824242-21.20404-41.890909-52.751515-41.89091S351.676768 782.480808 351.676768 807.822222c0 20.169697 12.412121 33.09899 38.787878 38.787879l18.618182 4.137374c18.10101 4.137374 25.341414 10.343434 25.341414 20.169697 0 11.894949-11.894949 20.169697-28.961616 20.169697s-30.513131-8.791919-32.064646-21.721212l-24.824243 0.517171zM258.585859 788.686869v53.785858h20.686868c18.10101 0 31.030303-9.826263 31.030303-26.892929s-12.412121-26.892929-31.030303-26.892929h-20.686868z m-25.858586-23.272728h56.888889c28.444444 0 46.545455 20.686869 46.545454 49.131314s-17.583838 49.131313-46.545454 49.131313h-31.030303v46.545454h-25.858586v-144.808081z"
fill="#230091" p-id="14231"></path>
<path d="M415.806061 443.216162h-31.030303v137.567676H321.163636V196.525253h93.090909c91.022222 0 140.670707 35.684848 140.670708 122.052525 0.517172 103.434343-92.573737 124.638384-139.119192 124.638384z m-5.688889-192.905051c-6.206061 0-14.99798 0.517172-26.89293 1.551515V387.878788h36.202021C461.834343 387.878788 491.313131 366.157576 491.313131 312.888889c0-40.856566-32.064646-62.577778-81.195959-62.577778z m258.068687 337.19596c70.852525 0 96.711111-50.165657 96.711111-84.816162 0-54.820202-35.684848-67.749495-66.19798-87.40202-25.858586-17.066667-49.648485-23.272727-49.648485-40.856566 0-22.755556 15.515152-27.410101 36.20202-27.410101 25.858586 0 54.820202 2.068687 67.232323 16.032323V306.682828c-12.929293-10.343434-37.236364-13.446465-68.783838-13.446464-67.749495 0-93.608081 44.993939-93.608081 84.29899 0 50.682828 33.616162 65.680808 66.19798 83.781818 21.20404 11.894949 46.545455 20.686869 46.545455 44.476767 0 13.963636-8.791919 27.927273-37.753536 27.927273-26.892929 0-59.991919-10.343434-74.989899-23.789899v59.991919c13.446465 10.343434 40.339394 17.583838 78.09293 17.583839z"
fill="#7AB7FF" p-id="14232"></path>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

+10 -1
View File
@@ -1 +1,10 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189544355" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8186" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M176 1024l672 0c26.464 0 48-21.536 48-48l0-672c0-0.544-0.256-0.992-0.288-1.504-0.064-0.736-0.256-1.376-0.416-2.08-0.544-2.272-1.472-4.32-2.88-6.112-0.224-0.288-0.192-0.64-0.416-0.928l-256-288c-0.096-0.096-0.224-0.096-0.32-0.192-1.92-2.048-4.352-3.456-7.136-4.288-0.608-0.192-1.152-0.256-1.792-0.352-0.928-0.16-1.792-0.544-2.752-0.544l-448 0c-26.464 0-48 21.536-48 48l0 928c0 26.464 21.536 48 48 48zM640 58.08l204.384 229.92-188.384 0c-7.04 0-16-13.44-16-24l0-205.92zM160 48c0-8.832 7.2-16 16-16l432 0 0 232c0 25.888 20.96 56 48 56l208 0 0 656c0 8.832-7.2 16-16 16l-672 0c-8.96 0-16-7.04-16-16l0-928zM304 448l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 576l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 320l192 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-192 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 704l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 832l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16z" p-id="8187"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1514189544355" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="8186" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M176 1024l672 0c26.464 0 48-21.536 48-48l0-672c0-0.544-0.256-0.992-0.288-1.504-0.064-0.736-0.256-1.376-0.416-2.08-0.544-2.272-1.472-4.32-2.88-6.112-0.224-0.288-0.192-0.64-0.416-0.928l-256-288c-0.096-0.096-0.224-0.096-0.32-0.192-1.92-2.048-4.352-3.456-7.136-4.288-0.608-0.192-1.152-0.256-1.792-0.352-0.928-0.16-1.792-0.544-2.752-0.544l-448 0c-26.464 0-48 21.536-48 48l0 928c0 26.464 21.536 48 48 48zM640 58.08l204.384 229.92-188.384 0c-7.04 0-16-13.44-16-24l0-205.92zM160 48c0-8.832 7.2-16 16-16l432 0 0 232c0 25.888 20.96 56 48 56l208 0 0 656c0 8.832-7.2 16-16 16l-672 0c-8.96 0-16-7.04-16-16l0-928zM304 448l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 576l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 320l192 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-192 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 704l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16zM304 832l416 0c8.832 0 16-7.168 16-16s-7.168-16-16-16l-416 0c-8.832 0-16 7.168-16 16s7.168 16 16 16z"
p-id="8187"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

+23 -1
View File
@@ -1 +1,23 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1514189554639" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8871" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M80 34.4h864v960H80z" fill="#8095FF" p-id="8872"></path><path d="M176 112m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8873"></path><path d="M176 272m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8874"></path><path d="M176 432m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8875"></path><path d="M176 592m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8876"></path><path d="M176 752m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8877"></path><path d="M176 912m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8878"></path><path d="M864 112m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8879"></path><path d="M864 272m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8880"></path><path d="M864 432m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8881"></path><path d="M864 592m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8882"></path><path d="M864 752m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8883"></path><path d="M864 912m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8884"></path><path d="M648 508L436 362.4c-4.8-3.2-11.2-4-16.8-0.8-5.6 3.2-8.8 8.8-8.8 14.4v290.4c0 5.6 3.2 11.2 8.8 14.4 5.6 3.2 12 2.4 16.8-0.8L648 533.6c4.8-2.4 7.2-8 7.2-12.8 0-4.8-3.2-9.6-7.2-12.8z" fill="#FFFFFF" p-id="8885"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1514189554639" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="8871" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M80 34.4h864v960H80z" fill="#8095FF" p-id="8872"></path>
<path d="M176 112m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8873"></path>
<path d="M176 272m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8874"></path>
<path d="M176 432m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8875"></path>
<path d="M176 592m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8876"></path>
<path d="M176 752m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8877"></path>
<path d="M176 912m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8878"></path>
<path d="M864 112m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8879"></path>
<path d="M864 272m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8880"></path>
<path d="M864 432m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8881"></path>
<path d="M864 592m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8882"></path>
<path d="M864 752m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8883"></path>
<path d="M864 912m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#FFFFFF" p-id="8884"></path>
<path d="M648 508L436 362.4c-4.8-3.2-11.2-4-16.8-0.8-5.6 3.2-8.8 8.8-8.8 14.4v290.4c0 5.6 3.2 11.2 8.8 14.4 5.6 3.2 12 2.4 16.8-0.8L648 533.6c4.8-2.4 7.2-8 7.2-12.8 0-4.8-3.2-9.6-7.2-12.8z"
fill="#FFFFFF" p-id="8885"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

+17 -1
View File
@@ -1 +1,17 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543300142282" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2233" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M534.310676 38.36489h65.428495v88.119185c110.25913 0 220.507245 0.220298 330.766374-0.330447 18.615178 0.771043 39.113903-0.550745 55.305803 10.574302 11.345345 16.302049 10.023557 37.010058 10.783586 55.746399-0.550745 191.218631-0.319432 382.327112-0.220298 573.435594-0.550745 32.053353 2.974022 64.767601-3.745066 96.380358-4.405959 22.910988-31.943204 23.461733-50.338084 24.232776-114.114344 0.330447-228.338837-0.220298-342.56333 0v99.134083h-68.402517c-167.867047-30.511268-336.075555-58.709407-504.151885-88.119185v-770.932716c169.0897-29.420793 338.168386-58.389975 507.136922-88.240349z" fill="#207245" p-id="2234"></path><path d="M599.739171 159.528769h363.491636v693.938579h-363.491636v-66.089389h88.119184v-77.104286h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-66.089388z" fill="#FFFFFF" p-id="2235"></path><path d="M731.917948 225.618157h154.208573v77.104287h-154.208573v-77.104287z" fill="#207245" p-id="2236"></path><path d="M353.44605 328.827752c24.89367-1.762384 49.897488-3.304469 74.901307-4.516108-29.409778 60.251492-58.929705 120.502985-88.879213 180.53418 30.29097 61.683429 61.242833 122.926263 91.632937 184.609692-26.545904-1.553101-52.98166-3.19432-79.527564-5.066854-18.725327-45.932125-41.526166-90.322164-54.964342-138.358134-14.969246 44.731501-36.349164 86.918561-53.521389 130.74684-24.122627-0.330447-48.256268-1.321788-72.367881-2.313129 28.308288-55.404937 55.625235-111.250471 84.814716-166.324961-24.783521-56.726725-52.001334-112.35196-77.544883-168.748238 24.232776-1.431937 48.454537-2.863874 72.698327-4.185662 16.412198 43.068251 34.366482 85.585758 47.925822 129.7555 14.506621-46.802302 36.216985-90.861894 54.832163-136.133126z" fill="#FFFFFF" p-id="2237"></path><path d="M731.917948 346.782036h154.208573v77.104287h-154.208573v-77.104287zM731.917948 467.945915h154.208573v77.104287h-154.208573v-77.104287zM731.917948 589.109794h154.208573v77.104287h-154.208573v-77.104287zM731.917948 710.273673h154.208573v77.104286h-154.208573v-77.104286z" fill="#207245" p-id="2238"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1543300142282" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="2233" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M534.310676 38.36489h65.428495v88.119185c110.25913 0 220.507245 0.220298 330.766374-0.330447 18.615178 0.771043 39.113903-0.550745 55.305803 10.574302 11.345345 16.302049 10.023557 37.010058 10.783586 55.746399-0.550745 191.218631-0.319432 382.327112-0.220298 573.435594-0.550745 32.053353 2.974022 64.767601-3.745066 96.380358-4.405959 22.910988-31.943204 23.461733-50.338084 24.232776-114.114344 0.330447-228.338837-0.220298-342.56333 0v99.134083h-68.402517c-167.867047-30.511268-336.075555-58.709407-504.151885-88.119185v-770.932716c169.0897-29.420793 338.168386-58.389975 507.136922-88.240349z"
fill="#207245" p-id="2234"></path>
<path d="M599.739171 159.528769h363.491636v693.938579h-363.491636v-66.089389h88.119184v-77.104286h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-44.059592h88.119184v-77.104287h-88.119184v-66.089388z"
fill="#FFFFFF" p-id="2235"></path>
<path d="M731.917948 225.618157h154.208573v77.104287h-154.208573v-77.104287z" fill="#207245" p-id="2236"></path>
<path d="M353.44605 328.827752c24.89367-1.762384 49.897488-3.304469 74.901307-4.516108-29.409778 60.251492-58.929705 120.502985-88.879213 180.53418 30.29097 61.683429 61.242833 122.926263 91.632937 184.609692-26.545904-1.553101-52.98166-3.19432-79.527564-5.066854-18.725327-45.932125-41.526166-90.322164-54.964342-138.358134-14.969246 44.731501-36.349164 86.918561-53.521389 130.74684-24.122627-0.330447-48.256268-1.321788-72.367881-2.313129 28.308288-55.404937 55.625235-111.250471 84.814716-166.324961-24.783521-56.726725-52.001334-112.35196-77.544883-168.748238 24.232776-1.431937 48.454537-2.863874 72.698327-4.185662 16.412198 43.068251 34.366482 85.585758 47.925822 129.7555 14.506621-46.802302 36.216985-90.861894 54.832163-136.133126z"
fill="#FFFFFF" p-id="2237"></path>
<path d="M731.917948 346.782036h154.208573v77.104287h-154.208573v-77.104287zM731.917948 467.945915h154.208573v77.104287h-154.208573v-77.104287zM731.917948 589.109794h154.208573v77.104287h-154.208573v-77.104287zM731.917948 710.273673h154.208573v77.104286h-154.208573v-77.104286z"
fill="#207245" p-id="2238"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

+11 -1
View File
@@ -1 +1,11 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1544085034574" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1828" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M0 512A512 512 0 1 0 512 0 512 512 0 0 0 0 512" fill="#36D49F" p-id="1829"></path><path d="M450.37037 552.580741l-115.105185-87.798519-50.820741 39.822222L483.365926 701.62963C517.499259 616.296296 625.777778 449.232593 758.518519 330.714074L746.382222 303.407407a1264.071111 1264.071111 0 0 0-296.011852 249.173334" fill="#FFFFFF" p-id="1830"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1544085034574" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="1828" width="200" height="200">
<defs>
<style type="text/css"></style>
</defs>
<path d="M0 512A512 512 0 1 0 512 0 512 512 0 0 0 0 512" fill="#36D49F" p-id="1829"></path>
<path d="M450.37037 552.580741l-115.105185-87.798519-50.820741 39.822222L483.365926 701.62963C517.499259 616.296296 625.777778 449.232593 758.518519 330.714074L746.382222 303.407407a1264.071111 1264.071111 0 0 0-296.011852 249.173334"
fill="#FFFFFF" p-id="1830"></path>
</svg>

Before

Width:  |  Height:  |  Size: 743 B

After

Width:  |  Height:  |  Size: 755 B

+9 -12
View File
@@ -1,31 +1,29 @@
import React from 'react';
/**
* 带有挂载状态的自定义组件
*/
export default class TankComponent<P, S> extends React.Component <P, S> {
export default class TankComponent<P, S> extends React.Component<P, S> {
//是否处于挂载的状态。
mounted: boolean = false
mounted: boolean = false;
componentWillUnmount() {
let that = this
that.mounted = false
let that = this;
that.mounted = false;
}
componentWillMount() {
let that = this
that.mounted = true
let that = this;
that.mounted = true;
}
//更新当前视图。
updateUI() {
let that = this
let that = this;
if (that.mounted) {
that.setState({})
that.setState({});
} else {
console.info(this.constructor.name + "已经脱离挂载,不再刷新视图。")
console.info(this.constructor.name + '已经脱离挂载,不再刷新视图。');
}
}
@@ -34,4 +32,3 @@ export default class TankComponent<P, S> extends React.Component <P, S> {
return this.constructor.name;
}
}
+4
View File
@@ -0,0 +1,4 @@
export const FORM_LAYOUT = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};
+7 -7
View File
@@ -1,5 +1,5 @@
import {ToolItem} from "../types";
import React from "react";
import { ToolItem } from '../types';
import React from 'react';
export default class MenuItem implements ToolItem {
name: string;
@@ -7,18 +7,18 @@ export default class MenuItem implements ToolItem {
icon: React.ReactNode;
active: boolean = false;
constructor(name: string, url: string, icon: React.ReactNode) {
this.name = name;
this.url = url;
this.icon = icon;
//当前的url完全一致,那么就高亮显示
if (url == "/user/login") {
this.active = window.location.pathname === url || window.location.pathname === "/user/register"
if (url == '/user/login') {
this.active =
window.location.pathname === url ||
window.location.pathname === '/user/register';
} else {
this.active = window.location.pathname === url
this.active = window.location.pathname === url;
}
}
}
+64 -29
View File
@@ -1,40 +1,36 @@
/**
* 管理当前所有的菜单
*/
import React from "react";
import React from 'react';
import MenuItem from './MenuItem';
import Moon from '../model/global/Moon';
import User from '../model/user/User';
import {UserRole} from '../model/user/UserRole';
import { UserRole } from '../model/user/UserRole';
import Sun from '../../common/model/global/Sun';
import {
AppstoreOutlined,
CloudSyncOutlined,
DashboardOutlined,
DeleteOutlined,
PoweroffOutlined,
SettingOutlined,
ShareAltOutlined,
TeamOutlined,
DeleteOutlined
} from '@ant-design/icons';
import {LoginOutlined} from "@ant-design/icons/lib";
import Preference from "../model/preference/Preference";
import Lang from "../model/global/Lang";
import { LoginOutlined } from '@ant-design/icons/lib';
import Preference from '../model/preference/Preference';
import Lang from '../model/global/Lang';
export default class MenuManager {
//单例模式
private static singleton: MenuManager;
constructor() {
}
constructor() {}
static getSingleton(): MenuManager {
if (!MenuManager.singleton) {
//初始化一个mainLand.
MenuManager.singleton = new MenuManager();
}
return MenuManager.singleton;
}
@@ -43,11 +39,11 @@ export default class MenuManager {
* 获取到当前高亮的菜单
*/
getSelectedKeys(): string[] {
let keys: string[] = this.getMenuItems()
.filter((menuItem: MenuItem, index: number) => {
return menuItem.active;
}).map((menuItem: MenuItem, index: number) => {
})
.map((menuItem: MenuItem, index: number) => {
return menuItem.url;
});
@@ -58,11 +54,9 @@ export default class MenuManager {
* 高亮某个菜单
*/
selectMenu(url: string) {
this.getMenuItems().forEach((menuItem: MenuItem, index: number) => {
menuItem.active = menuItem.url === url;
});
}
getMenuItems(): MenuItem[] {
@@ -73,29 +67,70 @@ export default class MenuManager {
if (!preference.installed) {
menuItems = [
new MenuItem(Lang.t("layout.install"), '/install/index', <SettingOutlined/>),
new MenuItem(
Lang.t('layout.install'),
'/install/index',
<SettingOutlined />
),
];
} else if (user.role === UserRole.GUEST) {
menuItems = [
new MenuItem(Lang.t("user.login"), '/user/login', <LoginOutlined/>),
new MenuItem(Lang.t('user.login'), '/user/login', <LoginOutlined />),
];
} else {
menuItems.push(new MenuItem(Lang.t("layout.allFiles"), '/matter/list', <AppstoreOutlined/>))
menuItems.push(new MenuItem(Lang.t("layout.myShare"), '/share/list', <ShareAltOutlined/>))
menuItems.push(new MenuItem(Lang.t("layout.bin"), '/bin/list', <DeleteOutlined />))
menuItems.push(
new MenuItem(
Lang.t('layout.allFiles'),
'/matter/list',
<AppstoreOutlined />
)
);
menuItems.push(
new MenuItem(Lang.t('layout.space'), '/space', <CloudSyncOutlined />)
);
menuItems.push(
new MenuItem(
Lang.t('layout.myShare'),
'/share/list',
<ShareAltOutlined />
)
);
menuItems.push(
new MenuItem(Lang.t('layout.bin'), '/bin/list', <DeleteOutlined />)
);
if (user.role === UserRole.ADMINISTRATOR) {
menuItems.push(new MenuItem(Lang.t("layout.setting"), '/preference/index', <SettingOutlined/>))
menuItems.push(new MenuItem(Lang.t("layout.dashboard"), '/dashboard/index', <DashboardOutlined/>))
menuItems.push(new MenuItem(Lang.t("layout.users"), `${Sun.getSingleton().isMobile ? '/mobile': ''}/user/list`, <TeamOutlined/>))
menuItems.push(
new MenuItem(
Lang.t('layout.setting'),
'/preference/index',
<SettingOutlined />
)
);
menuItems.push(
new MenuItem(
Lang.t('layout.dashboard'),
'/dashboard/index',
<DashboardOutlined />
)
);
menuItems.push(
new MenuItem(
Lang.t('layout.users'),
`${Sun.getSingleton().isMobile ? '/mobile' : ''}/user/list`,
<TeamOutlined />
)
);
}
menuItems.push(new MenuItem(Lang.t("layout.logout"), '/user/logout', <PoweroffOutlined/>))
menuItems.push(
new MenuItem(
Lang.t('layout.logout'),
'/user/logout',
<PoweroffOutlined />
)
);
}
return menuItems;
}
}
+39 -64
View File
@@ -1,6 +1,6 @@
import ObjectUtil from "../../util/ObjectUtil";
import JsonUtil from "../../util/JsonUtil";
import DateUtil from "../../util/DateUtil";
import ObjectUtil from '../../util/ObjectUtil';
import JsonUtil from '../../util/JsonUtil';
import DateUtil from '../../util/DateUtil';
/**
*
@@ -8,29 +8,25 @@ import DateUtil from "../../util/DateUtil";
* 在这个类中可以统计建立的实体个数。
*/
export default class Base {
//id自增长值
private static AUTO_INCREMENT_ID = 0
private static AUTO_INCREMENT_ID = 0;
//当前对象的ID,这个字段让我们可以很轻松的统计出共创建了多少个实体类。
autoId: number = 0
autoId: number = 0;
//我们认为每个实体都会存放于某个react组件中,当然可以不传入。
constructor() {
this.autoId = Base.generateAutoId()
this.autoId = Base.generateAutoId();
}
static generateAutoId() {
Base.AUTO_INCREMENT_ID++
return Base.AUTO_INCREMENT_ID
Base.AUTO_INCREMENT_ID++;
return Base.AUTO_INCREMENT_ID;
}
//把obj中的属性,赋值到this中来。采用深拷贝。
assign(obj: any) {
ObjectUtil.extend(this, obj)
ObjectUtil.extend(this, obj);
}
/**
@@ -39,11 +35,9 @@ export default class Base {
* @param Clazz 类型名
*/
assignList<F extends keyof this>(field: string, Clazz: any) {
ObjectUtil.assignList(this, field, Clazz)
ObjectUtil.assignList(this, field, Clazz);
}
/**
* 根据一个类型,渲染出对应的数组。
* @param json 字符串或者数组对象。
@@ -51,99 +45,80 @@ export default class Base {
* @returns {*}
*/
static renderList(json: any, Clazz: any) {
let target: any = [];
let target: any = []
let arr: any = []
if (json instanceof String || typeof json === "string") {
arr = JsonUtil.parseList(json)
let arr: any = [];
if (json instanceof String || typeof json === 'string') {
arr = JsonUtil.parseList(json);
} else if (json instanceof Array) {
arr = json
arr = json;
} else {
console.error("源必须为字符或者数组", json, typeof json)
return target
console.error('源必须为字符或者数组', json, typeof json);
return target;
}
//如果我们要转换成字符串的数组形式,那么this[field]应该是一个字符串才对。
if (Clazz === String) {
return arr
return arr;
}
if (!Clazz || !(Clazz.prototype instanceof Base)) {
console.error("指定的类型必须是 Base的子类 ")
return target
console.error('指定的类型必须是 Base的子类 ');
return target;
}
for (let i = 0; i < arr.length; i++) {
let bean = arr[i]
let bean = arr[i];
let clazz = new Clazz()
let clazz = new Clazz();
clazz.assign(bean)
clazz.assign(bean);
target.push(clazz)
target.push(clazz);
}
return target
return target;
}
//直接render出一个Entity. field字段名,Clazz类名。
assignEntity(field: any, Clazz: any) {
let thisObj: any = this;
let thisObj: any = this
let obj: any = thisObj[field]
let obj: any = thisObj[field];
if (!obj) {
if (Clazz) {
let EntityClazz: any = this.constructor
obj = (new EntityClazz())[field]
let EntityClazz: any = this.constructor;
obj = new EntityClazz()[field];
} else {
return
return;
}
}
if (Clazz === Date) {
thisObj[field] = DateUtil.str2Date(obj)
thisObj[field] = DateUtil.str2Date(obj);
} else if (Clazz.prototype instanceof Base) {
//可能此处的该项属性做了特殊处理的。
//1024*1024 以及 "图片尺寸不超过1M"用let bean = new Clazz(); 就无法反映出来。因为父类assign的时候已经将avatar给变成了Object.
let bean = (new thisObj.constructor())[field]
let bean = new thisObj.constructor()[field];
if (!bean) {
bean = new Clazz()
bean = new Clazz();
}
if (typeof obj === "string") {
if (typeof obj === 'string') {
try {
obj = JSON.parse(obj)
obj = JSON.parse(obj);
} catch (e) {
console.error("JSON parse obj error", e)
console.error('JSON parse obj error', e);
}
}
if (obj !== null) {
bean.assign(obj)
thisObj[field] = bean
bean.assign(obj);
thisObj[field] = bean;
}
} else {
console.error('调用错误!')
console.error('调用错误!');
}
}
}
+44 -59
View File
@@ -4,13 +4,11 @@ import SortFilter from './filter/SortFilter';
import Filter from './filter/Filter';
import HttpBase from './HttpBase';
/**
* 实体基类
* 继承这个类的表示在数据库中有对应的表。
*/
export default class BaseEntity extends HttpBase {
/**
* 唯一标识
*/
@@ -31,17 +29,13 @@ export default class BaseEntity extends HttpBase {
*/
updateTime: Date | null = null;
//************ 前端辅助字段 ****************/
//加载详情的指示
detailLoading: boolean = false;
//我们认为每个实体都会存放于某个react组件中,当然可以不传入。
constructor(reactComponent?: React.Component | null) {
super(reactComponent);
}
//把obj中的属性,赋值到this中来。采用深拷贝。
@@ -50,7 +44,6 @@ export default class BaseEntity extends HttpBase {
this.assignEntity('createTime', Date);
this.assignEntity('updateTime', Date);
}
//获取过滤器,必须每次动态生成,否则会造成filter逻辑混乱。
@@ -59,12 +52,10 @@ export default class BaseEntity extends HttpBase {
new SortFilter('修改时间排序', 'orderCreateTime'),
new SortFilter('创建时间排序', 'orderUpdateTime'),
];
};
}
//提交之前对自己进行验证。返回错误信息,null表示没有错误。
validate(): any {
return null;
}
@@ -75,7 +66,6 @@ export default class BaseEntity extends HttpBase {
//获取到当前类的单数标签。比如 Project便得到 project
getTAG(): string {
let className = this.constructor.name;
//IE无法直接通过this.constructor.name获取到相应名称
@@ -86,13 +76,11 @@ export default class BaseEntity extends HttpBase {
return StringUtil.lowerCamel(className);
}
//获取到当前实体的url前缀。比如Notification获取到 /api/fs/notification
getUrlPrefix(): string {
return '/api' + StringUtil.lowerSlash(this.getTAG());
}
getUrlCreate(): string {
let prefix = this.getUrlPrefix();
@@ -103,7 +91,6 @@ export default class BaseEntity extends HttpBase {
let prefix = this.getUrlPrefix();
return prefix + '/delete';
}
getUrlEdit(): string {
@@ -130,10 +117,8 @@ export default class BaseEntity extends HttpBase {
return prefix + '/sort';
}
//新增或者修改
httpSave(successCallback?: any, errorCallback?: any, finallyCallback?: any) {
let that = this;
let url = this.getUrlCreate();
@@ -141,30 +126,33 @@ export default class BaseEntity extends HttpBase {
url = this.getUrlEdit();
}
this.errorMessage = this.validate();
if (this.errorMessage) {
that.defaultErrorHandler(this.errorMessage, errorCallback);
return;
}
this.httpPost(url, this.getForm(), function(response: any) {
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finallyCallback);
this.httpPost(
url,
this.getForm(),
function (response: any) {
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finallyCallback
);
}
//common http detail methods.
httpDetail(successCallback?: any, errorCallback?: any, finallyCallback?: any) {
httpDetail(
successCallback?: any,
errorCallback?: any,
finallyCallback?: any
) {
let that = this;
if (!this.uuid) {
this.errorMessage = 'uuid未指定,无法获取到详情!';
this.defaultErrorHandler(this.errorMessage, errorCallback);
@@ -176,33 +164,33 @@ export default class BaseEntity extends HttpBase {
this.detailLoading = true;
this.httpGet(url, {}, function(response: any) {
that.detailLoading = false;
this.httpGet(
url,
{},
function (response: any) {
that.detailLoading = false;
that.assign(response.data.data);
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
}, function(response: any) {
that.detailLoading = false;
if (typeof errorCallback === 'function') {
errorCallback(that.getErrorMessage(response), response);
} else {
//没有传入错误处理的方法就采用默认处理方法:toast弹出该错误信息。
that.defaultErrorHandler(response);
}
}, finallyCallback);
SafeUtil.safeCallback(successCallback)(response);
},
function (response: any) {
that.detailLoading = false;
if (typeof errorCallback === 'function') {
errorCallback(that.getErrorMessage(response), response);
} else {
//没有传入错误处理的方法就采用默认处理方法:toast弹出该错误信息。
that.defaultErrorHandler(response);
}
},
finallyCallback
);
}
httpDel(successCallback?: any, errorCallback?: any, finallyCallback?: any) {
let that = this;
if (!this.uuid) {
this.errorMessage = '没有id,无法删除!';
that.defaultErrorHandler(this.errorMessage, errorCallback);
@@ -211,17 +199,14 @@ export default class BaseEntity extends HttpBase {
let url = this.getUrlDel() + '?uuid=' + this.uuid;
this.httpPost(url, {}, function(response: any) {
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finallyCallback);
this.httpPost(
url,
{},
function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finallyCallback
);
}
}
+150 -154
View File
@@ -1,291 +1,287 @@
import HttpUtil from "../../util/HttpUtil";
import SafeUtil from "../../util/SafeUtil";
import qs from "qs"
import React from "react";
import ViewBase from "./ViewBase";
import Sun from "../global/Sun";
import {WebResultCode} from "./WebResultCode";
import MessageBoxUtil from "../../util/MessageBoxUtil";
import HttpUtil from '../../util/HttpUtil';
import SafeUtil from '../../util/SafeUtil';
import qs from 'qs';
import React from 'react';
import ViewBase from './ViewBase';
import Sun from '../global/Sun';
import { WebResultCode } from './WebResultCode';
import MessageBoxUtil from '../../util/MessageBoxUtil';
/**
* 基类。带有网络请求能力的基类
* 继承了该类就表示具有了去服务器请求的能力。
*/
export default class HttpBase extends ViewBase {
static lastLoginErrorTimestamp = 0
static lastLoginErrorTimestamp = 0;
//是否需要自动刷新State
needReactComponentUpdate: boolean = true
needReactComponentUpdate: boolean = true;
//当前是否正在进行http请求
loading: boolean = false
loading: boolean = false;
//请求http的时候是否有错误
errorMessage: string | null = null
errorMessage: string | null = null;
//我们认为每个实体都会存放于某个react组件中,当然可以不传入。
constructor(reactComponent?: React.Component | null) {
super()
super();
if (reactComponent) {
this.reactComponent = reactComponent
this.reactComponent = reactComponent;
}
}
//更新当前的视图,只在需要更新的情况下才更新。
updateUI() {
if (this.needReactComponentUpdate && this.reactComponent) {
ViewBase.updateComponentUI(this.reactComponent, this)
ViewBase.updateComponentUI(this.reactComponent, this);
}
}
/**
* 转跳到登录页面
*/
jumpLogin() {
Sun.navigateTo("/user/login")
Sun.navigateTo('/user/login');
}
/**
* 转跳到网站安装页面
*/
jumpInstall() {
Sun.navigateTo("/install/index")
Sun.navigateTo('/install/index');
}
//从一个返回中获取出其错误信息。适配各种错误的类型。
getErrorMessage(response: any) {
console.error("getErrorMessage", response)
let msg = '服务器出错,请稍后再试!'
console.error('getErrorMessage', response);
let msg = '服务器出错,请稍后再试!';
if (!response) {
msg = '出错啦,请稍后重试!'
msg = '出错啦,请稍后重试!';
} else if (typeof response === 'string') {
msg = response
msg = response;
} else if (response['msg']) {
msg = response['msg']
msg = response['msg'];
} else if (response['message']) {
msg = response['message']
msg = response['message'];
} else {
let temp = response['data']
let temp = response['data'];
if (temp !== null && typeof temp === 'object') {
if (temp['message']) {
msg = temp['message']
msg = temp['message'];
} else if (temp['msg']) {
msg = temp['msg']
msg = temp['msg'];
} else {
if (temp['error'] && temp['error']['message']) {
msg = temp['error']['message']
msg = temp['error']['message'];
}
}
}
}
this.errorMessage = msg
return msg
this.errorMessage = msg;
return msg;
}
//提供全局的默认处理方式,可以自定义错误处理
defaultErrorHandler(response: any, errorCallback?: any) {
let msg = this.getErrorMessage(response);
let msg = this.getErrorMessage(response)
console.error("请求出错了", typeof msg, msg)
console.error('请求出错了', typeof msg, msg);
if (typeof errorCallback === 'function') {
errorCallback(msg, response)
errorCallback(msg, response);
} else {
MessageBoxUtil.error(msg)
MessageBoxUtil.error(msg);
}
}
//专门捕捉没有登录这种错误。return true -> 有错误(已经处理掉了) false -> 没错误 (什么都没干)
specialErrorHandler(response: any) {
if (!response || !response.data) {
return false
return false;
}
//1.判断是不是登录错误
if (response.data["code"] === WebResultCode.LOGIN) {
if (response.data['code'] === WebResultCode.LOGIN) {
//这个问题不能报的太频繁,比如一个页面请求了两个接口,两个接口都报没有登录。
if ((new Date().getTime()) - HttpBase.lastLoginErrorTimestamp < 3000) {
return true
if (new Date().getTime() - HttpBase.lastLoginErrorTimestamp < 3000) {
return true;
} else {
HttpBase.lastLoginErrorTimestamp = (new Date().getTime());
HttpBase.lastLoginErrorTimestamp = new Date().getTime();
}
MessageBoxUtil.error("您尚未登录,请登录后访问!")
MessageBoxUtil.error('您尚未登录,请登录后访问!');
//立即进行登录跳转。
this.jumpLogin()
this.jumpLogin();
return true;
}
return false
return false;
}
/**
* 基类中的http请求会去统一处理错误情况。
* 1.登录过期
*
*/
httpGet(url: any, params = {}, successCallback?: any, errorCallback?: any, finallyCallback?: any, opts?: any) {
let that = this
httpGet(
url: any,
params = {},
successCallback?: any,
errorCallback?: any,
finallyCallback?: any,
opts?: any
) {
let that = this;
if (!opts) {
opts = {}
opts = {};
}
that.loading = true
that.loading = true;
//更新react控件的状态
that.updateUI()
that.updateUI();
return HttpUtil.httpGet(url, params, function (response: any) {
//有可能正常接口回来的数据也是错误的。交给错误处理器处理。
if (that.specialErrorHandler(response)) {
return HttpUtil.httpGet(
url,
params,
function (response: any) {
//有可能正常接口回来的数据也是错误的。交给错误处理器处理。
if (that.specialErrorHandler(response)) {
SafeUtil.safeCallback(errorCallback)(response);
return;
}
SafeUtil.safeCallback(errorCallback)(response)
return
}
SafeUtil.safeCallback(successCallback)(response);
},
function (err: any) {
let response = err.response;
console.error('请求出错啦', response);
//特殊错误情况的通用处理方式
if (that.specialErrorHandler(response)) {
return;
}
SafeUtil.safeCallback(successCallback)(response)
that.defaultErrorHandler(response, errorCallback);
},
function (res: any) {
that.loading = false;
}, function (err: any) {
//更新react控件的状态
that.updateUI();
let response = err.response
console.error("请求出错啦", response)
//特殊错误情况的通用处理方式
if (that.specialErrorHandler(response)) {
return
}
that.defaultErrorHandler(response, errorCallback)
}, function (res: any) {
that.loading = false
//更新react控件的状态
that.updateUI()
SafeUtil.safeCallback(finallyCallback)(res)
}, opts);
SafeUtil.safeCallback(finallyCallback)(res);
},
opts
);
}
/**
* 基类中纯粹的http get请求。
*
*/
httpPureGet(url: any, params = {}, successCallback?: any, errorCallback?: any, finallyCallback?: any, opts?: any) {
let that = this
httpPureGet(
url: any,
params = {},
successCallback?: any,
errorCallback?: any,
finallyCallback?: any,
opts?: any
) {
let that = this;
if (!opts) {
opts = {}
opts = {};
}
return HttpUtil.httpGet(url, params, function (response: any) {
SafeUtil.safeCallback(successCallback)(response)
}, function (err: any) {
SafeUtil.safeCallback(errorCallback)(that.getErrorMessage(err.response))
}, function (res: any) {
SafeUtil.safeCallback(finallyCallback)(res)
}, opts);
return HttpUtil.httpGet(
url,
params,
function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
},
function (err: any) {
SafeUtil.safeCallback(errorCallback)(
that.getErrorMessage(err.response)
);
},
function (res: any) {
SafeUtil.safeCallback(finallyCallback)(res);
},
opts
);
}
/**
* 基类中的http请求会去统一处理错误情况。
* 1.登录过期
*
*/
httpPost(url: any, params = {}, successCallback?: any, errorCallback?: any, finallyCallback?: any, opts?: any) {
let that = this
httpPost(
url: any,
params = {},
successCallback?: any,
errorCallback?: any,
finallyCallback?: any,
opts?: any
) {
let that = this;
if (!opts) {
opts = {}
opts = {};
}
that.loading = true
that.loading = true;
//更新react控件的状态
that.updateUI()
that.updateUI();
let formData = qs.stringify(params);
if (!opts["headers"]) {
opts["headers"] = {}
if (!opts['headers']) {
opts['headers'] = {};
}
opts["headers"]['Content-Type'] = 'application/x-www-form-urlencoded'
opts['headers']['Content-Type'] = 'application/x-www-form-urlencoded';
return HttpUtil.httpPost(
url,
formData,
function (response: any) {
//有可能正常接口回来的数据也是错误的。交给错误处理器处理。
if (that.specialErrorHandler(response)) {
SafeUtil.safeCallback(errorCallback)(response);
return;
}
return HttpUtil.httpPost(url, formData, function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
},
function (err: any) {
let response = err.response;
//有可能正常接口回来的数据也是错误的。交给错误处理器处理。
if (that.specialErrorHandler(response)) {
console.error('请求出错啦', response ? response : err);
SafeUtil.safeCallback(errorCallback)(response)
return
}
//特殊错误情况的通用处理方式
if (that.specialErrorHandler(response)) {
return;
}
SafeUtil.safeCallback(successCallback)(response)
that.defaultErrorHandler(response, errorCallback);
},
function (res: any) {
that.loading = false;
}, function (err: any) {
//更新react控件的状态
that.updateUI();
let response = err.response
console.error("请求出错啦", response ? response : err)
//特殊错误情况的通用处理方式
if (that.specialErrorHandler(response)) {
return
}
that.defaultErrorHandler(response, errorCallback)
}, function (res: any) {
that.loading = false
//更新react控件的状态
that.updateUI()
SafeUtil.safeCallback(finallyCallback)(res)
}, opts);
SafeUtil.safeCallback(finallyCallback)(res);
},
opts
);
}
}
+45 -93
View File
@@ -13,7 +13,6 @@ import HttpBase from './HttpBase';
* 这个类具有非常神奇的能力,可以说是整个项目含金量最高的一个类!
*/
export default class Pager<T> extends HttpBase {
static MAX_PAGE_SIZE = 500;
/**
@@ -58,15 +57,17 @@ export default class Pager<T> extends HttpBase {
*/
history: boolean = false;
constructor(reactComponent: React.Component | null, Clazz: any, pageSize = 20) {
constructor(
reactComponent: React.Component | null,
Clazz: any,
pageSize = 20
) {
super(reactComponent);
this.pageSize = pageSize;
//这里的处理利用了js的原型调用链,比较魔法。
if (Clazz && (Clazz.prototype instanceof BaseEntity)) {
if (Clazz && Clazz.prototype instanceof BaseEntity) {
this.Clazz = Clazz;
let urlPage = Clazz.prototype.getUrlList();
@@ -77,16 +78,13 @@ export default class Pager<T> extends HttpBase {
}
if (Clazz.prototype.getFilters) {
//直接获取该类的过滤器。
this.filters = Clazz.prototype.getFilters();
} else {
console.error('The Clazz MUST define a prototype method named \'getFilters\'');
console.error(
"The Clazz MUST define a prototype method named 'getFilters'"
);
}
} else {
console.error('You MUST specify a Clazz extended BaseEntity');
}
@@ -94,14 +92,11 @@ export default class Pager<T> extends HttpBase {
//把obj中的属性,赋值到this中来。采用深拷贝。
assign(obj: any) {
super.assign(obj);
this.assignList('data', this.Clazz);
}
//该方法是在地址栏添加上query参数,参数就是filters中的key和value.
//同时地址栏上有的参数也会自动读取到filters中去
//因此,启用该方法后返回时可以停留在之前的页码中。
@@ -126,32 +121,27 @@ export default class Pager<T> extends HttpBase {
this.pageSize = 10;
}
//从请求参数中传值。
for (let i = 0; i < this.filters.length; i++) {
let filter: Filter = this.filters[i];
let queryValue = BrowserUtil.getQueryByName(filter.key);
if (queryValue !== null && queryValue !== '') {
filter.putValue(queryValue);
} else {
filter.reset();
}
}
}
//重置所有过滤器
resetFilter() {
for (let i = 0; i < this.filters.length; i++) {
let filter = this.filters[i];
filter.reset();
}
};
}
//重置排序过滤器
resetSortFilters() {
@@ -161,8 +151,7 @@ export default class Pager<T> extends HttpBase {
filter.reset();
}
}
};
}
//手动设置过滤器的值
setFilterValue(key: string, value: any) {
@@ -171,38 +160,20 @@ export default class Pager<T> extends HttpBase {
}
for (let i = 0; i < this.filters.length; i++) {
let filter = this.filters[i];
if (filter.key === key) {
filter.putValue(value);
}
}
};
}
setFilterValues(obj: any) {
const keys = Object.keys(obj);
if(keys.length) {
keys.forEach(key => this.setFilterValue(key, obj[key]))
if (keys.length) {
keys.forEach((key) => this.setFilterValue(key, obj[key]));
}
};
//根据key来删除某个Filter
removeFilter(key: string) {
if (!this.filters || !this.filters.length) {
return;
}
for (let i = 0; i < this.filters.length; i++) {
let filter = this.filters[i];
if (filter.key === key) {
this.filters.splice(i, 1);
break;
}
}
};
}
//隐藏某个Filter,实际上我们可以根据这个filter来筛选,只不过不出现在NbFilter中而已。
showFilter(key: string, visible = true) {
@@ -216,7 +187,7 @@ export default class Pager<T> extends HttpBase {
break;
}
}
};
}
showAllFilter(visible = true) {
if (!this.filters || !this.filters.length) {
@@ -242,25 +213,23 @@ export default class Pager<T> extends HttpBase {
}
return null;
};
}
//根据一个key来获取某个filter
getFilterValue(key: string) {
let filter = this.getFilter(key);
if (!filter) {
return null
return null;
} else {
return filter.getValueString()
return filter.getValueString();
}
};
}
/**
* 获取当前进行sort的那个filter
* 我们认为一次只有一个排序值
*/
getCurrentSortFilter(): Filter | null {
if (!this.filters || !this.filters.length) {
return null;
}
@@ -272,15 +241,12 @@ export default class Pager<T> extends HttpBase {
return filter;
}
}
}
return null;
}
//获取所有的filter参数,键值对形式
getParams(): { [s: string]: string | number } {
let params: { [s: string]: string | number } = {
page: this.page,
pageSize: this.pageSize,
@@ -299,8 +265,7 @@ export default class Pager<T> extends HttpBase {
}
return params;
};
}
//元素是否为空
isEmpty(): boolean {
@@ -309,39 +274,41 @@ export default class Pager<T> extends HttpBase {
//去服务器端进行请求
httpList(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this;
let params: { [s: string]: string | number } = this.getParams();
if (this.history) {
window.history.replaceState({}, '', window.location.pathname + '?' + ObjectUtil.param(params));
window.history.replaceState(
{},
'',
window.location.pathname + '?' + ObjectUtil.param(params)
);
}
//准备去请求,所有错误置为空
this.errorMessage = null;
this.httpGet(this.urlPage, params, function(response: any) {
// handle success
this.httpGet(
this.urlPage,
params,
function (response: any) {
// handle success
that.assign(response.data.data);
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
}, function(errorMessage: any, response: any) {
//失败了就清空
that.data = [];
SafeUtil.safeCallback(errorCallback)(errorMessage, response);
}, finalCallback);
SafeUtil.safeCallback(successCallback)(response);
},
function (errorMessage: any, response: any) {
//失败了就清空
that.data = [];
SafeUtil.safeCallback(errorCallback)(errorMessage, response);
},
finalCallback
);
}
//从pager中获取当前的分页情况,在table的分页器中显示 PaginationConfig
getPagination(): any | false {
let that = this;
@@ -357,35 +324,28 @@ export default class Pager<T> extends HttpBase {
} else {
return false;
}
}
//获取默认的排序顺序,提供给table使用
getDefaultSortOrder(columnKey: string): any | boolean {
//将变化的这个情况更新。
let filterKey = 'order' + StringUtil.capitalize(columnKey);
//直接放进排序值即可,底层会自动兼容
let sortFilter = this.getFilter(filterKey);
if (sortFilter && (sortFilter instanceof SortFilter)) {
if (sortFilter && sortFilter instanceof SortFilter) {
let antdSortValue: any | null = sortFilter.getAntdValue();
if (antdSortValue) {
return antdSortValue;
} else {
return false;
}
} else {
return false;
}
}
//在table的分页器 发生变化调用 TODO: PaginationConfig SorterResult TableCurrentDataSource
tableOnChange(pagination: any, filters: any, sorter: any, extra: any) {
let that = this;
@@ -395,22 +355,19 @@ export default class Pager<T> extends HttpBase {
}
if (pagination.pageSize !== undefined) {
that.pageSize = pagination.pageSize;
}
//重置所有的sort
that.resetSortFilters();
if (!StringUtil.isEmptyObject(sorter)) {
//将变化的这个情况更新。
let filterKey = 'order' + StringUtil.capitalize(StringUtil.underScoreToCamel(sorter.field));
let filterKey =
'order' +
StringUtil.capitalize(StringUtil.underScoreToCamel(sorter.field));
//直接放进排序值即可,底层会自动兼容
that.setFilterValue(filterKey, sorter.order);
}
//直接去刷新
@@ -424,9 +381,4 @@ export default class Pager<T> extends HttpBase {
this.totalItems = 0;
this.totalPages = 0;
}
}
+2 -3
View File
@@ -5,8 +5,7 @@ enum SortDirection {
//这两项是antd的标准
DESCEND = 'descend',
ASCEND = 'ascend'
ASCEND = 'ascend',
}
export default SortDirection
export default SortDirection;
+10 -15
View File
@@ -5,34 +5,29 @@
* 这个类附带了一个视图,通过对这个类的操作可以对对应的视图进行更新等操作。
* Castle中的类,大多数直接继承这个类。
*/
import Base from "./Base";
import React from "react";
import Base from './Base';
import React from 'react';
export default class ViewBase extends Base {
//该类对应的React视图。
reactComponent: React.Component | null = null
reactComponent: React.Component | null = null;
//更新某一个组件对应的UI
static updateComponentUI(component: React.Component | null, entity?: any) {
if (component) {
//_version在这里是充当fake的。
component.setState({_version: new Date().getTime()})
component.setState({ _version: new Date().getTime() });
} else {
console.warn(`${entity && entity.constructor ? entity.constructor.name : '未知对象'} 的 reactComponent 不存在,无法更新其对应的UI`)
console.warn(
`${
entity && entity.constructor ? entity.constructor.name : '未知对象'
} 的 reactComponent 不存在,无法更新其对应的UI`
);
}
}
//更新当前的视图
updateUI() {
ViewBase.updateComponentUI(this.reactComponent, this)
ViewBase.updateComponentUI(this.reactComponent, this);
}
}
+51 -54
View File
@@ -1,62 +1,59 @@
import SelectionOption from "../base/option/SelectionOption";
import SelectionOption from '../base/option/SelectionOption';
enum WebResultCode {
OK = "OK",
BAD_REQUEST = "BAD_REQUEST",
NEED_SHARE_CODE = "NEED_SHARE_CODE",
SHARE_CODE_ERROR = "SHARE_CODE_ERROR",
LOGIN = "LOGIN",
NOT_INSTALLED = "NOT_INSTALLED",
SERVER = "SERVER",
UNKNOWN = "UNKNOWN",
OK = 'OK',
BAD_REQUEST = 'BAD_REQUEST',
NEED_SHARE_CODE = 'NEED_SHARE_CODE',
SHARE_CODE_ERROR = 'SHARE_CODE_ERROR',
LOGIN = 'LOGIN',
NOT_INSTALLED = 'NOT_INSTALLED',
SERVER = 'SERVER',
UNKNOWN = 'UNKNOWN',
}
let WebResultCodes: WebResultCode[] = Object.keys(WebResultCode).map(k => k as WebResultCode)
let WebResultCodes: WebResultCode[] = Object.keys(WebResultCode).map(
(k) => k as WebResultCode
);
let WebResultCodeMap: { [key in keyof typeof WebResultCode]: SelectionOption } = {
OK: {
"name": "成功",
"value": "OK",
},
BAD_REQUEST: {
"name": "请求错误",
"value": "BAD_REQUEST",
},
NEED_SHARE_CODE: {
name: "提取码必填",
value: "NEED_SHARE_CODE"
},
SHARE_CODE_ERROR: {
name: "提取码错误",
value: "SHARE_CODE_ERROR"
},
LOGIN: {
"name": "未登录",
"value": "LOGIN",
},
NOT_INSTALLED: {
"name": "未安装",
"value": "NOT_INSTALLED",
},
SERVER: {
"name": "服务器出错",
"value": "SERVER",
},
UNKNOWN: {
"name": "未知错误",
"value": "UNKNOWN",
},
}
let WebResultCodeMap: { [key in keyof typeof WebResultCode]: SelectionOption } =
{
OK: {
name: '成功',
value: 'OK',
},
BAD_REQUEST: {
name: '请求错误',
value: 'BAD_REQUEST',
},
NEED_SHARE_CODE: {
name: '提取码必填',
value: 'NEED_SHARE_CODE',
},
SHARE_CODE_ERROR: {
name: '提取码错误',
value: 'SHARE_CODE_ERROR',
},
LOGIN: {
name: '未登录',
value: 'LOGIN',
},
NOT_INSTALLED: {
name: '未安装',
value: 'NOT_INSTALLED',
},
SERVER: {
name: '服务器出错',
value: 'SERVER',
},
UNKNOWN: {
name: '未知错误',
value: 'UNKNOWN',
},
};
let WebResultCodeList: SelectionOption[] = []
let WebResultCodeList: SelectionOption[] = [];
WebResultCodes.forEach((type: WebResultCode, index: number) => {
WebResultCodeList.push(WebResultCodeMap[type])
})
export {WebResultCode, WebResultCodes, WebResultCodeMap, WebResultCodeList}
WebResultCodeList.push(WebResultCodeMap[type]);
});
export { WebResultCode, WebResultCodes, WebResultCodeMap, WebResultCodeList };
+14 -19
View File
@@ -1,5 +1,4 @@
import Filter from "./Filter";
import Filter from './Filter';
/**
*
@@ -7,46 +6,42 @@ import Filter from "./Filter";
*
*/
export default class CheckFilter extends Filter {
//值,最终填写的内容。null表示这个值不设置
value: boolean | null = null
value: boolean | null = null;
constructor(name: string, code: string, visible?: boolean) {
super(name, code, visible)
super(name, code, visible);
}
//获取值字符串,[null,true,false] => ["","true","false"]
getValueString(): string {
let stringValue = ""
let stringValue = '';
if (this.value === true) {
stringValue = "true"
stringValue = 'true';
} else if (this.value === false) {
stringValue = "false"
stringValue = 'false';
} else {
stringValue = ""
stringValue = '';
}
return stringValue
return stringValue;
}
//通过一个字符串来设置值
putValue(value: any) {
if (value === "true" || value === true) {
this.value = true
} else if (value === "false" || value === false) {
this.value = false
if (value === 'true' || value === true) {
this.value = true;
} else if (value === 'false' || value === false) {
this.value = false;
} else {
this.value = null
this.value = null;
}
}
reset() {
this.value = null
this.value = null;
}
isEmpty(): boolean {
return this.value === null;
}
}
+11 -19
View File
@@ -1,6 +1,5 @@
import Filter from "./Filter";
import DateUtil from "../../../util/DateUtil";
import Filter from './Filter';
import DateUtil from '../../../util/DateUtil';
/**
*
@@ -8,47 +7,40 @@ import DateUtil from "../../../util/DateUtil";
*
*/
export default class DateFilter extends Filter {
//时间筛选的格式
format: string
format: string;
//值,最终填写的内容。null表示这个值不设置
value: Date | null = null
value: Date | null = null;
constructor(name: string, code: string, format?: string, visible?: boolean) {
super(name, code, visible)
super(name, code, visible);
if (format) {
this.format = format
this.format = format;
} else {
this.format = DateUtil.DATE_FORMAT
this.format = DateUtil.DATE_FORMAT;
}
}
//获取值字符串
getValueString(): string {
if (this.value) {
return DateUtil.format(this.value, this.format)
return DateUtil.format(this.value, this.format);
} else {
return ""
return '';
}
}
//通过一个字符串来设置值
putValue(value: string) {
this.value = DateUtil.parse(value)
this.value = DateUtil.parse(value);
}
reset() {
this.value = null
this.value = null;
}
isEmpty(): boolean {
return this.value === null;
}
}
+11 -19
View File
@@ -1,6 +1,5 @@
import Filter from "./Filter";
import DateUtil from "../../../util/DateUtil";
import Filter from './Filter';
import DateUtil from '../../../util/DateUtil';
/**
*
@@ -8,47 +7,40 @@ import DateUtil from "../../../util/DateUtil";
*
*/
export default class DateTimeFilter extends Filter {
//时间筛选的格式
format: string
format: string;
//值,最终填写的内容。null表示这个值不设置
value: Date | null = null
value: Date | null = null;
constructor(name: string, code: string, format?: string, visible?: boolean) {
super(name, code, visible)
super(name, code, visible);
if (format) {
this.format = format
this.format = format;
} else {
this.format = DateUtil.DEFAULT_FORMAT
this.format = DateUtil.DEFAULT_FORMAT;
}
}
//获取值字符串
getValueString(): string {
if (this.value) {
return DateUtil.format(this.value, this.format)
return DateUtil.format(this.value, this.format);
} else {
return ""
return '';
}
}
//通过一个字符串来设置值
putValue(value: string) {
this.value = DateUtil.parse(value)
this.value = DateUtil.parse(value);
}
reset() {
this.value = null
this.value = null;
}
isEmpty(): boolean {
return this.value === null;
}
}
+22 -16
View File
@@ -2,23 +2,22 @@
* 这个是列表筛选器的基类。
*/
export default class Filter {
//中文名
name: string
name: string;
//提交时候的键值,英文
key: string
key: string;
//是否可见
visible: boolean = true
visible: boolean = true;
constructor(name: string, key: string, visible?: boolean) {
this.name = name
this.key = key
this.name = name;
this.key = key;
//没有传默认为true.
if (visible === undefined) {
this.visible = true
this.visible = true;
} else {
this.visible = visible
this.visible = visible;
}
}
@@ -26,30 +25,37 @@ export default class Filter {
* 获取到一个字符串的值
*/
getValueString(): string {
console.error(`${this.constructor.name} 的 getValue 不存在,请开发者及时配置。`)
return ""
console.error(
`${this.constructor.name} 的 getValue 不存在,请开发者及时配置。`
);
return '';
}
/**
* 把一个字符串放置到value中去,因为有可能从请求参数中回填值
*/
putValue(value: string) {
console.error(`${this.constructor.name} 的 putValue 不存在,请开发者及时配置。`)
console.error(
`${this.constructor.name} 的 putValue 不存在,请开发者及时配置。`
);
}
/**
* 将过滤器的值清空
*/
reset() {
console.error(`${this.constructor.name} 的 reset 不存在,请开发者及时配置。`)
console.error(
`${this.constructor.name} 的 reset 不存在,请开发者及时配置。`
);
}
/**
* 当前过滤器是否为空
*/
isEmpty():boolean {
console.error(`${this.constructor.name} 的 isEmpty 不存在,请开发者及时配置。`)
return false
isEmpty(): boolean {
console.error(
`${this.constructor.name} 的 isEmpty 不存在,请开发者及时配置。`
);
return false;
}
}
@@ -1,6 +1,5 @@
import SelectionFilter from "./SelectionFilter";
import SelectionFilterType from "./SelectionFilterType";
import SelectionFilter from './SelectionFilter';
import SelectionFilterType from './SelectionFilterType';
/**
*
@@ -8,25 +7,29 @@ import SelectionFilterType from "./SelectionFilterType";
*
*/
export default class HttpSelectionFilter extends SelectionFilter {
//远程的访问链接。
url: string
url: string;
constructor(name: string, code: string, url: string, selectionType?: SelectionFilterType, visible?: boolean) {
super(name, code, [], selectionType, visible)
this.url = url
constructor(
name: string,
code: string,
url: string,
selectionType?: SelectionFilterType,
visible?: boolean
) {
super(name, code, [], selectionType, visible);
this.url = url;
}
//通过一个字符串来设置值
//远程调用的值必须要宽容,因为值可能还没有从远程取回来呢。
putValue(value: string) {
this.value = value
this.value = value;
}
//严格的回填,采用父类的策略
//这个方法会在http请求完成了之后调用。HttpSelectionFilter中会调用
strictPutValue(value: string) {
super.putValue(value)
super.putValue(value);
}
}
+16 -17
View File
@@ -1,47 +1,46 @@
import Filter from "./Filter";
import Filter from './Filter';
/**
* 文本输入框筛选器
* 这种一般是模糊搜索。
*/
export default class InputFilter extends Filter {
//值,最终填写的内容
value: string = ""
value: string = '';
//占位符
placeholder: string
placeholder: string;
constructor(name: string, code: string, placeholder?: string | null, visible?: boolean) {
super(name, code, visible)
constructor(
name: string,
code: string,
placeholder?: string | null,
visible?: boolean
) {
super(name, code, visible);
if (placeholder) {
this.placeholder = placeholder
this.placeholder = placeholder;
} else {
this.placeholder = name
this.placeholder = name;
}
}
//获取值字符串
getValueString(): string {
return this.value
return this.value;
}
//通过一个字符串来设置值
putValue(value: string) {
this.value = value
this.value = value;
}
reset() {
this.value = ""
this.value = '';
}
isEmpty(): boolean {
return this.value === "";
return this.value === '';
}
}
+26 -27
View File
@@ -1,7 +1,6 @@
import Filter from "./Filter";
import SelectionOption from "../option/SelectionOption";
import SelectionFilterType from "./SelectionFilterType";
import Filter from './Filter';
import SelectionOption from '../option/SelectionOption';
import SelectionFilterType from './SelectionFilterType';
/**
*
@@ -9,60 +8,60 @@ import SelectionFilterType from "./SelectionFilterType";
*
*/
export default class SelectionFilter extends Filter {
//候选项,要求必须有name和value。
options: SelectionOption[]
options: SelectionOption[];
//值,最终填写的内容。null表示这个值不设置
value: string = ""
value: string = '';
//采用的样式
selectionType: SelectionFilterType
selectionType: SelectionFilterType;
constructor(name: string, code: string, options: SelectionOption[],selectionType?: SelectionFilterType, visible?: boolean) {
super(name, code, visible)
constructor(
name: string,
code: string,
options: SelectionOption[],
selectionType?: SelectionFilterType,
visible?: boolean
) {
super(name, code, visible);
this.options = options
this.options = options;
if (selectionType === undefined) {
this.selectionType = SelectionFilterType.COMBOBOX
this.selectionType = SelectionFilterType.COMBOBOX;
} else {
this.selectionType = selectionType
this.selectionType = selectionType;
}
}
//获取值字符串
getValueString(): string {
return this.value
return this.value;
}
//通过一个字符串来设置值
putValue(value: string) {
if (value === "") {
this.value = ""
if (value === '') {
this.value = '';
} else {
//必须是options中的值才接纳。
for (let j = 0; j < this.options.length; j++) {
let opt = this.options[j]
let opt = this.options[j];
if (opt.value === value) {
this.value = value
return
this.value = value;
return;
}
}
this.value = ""
this.value = '';
}
}
reset() {
this.value = ""
this.value = '';
}
isEmpty(): boolean {
return this.value === "";
return this.value === '';
}
}
@@ -3,9 +3,9 @@
*/
enum SelectionFilterType {
//下拉框的样式
COMBOBOX = "COMBOBOX",
COMBOBOX = 'COMBOBOX',
//按钮的样式
BUTTON = "BUTTON",
BUTTON = 'BUTTON',
}
export default SelectionFilterType
export default SelectionFilterType;
+23 -24
View File
@@ -1,6 +1,5 @@
import Filter from "./Filter";
import SortDirection from "../SortDirection";
import Filter from './Filter';
import SortDirection from '../SortDirection';
/**
*
@@ -8,62 +7,62 @@ import SortDirection from "../SortDirection";
*
*/
export default class SortFilter extends Filter {
//值,最终填写的内容。null表示这个值不设置
value: SortDirection | null = null
value: SortDirection | null = null;
constructor(name: string, code: string, visible?: boolean) {
super(name, code, visible)
super(name, code, visible);
//过滤排序器,more是不显示的。
//没有传默认为true.
if (visible === undefined) {
this.visible = false
this.visible = false;
} else {
this.visible = visible
this.visible = visible;
}
}
//获取值字符串,[null,true,false] => ["","true","false"]
getValueString(): string {
if (this.value) {
return this.value
return this.value;
} else {
return ""
return '';
}
}
//通过一个字符串来设置值
putValue(value: string) {
if (value === SortDirection.DESC || value === SortDirection.DESCEND) {
this.value = SortDirection.DESC
this.value = SortDirection.DESC;
} else if (value === SortDirection.ASC || value === SortDirection.ASCEND) {
this.value = SortDirection.ASC
this.value = SortDirection.ASC;
} else {
this.value = null
this.value = null;
}
}
//获取到antd需要使用的值 TODO: SortOrder
getAntdValue(): any | null {
if (this.value === SortDirection.DESC || this.value === SortDirection.DESCEND) {
return SortDirection.DESCEND
} else if (this.value === SortDirection.ASC || this.value === SortDirection.ASCEND) {
return SortDirection.ASCEND
if (
this.value === SortDirection.DESC ||
this.value === SortDirection.DESCEND
) {
return SortDirection.DESCEND;
} else if (
this.value === SortDirection.ASC ||
this.value === SortDirection.ASCEND
) {
return SortDirection.ASCEND;
} else {
return null
return null;
}
}
reset() {
this.value = null
this.value = null;
}
isEmpty(): boolean {
return this.value === null;
}
}
@@ -2,9 +2,9 @@
* 面包屑对应的模型
*/
export default interface BreadcrumbModel {
name: string,
path: string,
query: any,
displayDirect: boolean,
name: string;
path: string;
query: any;
displayDirect: boolean;
onClick?: () => void;
}
@@ -1,9 +1,9 @@
/**
* 带有Code的一个selection
*/
import SelectionOption from "./SelectionOption";
import SelectionOption from './SelectionOption';
export default interface CodeSelectionOption extends SelectionOption {
code: string,
type: string,
code: string;
type: string;
}
+6 -6
View File
@@ -1,9 +1,9 @@
enum Color {
PRIMARY = "#215891",
INFO = "#2DB7F5",
SUCCESS = "#67C23A",
WARNING = "#E6A23C",
DANGER = "#FF756F",
PRIMARY = '#215891',
INFO = '#2DB7F5',
SUCCESS = '#67C23A',
WARNING = '#E6A23C',
DANGER = '#FF756F',
}
export default Color
export default Color;
@@ -1,9 +1,9 @@
/**
* 下拉筛选框的选项,统一使用该接口,同时这个作为枚举的复杂类型。
*/
import SelectionOption from "./SelectionOption";
import SelectionOption from './SelectionOption';
export default interface ColorSelectionOption extends SelectionOption {
index: number,
color: string,
index: number;
color: string;
}
@@ -1,8 +1,8 @@
/**
* 下拉筛选框的选项,统一使用该接口,同时这个作为枚举的复杂类型。
*/
import SelectionOption from "./SelectionOption";
import SelectionOption from './SelectionOption';
export default interface ColorSelectionOption extends SelectionOption {
color: string,
color: string;
}
@@ -1,11 +1,9 @@
/**
* 包含图标和 index序列的选项,在ProteinSqlPendant中有用到
*/
import SelectionOption from "./SelectionOption";
import SelectionOption from './SelectionOption';
export default interface IconIndexSelectionOption extends SelectionOption {
index: number,
icon: string,
index: number;
icon: string;
}
@@ -1,10 +1,10 @@
/**
* 下拉筛选框的选项,统一使用该接口,同时这个作为枚举的复杂类型。
*/
import SelectionOption from "./SelectionOption";
import SelectionOption from './SelectionOption';
export default interface IndexSelectionOption extends SelectionOption {
index: number,
name: string,
value: string
index: number;
name: string;
value: string;
}
@@ -2,7 +2,6 @@
* 下拉筛选框的选项,统一使用该接口,同时这个作为枚举的复杂类型。
*/
export default interface SelectionOption {
name: string,
value: string
name: string;
value: string;
}
@@ -1,8 +1,8 @@
/**
* 下拉筛选框的选项,统一使用该接口,同时这个作为枚举的复杂类型。
*/
import SelectionOption from "./SelectionOption";
import SelectionOption from './SelectionOption';
export default interface StyleSelectionOption extends SelectionOption {
style: string,
style: string;
}
+13 -19
View File
@@ -3,55 +3,49 @@
*/
export interface IconActionItem {
//图片的资源链接(优先级高于iconType)
icon?: string
icon?: string;
//antd的图标类型
iconType?: string
iconType?: string;
//名称
name?: string
name?: string;
//是否可见。默认可见
visible?: boolean
visible?: boolean;
//点击图标后的行为
onClick?: () => void
onClick?: () => void;
}
/**
* 工具箱的条目,图标采用antd的图标。
* 图标不带有高亮样式
*/
export interface ToolItem {
name: string
name: string;
//当前是否处于展开状态
active: boolean
active: boolean;
//antd的图标样式
iconType: string
iconType: string;
}
/**
* 该接口可以像文件树一样展示。
* 同时图标带有高亮样式
*/
export interface SkeletonItem {
name: string
name: string;
//当前是否处于展开状态
active: boolean
active: boolean;
//高亮的图标 资源链接
activeIcon: string
activeIcon: string;
//一般状态的图标 资源链接
icon: string
icon: string;
}
/**
* 请求的Map,键值对
*/
export type RequestParamMap = { [key: string]: string }
export type RequestParamMap = { [key: string]: string };
+733
View File
@@ -0,0 +1,733 @@
import axios, { CancelTokenSource } from 'axios';
import HttpUtil from '../../util/HttpUtil';
import { calculateChunkMd5, isMd5Available } from '../../util/Md5Util';
// Default chunk size: 50MB
const DEFAULT_CHUNK_SIZE = 50 * 1024 * 1024;
// Minimum chunk size: 5MB (aligned with AWS S3 standard)
const MIN_CHUNK_SIZE = 5 * 1024 * 1024;
// Maximum chunk size: 100MB
const MAX_CHUNK_SIZE = 100 * 1024 * 1024;
// Minimum file size to use chunked upload: 100MB
const MIN_FILE_SIZE_FOR_CHUNK = 100 * 1024 * 1024;
// Maximum concurrent chunk uploads
const MAX_CONCURRENT_UPLOADS = 6;
// Maximum retry attempts for failed chunk uploads
const MAX_RETRY_ATTEMPTS = 3;
// Base delay for retry backoff (ms)
const RETRY_BASE_DELAY = 1000;
// Session expiration time for localStorage cleanup (24 hours in ms, matching backend)
const SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1000;
// Upload timeout for large chunks (10 minutes in ms)
const UPLOAD_TIMEOUT_MS = 10 * 60 * 1000;
// Upload session status
export enum UploadSessionStatus {
UPLOADING = 'UPLOADING',
MERGING = 'MERGING',
COMPLETED = 'COMPLETED',
ERROR = 'ERROR',
CANCELLED = 'CANCELLED',
PAUSED = 'PAUSED',
}
// Upload session info from server
export interface UploadSession {
uuid: string;
userUuid: string;
spaceUuid: string;
matterUuid: string;
puuid: string;
filename: string;
totalSize: number;
chunkSize: number;
totalChunks: number;
privacy: boolean;
fileMd5: string;
status: string;
expireTime: string;
}
// Session info with uploaded chunks
export interface UploadSessionInfo {
session: UploadSession;
uploadedChunks: number[];
missingChunks: number[];
}
// Chunk upload progress callback
export interface ChunkUploadProgress {
sessionUuid: string;
filename: string;
totalSize: number;
uploadedSize: number;
progress: number; // 0-1
speed: number; // bytes per second
uploadedChunks: number;
totalChunks: number;
status: UploadSessionStatus;
}
// Local storage key for saving upload sessions
const UPLOAD_SESSION_STORAGE_KEY = 'tank_chunk_upload_sessions';
export default class ChunkUploader {
// API endpoints
static URL_CREATE_SESSION = '/api/chunk/create/session';
static URL_UPLOAD_CHUNK = '/api/chunk/upload';
static URL_MERGE_CHUNKS = '/api/chunk/merge';
static URL_SESSION_INFO = '/api/chunk/session/info';
static URL_CANCEL_SESSION = '/api/chunk/cancel';
// File to upload
file: File;
// Parent directory UUID
puuid: string;
// Space UUID
spaceUuid: string;
// Privacy flag
privacy: boolean;
// Chunk size in bytes
chunkSize: number;
// Upload session info
session: UploadSession | null = null;
// Current status
status: UploadSessionStatus = UploadSessionStatus.UPLOADING;
// Uploaded chunk indices
uploadedChunks: Set<number> = new Set();
// Uploading chunk indices (currently in progress)
uploadingChunks: Set<number> = new Set();
// Total chunks
totalChunks: number = 0;
// Cancel token sources for each chunk
cancelSources: Map<number, CancelTokenSource> = new Map();
// Progress callback
onProgress?: (progress: ChunkUploadProgress) => void;
// Complete callback
onComplete?: (matter: any) => void;
// Error callback
onError?: (error: string) => void;
// React component for UI updates
reactComponent?: React.Component;
// Speed calculation
private lastUpdateTime: number = 0;
private lastUploadedSize: number = 0;
private currentSpeed: number = 0;
// Track progress of each chunk (chunkIndex -> bytes uploaded)
private chunkProgress: Map<number, number> = new Map();
// Quick file fingerprint for session matching (not full MD5)
private fileFingerprint: string = '';
// Whether chunk-level MD5 verification is available (spark-md5 installed)
private chunkMd5Available: boolean = false;
constructor(
file: File,
puuid: string,
spaceUuid: string,
privacy: boolean = true,
chunkSize?: number,
reactComponent?: React.Component
) {
this.file = file;
this.puuid = puuid;
this.spaceUuid = spaceUuid;
this.privacy = privacy;
this.reactComponent = reactComponent;
// Validate and set chunk size
if (chunkSize) {
this.chunkSize = Math.max(MIN_CHUNK_SIZE, Math.min(MAX_CHUNK_SIZE, chunkSize));
} else {
this.chunkSize = DEFAULT_CHUNK_SIZE;
}
// Calculate total chunks
this.totalChunks = Math.ceil(file.size / this.chunkSize);
}
// Check if file should use chunked upload
static shouldUseChunkedUpload(fileSize: number): boolean {
return fileSize >= MIN_FILE_SIZE_FOR_CHUNK;
}
// Start the upload process
async start(): Promise<void> {
try {
this.status = UploadSessionStatus.UPLOADING;
this.lastUpdateTime = Date.now();
this.lastUploadedSize = 0;
// Calculate file fingerprint for better session matching
this.fileFingerprint = await this.calculateFileFingerprint();
// Try to resume from existing session
const existingSession = await this.loadSessionFromStorage();
if (existingSession) {
// Verify session with server
const sessionInfo = await this.getSessionInfo(existingSession.uuid);
if (sessionInfo && sessionInfo.session.status === 'UPLOADING') {
this.session = sessionInfo.session;
this.uploadedChunks = new Set(sessionInfo.uploadedChunks);
this.totalChunks = this.session.totalChunks;
this.updateProgress();
await this.uploadMissingChunks(sessionInfo.missingChunks);
return;
} else {
// Session expired or completed, remove from storage
this.removeSessionFromStorage();
}
}
// Check if chunk-level MD5 verification is available
this.chunkMd5Available = await isMd5Available();
if (this.chunkMd5Available) {
console.info('Chunk-level MD5 verification enabled');
}
// Create new session
await this.createSession();
// Save session to storage for resume capability
this.saveSessionToStorage();
// Upload all chunks
const missingChunks = Array.from({ length: this.totalChunks }, (_, i) => i);
await this.uploadMissingChunks(missingChunks);
} catch (error: any) {
// Don't change status if already PAUSED (pause() was called)
if (this.status === UploadSessionStatus.PAUSED) {
return;
}
if (axios.isCancel(error)) {
this.status = UploadSessionStatus.CANCELLED;
} else {
this.status = UploadSessionStatus.ERROR;
this.onError?.(error.message || 'Upload failed');
}
this.updateProgress();
}
}
// Create upload session
private async createSession(): Promise<void> {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('puuid', this.puuid);
formData.append('filename', this.file.name);
formData.append('totalSize', this.file.size.toString());
formData.append('chunkSize', this.chunkSize.toString());
formData.append('privacy', this.privacy.toString());
formData.append('spaceUuid', this.spaceUuid);
HttpUtil.httpPost(
ChunkUploader.URL_CREATE_SESSION,
formData,
(response: any) => {
this.session = response.data.data;
this.totalChunks = this.session!.totalChunks;
resolve();
},
(error: any) => {
const msg = error.response?.data?.msg || 'Failed to create upload session';
reject(new Error(msg));
}
);
});
}
// Get session info from server
private async getSessionInfo(sessionUuid: string): Promise<UploadSessionInfo | null> {
return new Promise((resolve) => {
HttpUtil.httpGet(
ChunkUploader.URL_SESSION_INFO,
{ sessionUuid },
(response: any) => {
resolve(response.data.data);
},
() => {
resolve(null);
}
);
});
}
// Upload missing chunks with concurrency control
private async uploadMissingChunks(missingChunks: number[]): Promise<void> {
const queue = [...missingChunks];
const uploading: Promise<void>[] = [];
while (queue.length > 0 || uploading.length > 0) {
// Check if cancelled or paused
if (this.status === UploadSessionStatus.CANCELLED || this.status === UploadSessionStatus.PAUSED) {
// Wait for ongoing uploads to finish
await Promise.all(uploading);
return;
}
// Start new uploads up to max concurrent
while (queue.length > 0 && this.uploadingChunks.size < MAX_CONCURRENT_UPLOADS) {
const chunkIndex = queue.shift()!;
const promise = this.uploadChunk(chunkIndex).then(() => {
const index = uploading.indexOf(promise);
if (index > -1) {
uploading.splice(index, 1);
}
});
uploading.push(promise);
}
// Wait for at least one upload to complete
if (uploading.length > 0) {
await Promise.race(uploading);
}
}
// All chunks uploaded, merge them
if (this.status === UploadSessionStatus.UPLOADING) {
await this.mergeChunks();
}
}
// Upload a single chunk with retry support and chunk-level MD5 verification
private async uploadChunk(chunkIndex: number, retryCount: number = 0): Promise<void> {
if (this.uploadedChunks.has(chunkIndex)) {
return;
}
this.uploadingChunks.add(chunkIndex);
this.chunkProgress.set(chunkIndex, 0);
const start = chunkIndex * this.chunkSize;
const end = Math.min(start + this.chunkSize, this.file.size);
const chunk = this.file.slice(start, end);
const chunkActualSize = end - start;
// Calculate chunk MD5 if available (this is fast since chunk is small)
let chunkMd5 = '';
if (this.chunkMd5Available) {
try {
chunkMd5 = await calculateChunkMd5(chunk);
} catch (e) {
console.warn(`Failed to calculate MD5 for chunk ${chunkIndex}:`, e);
}
}
const formData = new FormData();
formData.append('sessionUuid', this.session!.uuid);
formData.append('chunkIndex', chunkIndex.toString());
formData.append('file', chunk);
formData.append('spaceUuid', this.spaceUuid);
if (chunkMd5) {
formData.append('chunkMd5', chunkMd5);
}
const cancelSource = axios.CancelToken.source();
this.cancelSources.set(chunkIndex, cancelSource);
return new Promise((resolve, reject) => {
HttpUtil.httpPostFile(
ChunkUploader.URL_UPLOAD_CHUNK,
formData,
() => {
this.uploadingChunks.delete(chunkIndex);
this.uploadedChunks.add(chunkIndex);
this.chunkProgress.set(chunkIndex, chunkActualSize);
this.cancelSources.delete(chunkIndex);
this.updateProgress();
resolve();
},
(error: any) => {
this.uploadingChunks.delete(chunkIndex);
this.cancelSources.delete(chunkIndex);
this.chunkProgress.delete(chunkIndex);
if (axios.isCancel(error)) {
reject(error);
} else if (retryCount < MAX_RETRY_ATTEMPTS) {
// Determine if error is retryable (network errors, timeouts)
const isRetryable =
error.code === 'ECONNABORTED' || // timeout
error.code === 'ERR_NETWORK' || // network error
!error.response || // no response (network issue)
(error.response?.status >= 500 && error.response?.status < 600); // server errors
if (isRetryable) {
// Retry with exponential backoff
const delay = RETRY_BASE_DELAY * Math.pow(2, retryCount);
console.warn(
`Chunk ${chunkIndex} upload failed (${error.code || error.response?.status || 'unknown'}), retrying in ${delay}ms (attempt ${retryCount + 1}/${MAX_RETRY_ATTEMPTS})`
);
setTimeout(() => {
this.uploadChunk(chunkIndex, retryCount + 1).then(resolve).catch(reject);
}, delay);
} else {
// Non-retryable error (4xx client errors)
const msg = error.response?.data?.msg || `Failed to upload chunk ${chunkIndex}: ${error.message}`;
reject(new Error(msg));
}
} else {
const msg = error.response?.data?.msg || `Failed to upload chunk ${chunkIndex} after ${MAX_RETRY_ATTEMPTS} attempts`;
reject(new Error(msg));
}
},
undefined,
(progressEvent: any) => {
// Track real-time progress for this chunk
if (progressEvent.loaded) {
this.chunkProgress.set(chunkIndex, progressEvent.loaded);
this.updateProgress();
}
},
{ cancelToken: cancelSource.token, timeout: UPLOAD_TIMEOUT_MS }
);
});
}
// Merge all chunks
private async mergeChunks(): Promise<void> {
this.status = UploadSessionStatus.MERGING;
this.updateProgress();
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('sessionUuid', this.session!.uuid);
formData.append('spaceUuid', this.spaceUuid);
HttpUtil.httpPost(
ChunkUploader.URL_MERGE_CHUNKS,
formData,
(response: any) => {
this.status = UploadSessionStatus.COMPLETED;
this.removeSessionFromStorage();
this.updateProgress();
this.onComplete?.(response.data.data);
resolve();
},
(error: any) => {
this.status = UploadSessionStatus.ERROR;
const msg = error.response?.data?.msg || 'Failed to merge chunks';
this.onError?.(msg);
reject(new Error(msg));
}
);
});
}
// Update progress with precise chunk-level tracking
private updateProgress(): void {
// Calculate precise uploaded size including in-progress chunks
let uploadedSize = 0;
// Add completed chunks (use actual size for last chunk)
Array.from(this.uploadedChunks).forEach((chunkIndex) => {
const start = chunkIndex * this.chunkSize;
const end = Math.min(start + this.chunkSize, this.file.size);
uploadedSize += end - start;
});
// Add in-progress chunk bytes
this.chunkProgress.forEach((bytes, chunkIndex) => {
if (!this.uploadedChunks.has(chunkIndex)) {
uploadedSize += bytes;
}
});
const actualUploadedSize = Math.min(uploadedSize, this.file.size);
// Calculate speed
const now = Date.now();
const timeDelta = now - this.lastUpdateTime;
if (timeDelta > 500) {
const sizeDelta = actualUploadedSize - this.lastUploadedSize;
this.currentSpeed = Math.round(sizeDelta / (timeDelta / 1000));
this.lastUpdateTime = now;
this.lastUploadedSize = actualUploadedSize;
}
const progress: ChunkUploadProgress = {
sessionUuid: this.session?.uuid || '',
filename: this.file.name,
totalSize: this.file.size,
uploadedSize: actualUploadedSize,
progress: actualUploadedSize / this.file.size,
speed: this.currentSpeed,
uploadedChunks: this.uploadedChunks.size,
totalChunks: this.totalChunks,
status: this.status,
};
this.onProgress?.(progress);
// Update React component if provided
if (this.reactComponent) {
this.reactComponent.setState({});
}
}
// Pause upload
pause(): void {
this.status = UploadSessionStatus.PAUSED;
// Cancel ongoing chunk uploads
this.cancelSources.forEach((source) => {
source.cancel('Paused');
});
this.uploadingChunks.clear();
this.cancelSources.clear();
// Don't clear chunkProgress or call updateProgress() here
// to avoid progress bar jumping back. The progress will be
// recalculated when upload resumes.
}
// Resume upload
async resume(): Promise<void> {
if (this.status !== UploadSessionStatus.PAUSED) {
return;
}
this.status = UploadSessionStatus.UPLOADING;
// Clear stale progress data from cancelled chunks
this.chunkProgress.clear();
// Get missing chunks
const missingChunks: number[] = [];
for (let i = 0; i < this.totalChunks; i++) {
if (!this.uploadedChunks.has(i)) {
missingChunks.push(i);
}
}
try {
await this.uploadMissingChunks(missingChunks);
} catch (error: any) {
// Don't change status if paused during resume (status may change during async operation)
const currentStatus = this.status as UploadSessionStatus;
if (currentStatus === UploadSessionStatus.PAUSED) {
return;
}
if (!axios.isCancel(error)) {
this.status = UploadSessionStatus.ERROR;
this.onError?.(error.message || 'Upload failed');
}
this.updateProgress();
}
}
// Cancel upload
cancel(): void {
this.status = UploadSessionStatus.CANCELLED;
this.cancelSources.forEach((source) => {
source.cancel('Cancelled');
});
// Cancel session on server
if (this.session) {
const formData = new FormData();
formData.append('sessionUuid', this.session.uuid);
formData.append('spaceUuid', this.spaceUuid);
HttpUtil.httpPost(ChunkUploader.URL_CANCEL_SESSION, formData);
}
this.removeSessionFromStorage();
this.updateProgress();
}
// Save session to local storage for resume capability
private saveSessionToStorage(): void {
if (!this.session) return;
const storageKey = this.getStorageKey();
const sessions = this.getAllSessionsFromStorage();
sessions[storageKey] = {
sessionUuid: this.session.uuid,
filename: this.file.name,
fileSize: this.file.size,
puuid: this.puuid,
spaceUuid: this.spaceUuid,
privacy: this.privacy,
lastModified: this.file.lastModified,
fingerprint: this.fileFingerprint, // Store fingerprint for better matching
savedAt: Date.now(),
};
try {
localStorage.setItem(UPLOAD_SESSION_STORAGE_KEY, JSON.stringify(sessions));
} catch (e) {
console.warn('Failed to save upload session to localStorage', e);
}
}
// Load session from local storage with improved file matching
private async loadSessionFromStorage(): Promise<{ uuid: string } | null> {
const storageKey = this.getStorageKey();
const sessions = this.getAllSessionsFromStorage();
const saved = sessions[storageKey];
if (!saved) {
return null;
}
// Basic checks: size and lastModified must match
if (saved.fileSize !== this.file.size || saved.lastModified !== this.file.lastModified) {
return null;
}
// Enhanced check: if fingerprint exists in saved session, verify it matches
if (saved.fingerprint && this.fileFingerprint && saved.fingerprint !== this.fileFingerprint) {
console.warn('File fingerprint mismatch, file content may have changed');
// Remove stale session
delete sessions[storageKey];
localStorage.setItem(UPLOAD_SESSION_STORAGE_KEY, JSON.stringify(sessions));
return null;
}
return { uuid: saved.sessionUuid };
}
// Remove session from local storage
private removeSessionFromStorage(): void {
const storageKey = this.getStorageKey();
const sessions = this.getAllSessionsFromStorage();
delete sessions[storageKey];
try {
localStorage.setItem(UPLOAD_SESSION_STORAGE_KEY, JSON.stringify(sessions));
} catch (e) {
console.warn('Failed to remove upload session from localStorage', e);
}
}
// Get all sessions from storage with automatic cleanup of expired sessions
private getAllSessionsFromStorage(): Record<string, any> {
try {
const data = localStorage.getItem(UPLOAD_SESSION_STORAGE_KEY);
if (!data) return {};
const sessions = JSON.parse(data);
const now = Date.now();
let hasExpired = false;
// Clean up expired sessions
for (const key of Object.keys(sessions)) {
const session = sessions[key];
if (session.savedAt && now - session.savedAt > SESSION_MAX_AGE_MS) {
delete sessions[key];
hasExpired = true;
console.info(`Cleaned up expired upload session: ${key}`);
}
}
// Save back if any sessions were removed
if (hasExpired) {
localStorage.setItem(UPLOAD_SESSION_STORAGE_KEY, JSON.stringify(sessions));
}
return sessions;
} catch (e) {
return {};
}
}
// Generate storage key based on file properties
private getStorageKey(): string {
return `${this.spaceUuid}_${this.puuid}_${this.file.name}_${this.file.size}_${this.file.lastModified}`;
}
// Get current progress value (0-1)
getProgress(): number {
return this.uploadedChunks.size / this.totalChunks;
}
// Get current speed in bytes per second
getSpeed(): number {
return this.currentSpeed;
}
// Get uploaded size with precise calculation
getUploadedSize(): number {
let uploadedSize = 0;
// Add completed chunks
Array.from(this.uploadedChunks).forEach((chunkIndex) => {
const start = chunkIndex * this.chunkSize;
const end = Math.min(start + this.chunkSize, this.file.size);
uploadedSize += end - start;
});
// Add in-progress chunk bytes
this.chunkProgress.forEach((bytes, chunkIndex) => {
if (!this.uploadedChunks.has(chunkIndex)) {
uploadedSize += bytes;
}
});
return Math.min(uploadedSize, this.file.size);
}
// Check if upload is complete
isComplete(): boolean {
return this.status === UploadSessionStatus.COMPLETED;
}
// Check if upload is in progress
isUploading(): boolean {
return this.status === UploadSessionStatus.UPLOADING || this.status === UploadSessionStatus.MERGING;
}
// Check if upload is paused
isPaused(): boolean {
return this.status === UploadSessionStatus.PAUSED;
}
// Check if upload failed
isFailed(): boolean {
return this.status === UploadSessionStatus.ERROR;
}
// Check if upload is cancelled
isCancelled(): boolean {
return this.status === UploadSessionStatus.CANCELLED;
}
// Calculate a quick fingerprint from file's first and last bytes
// This provides better uniqueness than just size+lastModified without reading the whole file
private async calculateFileFingerprint(): Promise<string> {
const SAMPLE_SIZE = 64 * 1024; // 64KB sample from start and end
try {
// Read first 64KB
const firstChunk = this.file.slice(0, Math.min(SAMPLE_SIZE, this.file.size));
const firstBuffer = await firstChunk.arrayBuffer();
const firstView = new Uint8Array(firstBuffer);
// Read last 64KB (if file is large enough)
let lastView = new Uint8Array(0);
if (this.file.size > SAMPLE_SIZE * 2) {
const lastChunk = this.file.slice(this.file.size - SAMPLE_SIZE);
const lastBuffer = await lastChunk.arrayBuffer();
lastView = new Uint8Array(lastBuffer);
}
// Simple hash: combine samples into a fingerprint string
// Using a simple checksum approach for speed
let hash = 0;
for (let i = 0; i < firstView.length; i++) {
hash = ((hash << 5) - hash + firstView[i]) | 0;
}
for (let i = 0; i < lastView.length; i++) {
hash = ((hash << 5) - hash + lastView[i]) | 0;
}
// Convert to hex string
return (hash >>> 0).toString(16).padStart(8, '0');
} catch (e) {
console.warn('Failed to calculate file fingerprint:', e);
return '';
}
}
}
+34 -34
View File
@@ -1,13 +1,11 @@
import SafeUtil from '../../util/SafeUtil';
import BaseEntity from '../base/BaseEntity';
import Filter from "../base/filter/Filter";
import SortFilter from "../base/filter/SortFilter";
import Filter from '../base/filter/Filter';
import SortFilter from '../base/filter/SortFilter';
export default class Dashboard extends BaseEntity {
static URL_ACTIVE_IP_TOP10 = "/api/dashboard/active/ip/top10"
static URL_ETL = "/api/dashboard/etl"
static URL_ACTIVE_IP_TOP10 = '/api/dashboard/active/ip/top10';
static URL_ETL = '/api/dashboard/etl';
invokeNum: number = 0;
totalInvokeNum: number = 0;
@@ -18,49 +16,51 @@ export default class Dashboard extends BaseEntity {
fileSize: number = 0;
totalFileSize: number = 0;
avgCost: number = 0;
dt: string = "";
dt: string = '';
constructor(reactComponent?: React.Component) {
super(reactComponent);
}
assign(obj: any) {
super.assign(obj);
}
getTAG(): string {
return 'dashboard';
}
getFilters(): Filter[] {
return [
...super.getFilters(),
new SortFilter("按DT排序", "orderDt"),
]
return [...super.getFilters(), new SortFilter('按DT排序', 'orderDt')];
}
httpActiveIpTop10(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
this.httpPost(Dashboard.URL_ACTIVE_IP_TOP10, {}, function (response: any) {
SafeUtil.safeCallback(successCallback)(response.data.data);
}, errorCallback, finalCallback)
httpActiveIpTop10(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
let that = this;
this.httpPost(
Dashboard.URL_ACTIVE_IP_TOP10,
{},
function (response: any) {
SafeUtil.safeCallback(successCallback)(response.data.data);
},
errorCallback,
finalCallback
);
}
httpEtl(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
this.httpPost(Dashboard.URL_ETL, {}, function (response: any) {
SafeUtil.safeCallback(successCallback)(response.data.data);
}, errorCallback, finalCallback)
let that = this;
this.httpPost(
Dashboard.URL_ETL,
{},
function (response: any) {
SafeUtil.safeCallback(successCallback)(response.data.data);
},
errorCallback,
finalCallback
);
}
}
@@ -1,6 +1,6 @@
import BaseEntity from "../../base/BaseEntity";
import DateUtil from "../../../util/DateUtil";
import SafeUtil from "../../../util/SafeUtil";
import BaseEntity from '../../base/BaseEntity';
import DateUtil from '../../../util/DateUtil';
import SafeUtil from '../../../util/SafeUtil';
export default class DownloadToken extends BaseEntity {
userUuid: string | null = null;
@@ -8,19 +8,19 @@ export default class DownloadToken extends BaseEntity {
expireTime: string | null = null;
ip: string | null = null;
static URL_FETCH_DOWNLOAD_TOKEN = "/api/alien/fetch/download/token";
static URL_FETCH_DOWNLOAD_TOKEN = '/api/alien/fetch/download/token';
constructor(reactComponent?: React.Component) {
super(reactComponent);
}
getUrlPrefix() {
return "/api/download/token";
getTAG(): string {
return 'downloadToken';
}
assign(obj: any) {
super.assign(obj);
this.assignEntity("expireTime", Date);
this.assignEntity('expireTime', Date);
}
getFilters() {
+26 -31
View File
@@ -1,18 +1,16 @@
/**
* 全局性的国际化
*/
import BrowserUtil from "../../util/BrowserUtil";
import LangEn from "./i18n/LangEn";
import LangZh from "./i18n/LangZh";
import BrowserUtil from '../../util/BrowserUtil';
import LangEn from './i18n/LangEn';
import LangZh from './i18n/LangZh';
export default class Lang {
//当前使用的语言,默认中文
lang: string = "zh"
lang: string = 'zh';
//全局的一个store对象
private static singleton: Lang | null = null
private static singleton: Lang | null = null;
//使用懒加载模式。
static getSingleton(): Lang {
@@ -20,54 +18,51 @@ export default class Lang {
Lang.singleton = new Lang();
//读取默认的语言
let lang = BrowserUtil.browserLang()
let localLang = localStorage.getItem("_lang");
if (localLang === "zh" || localLang === "en") {
lang = localLang
let lang = BrowserUtil.browserLang();
let localLang = localStorage.getItem('_lang');
if (localLang === 'zh' || localLang === 'en') {
lang = localLang;
}
Lang.singleton.lang = lang
console.info("current lang: ", lang)
Lang.singleton.lang = lang;
console.info('current lang: ', lang);
}
return Lang.singleton
return Lang.singleton;
}
//快捷获取一种语言对应的文字。
static t(key: string, ...params: any[]): string {
let json: { [key: string]: any } = LangEn;
let json: { [key: string]: any } = LangEn
let lang = Lang.getSingleton().lang
if (lang == "zh") {
json = LangZh
let lang = Lang.getSingleton().lang;
if (lang == 'zh') {
json = LangZh;
}
let keyParts: string[] = key.split(".")
let jsonBody: any = json
let keyParts: string[] = key.split('.');
let jsonBody: any = json;
for (let i = 0; i < keyParts.length; i++) {
let part: string = keyParts[i]
jsonBody = jsonBody[part]
let part: string = keyParts[i];
jsonBody = jsonBody[part];
if (jsonBody === undefined || jsonBody === null) {
//找不到key对应的内容,直接返回key
return key
return key;
}
}
//到这里已经找到了
if (typeof jsonBody !== "string") {
return key
if (typeof jsonBody !== 'string') {
return key;
}
//依次将参数写入进去。
if (params && params.length > 0) {
for (let i = 0; i < params.length; i++) {
let param = params[i]
jsonBody = jsonBody.replace("{}", param)
let param = params[i];
jsonBody = jsonBody.replace('{}', param);
}
}
return jsonBody
return jsonBody;
}
}
+10 -11
View File
@@ -3,30 +3,29 @@
* 这个类只包含Base的子类,比如User, Preference.
* 和Sun形成姊妹篇
*/
import User from "../user/User";
import Preference from "../preference/Preference";
import Matter from "../matter/Matter";
import User from '../user/User';
import Preference from '../preference/Preference';
import Matter from '../matter/Matter';
export default class Moon {
//全局具有唯一的用户,即当前登录的用户.
user: User = new User()
user: User = new User();
//全局唯一的偏好设置
preference: Preference = new Preference()
preference: Preference = new Preference();
//全局的上传文件对象,key是space的uuid
uploadMattersMap: Record<string, Matter[]> = {};
//全局的一个store对象
private static singleton: Moon | null = null
private static singleton: Moon | null = null;
//使用懒加载模式。
static getSingleton(): Moon {
if (Moon.singleton == null) {
Moon.singleton = new Moon();
}
return Moon.singleton
return Moon.singleton;
}
}
+11 -16
View File
@@ -5,7 +5,6 @@
*/
export default class Sun {
//全局的一个store对象
static singleton: Sun | null = null;
@@ -19,25 +18,21 @@ export default class Sun {
showDrawer: boolean = false;
//全局mobile标识
isMobile: boolean = /Mobile|iP(hone|od|ad)|iOS|Android|BlackBerry/i.test(window.navigator.userAgent);
isMobile: boolean = /Mobile|iP(hone|od|ad)|iOS|Android|BlackBerry/i.test(
window.navigator.userAgent
);
//由于这个类采用单例模式,因此所有属性都是独一份的。
constructor() {
}
constructor() {}
//使用懒加载模式。
static getSingleton(): Sun {
if (Sun.singleton == null) {
Sun.singleton = new Sun();
}
return Sun.singleton
return Sun.singleton;
}
//*******************和路由相关的方法 开始 *******************//
/**
@@ -45,9 +40,9 @@ export default class Sun {
*/
static navigateTo(path: any) {
if (Sun.getSingleton().reactRouter) {
Sun.getSingleton().reactRouter.push(path)
Sun.getSingleton().reactRouter.push(path);
} else {
console.error("全局的 reactRouter 未定义,请检查代码!")
console.error('全局的 reactRouter 未定义,请检查代码!');
}
}
@@ -69,9 +64,9 @@ export default class Sun {
*/
static navigateBack() {
if (Sun.getSingleton().reactRouter) {
Sun.getSingleton().reactRouter.go(-1)
Sun.getSingleton().reactRouter.go(-1);
} else {
console.error("全局的 reactRouter 未定义,请检查代码!")
console.error('全局的 reactRouter 未定义,请检查代码!');
}
}
@@ -83,9 +78,9 @@ export default class Sun {
*/
static updateFrame() {
if (Sun.getSingleton().frameComponent) {
Sun.getSingleton().frameComponent.updateUI()
Sun.getSingleton().frameComponent.updateUI();
} else {
console.error("全局的 frameComponent 未定义,请检查代码!")
console.error('全局的 frameComponent 未定义,请检查代码!');
}
}
+408 -325
View File
@@ -1,5 +1,5 @@
let LangEn = {
eyeblueTank: "EyeblueTank",
eyeblueTank: 'EyeblueTank',
dashboard: {
totalInvokeNum: 'Total PV',
weekRate: 'Week',
@@ -21,352 +21,435 @@ let LangEn = {
reRun: 'ReRun',
},
install: {
configMysql: "Config MySQL",
port: "Port",
schema: "Schema",
username: "Username",
password: "Password",
charset: "Charset",
mysqlConnectionPass: "Connect MySQL Ok",
testMysqlConnection: "Tes MySQL Connection",
notice: "Notice",
mysqlNotice1: "If Mysql and EyeblueTank installed on the same server, Host is 127.0.0.1",
mysqlNotice2: "Your mysql account must have access to create table, or the second step will fail.",
validateMysqlFirst: "Please test the mysql connection firstly.",
preStep: "Pre Step",
nextStep: "Next Step",
createTable: "Craete Tables",
installed: "Installed",
installedButMissing: "Installed, but missing fields",
toBeInstalled: "To be installed",
allFields: "All fields",
missingFields: "Missing fields",
configMysql: 'Config Database',
dbType: 'Database Type',
port: 'Port',
schema: 'Schema',
username: 'Username',
password: 'Password',
charset: 'Charset',
mysqlConnectionPass: 'Connect MySQL Ok',
testMysqlConnection: 'Tes MySQL Connection',
notice: 'Notice',
sqliteNotice1:
'Sqlite is a tiny file database, you can use it without installation',
mysqlNotice1:
'If Mysql and EyeblueTank installed on the same server, Host is 127.0.0.1',
mysqlNotice2:
'Your mysql account must have access to create table, or the second step will fail.',
validateMysqlFirst: 'Please test the mysql connection firstly.',
preStep: 'Pre Step',
nextStep: 'Next Step',
createTable: 'Craete Tables',
installed: 'Installed',
installedButMissing: 'Installed, but missing fields',
toBeInstalled: 'To be installed',
allFields: 'All fields',
missingFields: 'Missing fields',
tableNotice: "'Create Tables' will trigger the following actions:",
tableNotice1: "If a table not exist, create it.",
tableNotice2: "If a table exist and no fields missing, nothing will do on this table.",
tableNotice3: "If a table exist but some fields is missing, it will add the missing fields.",
tableNotice4: "If a table exist and some fields not necessary, nothing will do on this table.",
oneKeyCreate: "Create Tables",
createFinish: "Finish Creating Tables",
createTableSuccess: "Create tables successfully",
tableNotice1: 'If a table not exist, create it.',
tableNotice2:
'If a table exist and no fields missing, nothing will do on this table.',
tableNotice3:
'If a table exist but some fields is missing, it will add the missing fields.',
tableNotice4:
'If a table exist and some fields not necessary, nothing will do on this table.',
oneKeyCreate: 'Create Tables',
createFinish: 'Finish Creating Tables',
createTableSuccess: 'Create tables successfully',
crateTableFirst: "Please click 'Create Tables'",
setAdministrator: "Config Administrator",
detectAdministrator: "Detect the following administrators:",
useOrCreateAdministrator: "You can validate one of them, or you can create a new one.",
validateAdministrator: "Validate administrator",
createAdministrator: "Create administrator",
administratorUsername: "username",
administratorPassword: "password",
administratorRePassword: "Enter administrator password again",
usernameRule: "EyeblueTank will use username as directory name, so only lowercase letter and number and _ is permitted.",
congratulationInstall: "Congratulations, install successfully!",
configAdminFirst: "Please config administrator first.",
createAdminSuccess: "Create administrator successfully!",
validateAdminSuccess: "Validate administrator successfully!",
pressToHome: "Click the button below to enter home。",
enterHome: "Finish And Enter Home",
finish: "Finish",
setAdministrator: 'Config Administrator',
detectAdministrator: 'Detect the following administrators:',
useOrCreateAdministrator:
'You can validate one of them, or you can create a new one.',
validateAdministrator: 'Validate administrator',
createAdministrator: 'Create administrator',
administratorUsername: 'username',
administratorPassword: 'password',
administratorRePassword: 'Enter administrator password again',
usernameRule:
'EyeblueTank will use username as directory name, so only lowercase letter and number and _ is permitted.',
congratulationInstall: 'Congratulations, install successfully!',
configAdminFirst: 'Please config administrator first.',
createAdminSuccess: 'Create administrator successfully!',
validateAdminSuccess: 'Validate administrator successfully!',
pressToHome: 'Click the button below to enter home。',
enterHome: 'Finish And Enter Home',
finish: 'Finish',
},
layout: {
allFiles: "Files",
myShare: "My Share",
bin: "Recycle bin",
setting: "Setting",
dashboard: "Dashboard",
users: "Users",
logout: "Logout",
about: "About",
install: "Install",
dragMouseUp: "Put file here~"
allFiles: 'Files',
myShare: 'My share',
bin: 'Recycle bin',
setting: 'Setting',
dashboard: 'Dashboard',
users: 'Users',
logout: 'Logout',
about: 'About',
install: 'Install',
dragMouseUp: 'Put file here~',
space: 'Shared space',
},
matter: {
file: "File",
directory: "Directory",
rename: "Rename",
download: "Download",
delete: "Delete",
hardDelete: "Hard delete",
recovery: "Recovery",
more: "More",
share: "Share",
close: "Close",
size: "Size",
preview: "Preview",
move: "Move",
moveTo: "MoveTo",
upload: "Upload",
create: "Create",
createTime: "Create Time",
updateTime: "Update Time",
deleteTime: "Deleted Time",
root: "Root",
fillInPicLink: "Fill in Picture Link",
rePick: "Re Choose",
chooseImage: "Choose Image",
uploadMode: "Upload Mode",
fillMode: "Fill Mode",
sizeExceedLimit: "File size exceed limit {}>{}",
setPublic: "Set as public",
setPrivate: "Set as private",
copyLink: "Copy Link",
enterName: "Please enter name",
publicFileEveryoneCanVisit: "Public file, anyone can access",
fileDetail: "File info",
expire: "Expire",
copyLinkAndCode: "Copy link and code",
uploaded: "Uploaded",
file: 'File',
directory: 'Directory',
rename: 'Rename',
download: 'Download',
delete: 'Delete',
hardDelete: 'Hard delete',
recovery: 'Recovery',
recoverySuccess: 'Recovery Succeed',
more: 'More',
share: 'Share',
close: 'Close',
size: 'Size',
preview: 'Preview',
move: 'Move',
moveTo: 'MoveTo',
upload: 'Upload',
create: 'Create',
createTime: 'Create Time',
updateTime: 'Update Time',
deleteTime: 'Deleted Time',
uploadUserNickname: 'Upload User',
root: 'Root',
fillInPicLink: 'Fill in Picture Link',
rePick: 'Re Choose',
chooseImage: 'Choose Image',
uploadMode: 'Upload Mode',
fillMode: 'Fill Mode',
sizeExceedLimit: 'File size exceed limit {}>{}',
dropNotDirectory: 'Cannot drag and drop folders for uploading',
setPublic: 'Set as public',
setPrivate: 'Set as private',
copyLink: 'Copy Link',
enterName: 'Please enter name',
publicFileEveryoneCanVisit: 'Public file, anyone can access',
fileDetail: 'File info',
expire: 'Expire',
copyLinkAndCode: 'Copy link and code',
uploaded: 'Uploaded',
uploadDir: 'Upload dir',
uploadInfo: 'Upload Info',
uploadErrorInfo: 'Some files failed to upload, you can export CSV files for viewing',
uploadErrorInfo:
'Some files failed to upload, you can export CSV files for viewing',
exportCSV: 'Export upload error detail',
speed: "Speed",
fileInfo: "File basic info",
fileName: "Filename",
path: "Path",
copyPath: "Copy path",
publicOrPrivate: "Public or private",
privateInfo: "Private file, only self or auth user can download",
publicInfo: "Public file, anyone can download",
downloadTimes: "Download times",
operations: "Operation",
oneTimeLink: "One time link",
oneTimeLinkInfo: "One time link will expire after downloading, click to copy",
imageCache: "Image cache",
searchFile: "Search file",
noContentYet: "No content under this directory yet",
allFiles: "All Files",
newDirectory: "New directory",
notChoose: "Not choose any file",
exceed1000: "Exceed file limit 1000",
noImageCache: "No image cache",
recycleBin: "Recycle bin",
deleted: "Deleted",
unCompatibleBrowser: "The current browser does not support it. Please try choose another one",
canIUse: "To see if the current browser supports it",
intoRecycleBin: "Recycle bin",
finishingTip: "Please wait while files are sorted..."
speed: 'Speed',
fileInfo: 'File basic info',
fileName: 'Filename',
path: 'Path',
copyPath: 'Copy path',
publicOrPrivate: 'Public or private',
privateInfo: 'Private file, only self or auth user can download',
publicInfo: 'Public file, anyone can download',
downloadTimes: 'Download times',
operations: 'Operation',
oneTimeLink: 'One time link',
oneTimeLinkInfo:
'One time link will expire after downloading, click to copy',
imageCache: 'Image cache',
searchFile: 'Search file',
noContentYet: 'No content under this directory yet',
allFiles: 'All Files',
newDirectory: 'New directory',
notChoose: 'Not choose any file',
exceed1000: 'Exceed file limit 1000',
noImageCache: 'No image cache',
recycleBin: 'Recycle bin',
deleted: 'Deleted',
unCompatibleBrowser:
'The current browser does not support it. Please try choose another one',
canIUse: 'To see if the current browser supports it',
intoRecycleBin: 'Recycle bin',
finishingTip: 'Please wait while files are sorted...',
crawl: 'Crawl File',
crawlLink: 'Link',
crawlLinkTip: 'Please enter link',
crawlFilename: 'Filename',
crawlFilenameTip: 'Please enter filename',
crawlBackground: 'Background',
crawlSuccessTip: 'Crawl successfully',
crawlDescription:
'Crawl files refers to the backend downloading directly from the specified resource link to the current directory of EyeblueTank',
// Chunked upload related
chunks: 'chunks',
chunkedUpload: 'Chunked Upload',
mergingChunks: 'Merging chunks...',
paused: 'Paused',
completed: 'Completed',
uploadFailed: 'Upload Failed',
cancelled: 'Cancelled',
pause: 'Pause',
resume: 'Resume',
cancel: 'Cancel',
// MD5 verification related
calculatingMd5: 'Calculating MD5...',
md5VerificationEnabled: 'MD5 verification enabled',
},
router: {
allFiles: "All Files",
fileDetail: "File Detail",
login: "Login",
autoLogin: "Auto Login",
register: "Register",
users: "Users",
userDetail: "User Detail",
changePassword: "Change password",
editUser: "Edit User",
createUser: "Create User",
shareDetail: "Share Detail",
myShare: "My Share",
dashboard: "Dashboard",
install: "Install",
setting: "Setting"
allFiles: 'All Files',
fileDetail: 'File Detail',
login: 'Login',
autoLogin: 'Auto Login',
register: 'Register',
users: 'Users',
userDetail: 'User Detail',
changePassword: 'Change password',
editUser: 'Edit User',
createUser: 'Create User',
shareDetail: 'Share Detail',
myShare: 'My Share',
dashboard: 'Dashboard',
install: 'Install',
setting: 'Setting',
},
preference: {
basic: "Base Info",
preview: "Preview Conf",
scan: "Scan Disk",
websiteName: "Website Name",
logo: "Logo",
logoSquare: "Logo will be cropped to square size",
onlyAllowIco: "Only .ico allowed",
copyright: "Copyright (support html)",
extraInfo: "Extra info (support html)",
zipMaxNumLimit: "Zip download max num limit",
zipMaxSizeLimit: "Zip download max size limit(B)",
current: "Current",
noLimit: "No limit",
userDefaultSizeLimit: "User default size limit(B) ",
matterBinDefaultSaveDay: "Matter bin storage days",
enterMatterBinDefaultSaveDay: "Please enter matter bin storage days!",
docLink: "Document Link",
tankDocLink: "https://tank-doc.eyeblue.cn",
allowRegister: "Allow register",
systemCleanup: "System Cleanup",
systemCleanupDescription: "This operation will cleanup everything except administrators' data",
systemCleanupPrompt: "This operation will cleanup everything except administrators' account data, please input login password.",
previewConfig: "File Preview Config",
editPreference: "Edit Preference",
editPreviewEngine: "Edit Preview engine",
enterWebsiteName: "Please enter website name!",
enterZipMaxNumLimit: "Please enter zip max num!",
enterZipMaxSizeLimit: "Please enter zip max size!",
basic: 'Base Info',
preview: 'Preview Conf',
scan: 'Scan Disk',
websiteName: 'Website Name',
logo: 'Logo',
logoSquare: 'Logo will be cropped to square size',
onlyAllowIco: 'Only .ico allowed',
copyright: 'Copyright (support html)',
extraInfo: 'Extra info (support html)',
zipMaxNumLimit: 'Zip download max num limit',
zipMaxSizeLimit: 'Zip download max size limit(B)',
current: 'Current',
noLimit: 'No limit',
userDefaultSizeLimit: 'User default size limit(B) ',
matterBinDefaultSaveDay: 'Matter bin storage days',
enterMatterBinDefaultSaveDay: 'Please enter matter bin storage days!',
docLink: 'Document Link',
tankDocLink: 'https://tank-doc.eyeblue.cn',
allowRegister: 'Allow register',
allowChunkUpload: 'Allow chunk upload',
allowChunkUploadTip:
'Chunked upload is for large files (≥100MB) with resume support. Recommended when server supports HTTP/2, otherwise concurrent connection limits may slow down uploads.',
systemCleanup: 'System Cleanup',
systemCleanupDescription:
"This operation will cleanup everything except administrators' data",
systemCleanupPrompt:
"This operation will cleanup everything except administrators' account data, please input login password.",
previewConfig: 'File Preview Config',
editPreference: 'Edit Preference',
editPreviewEngine: 'Edit Preview engine',
enterWebsiteName: 'Please enter website name!',
enterZipMaxNumLimit: 'Please enter zip max num!',
enterZipMaxSizeLimit: 'Please enter zip max size!',
enterUserDefaultSizeLimit: "Please enter user's default size!",
engine: "The number {} engine",
noEngine: "No preview engine",
newEngine: "Add a preview engine",
engineReg: "Engine format",
engineSuffix: "Engine suffix",
enginePreview: "Engine preview",
defaultPreview: "Default preview engine",
previewEngine: "Number {} preview engine",
defaultPreviewDesc: "Default preview engine, can not be removed",
engineUsageHint: "Previewing a file using the first engine matches the extentions.",
engineRegHelper: "template syntax, {url} represents the file path, the preview will automatically replace with the corresponding file url",
engineRegPlaceHolder: "eg:https://xxx.xxx.xxx?url={url}",
engineSuffixPlaceHolder: "suffix can not be null, split by comma, look like: doc,ppt,xls",
previewCurrent: "preview in current page",
previewOpen: "preview in new page",
editScan: "Edit Scan disk",
enableScan: "Enable Scan disk",
disabledScan: "Disabled Scan disk",
scanCron: "Regularly Scan disk",
cron: "Cron expression",
cronValidate: "Cron expression can not be null",
scanScope: "Scan scope",
scanUsers: "Scan users",
scanPerTenSeconds: "Scan every ten seconds",
scanPerThirtySeconds: "Scan every thirty seconds",
scanPerMinute: "Scan every minute",
scanPerHour: "Scan every hour",
scanCustom: "Scan custom",
chooseUsers: "Fuzzy search users",
chooseUsersValidate: "choose one more user",
scanLoading: "waiting...",
matterBinDefaultTip: "set zero means to turn off the recycle bin"
engine: 'The number {} engine',
noEngine: 'No preview engine',
newEngine: 'Add a preview engine',
engineReg: 'Engine format',
engineSuffix: 'Engine suffix',
enginePreview: 'Engine preview',
defaultPreview: 'Default preview engine',
previewEngine: 'Number {} preview engine',
defaultPreviewDesc: 'Default preview engine, can not be removed',
engineUsageHint:
'Previewing a file using the first engine matches the extentions.',
engineRegHelper:
'template syntax, {url} represents the file path, the preview will automatically replace with the corresponding file url',
engineRegPlaceHolder: 'eg:https://xxx.xxx.xxx?url={url}',
engineSuffixPlaceHolder:
'suffix can not be null, split by comma, look like: doc,ppt,xls',
previewCurrent: 'preview in current page',
previewOpen: 'preview in new page',
editScan: 'Edit Scan disk',
enableScan: 'Enable Scan disk',
disabledScan: 'Disabled Scan disk',
scanCron: 'Regularly Scan disk',
scanCronValidate: 'Regularly scan disk can not be null',
cron: 'Cron expression',
cronValidate: 'Cron expression can not be null',
scanScope: 'Scan scope',
scanScopeValidate: 'Scan scope can not be null',
scanSpaces: 'Scan spaces',
scanPerTenSeconds: 'Scan every ten seconds',
scanPerThirtySeconds: 'Scan every thirty seconds',
scanPerMinute: 'Scan every minute',
scanPerHour: 'Scan every hour',
scanCustom: 'Scan custom',
chooseSpaces: 'Fuzzy search spaces',
chooseSpacesValidate: 'choose one more space',
scanLoading: 'waiting...',
matterBinDefaultTip: 'set zero means to turn off the recycle bin',
scanImmediately: 'Scan immediately',
},
share: {
shareDetail: "Share Detail",
shareTime: "Share Time",
expireTime: "Expire Time",
noExpire: "Never Expire",
expired: "Expired",
copyLinkAndCode: "Copy Link And Code",
shareSuccess: "Share Successfully",
sharer: "Share Person",
link: "Link",
copyLink: "Copy Link",
code: "Code",
copyCode: "Copy Code",
copySuccess: "Copy Successfully",
more: "More",
cancelShare: "Cancel Share",
getLink: "Get Link",
allFiles: "All Files",
noContent: "No content in this directory",
enterCode: "Please enter code",
getFiles: "Get Files",
codeError: "Code Error",
cancelPrompt: "This operation will cancel sharing forever, continue?",
hour: "1 Hour",
day: "1 Day",
week: "1 Week",
month: "1 Month",
year: "1 Year",
infinity: "Forever",
emptyHint: "No Share Yet",
shareDetail: 'Share Detail',
shareTime: 'Share Time',
expireTime: 'Expire Time',
noExpire: 'Never Expire',
expired: 'Expired',
copyLinkAndCode: 'Copy Link And Code',
shareSuccess: 'Share Successfully',
sharer: 'Share Person',
link: 'Link',
copyLink: 'Copy Link',
code: 'Code',
copyCode: 'Copy Code',
copySuccess: 'Copy Successfully',
more: 'More',
cancelShare: 'Cancel Share',
getLink: 'Get Link',
allFiles: 'All Files',
noContent: 'No content in this directory',
enterCode: 'Please enter code',
getFiles: 'Get Files',
codeError: 'Code Error',
cancelPrompt: 'This operation will cancel sharing forever, continue?',
hour: '1 Hour',
day: '1 Day',
week: '1 Week',
month: '1 Month',
year: '1 Year',
infinity: 'Forever',
emptyHint: 'No Share Yet',
},
user: {
redirecting: "Redirecting...",
oldPassword: "Old Password",
newPassword: "New Password",
confirmNewPassword: "Confirm New Password",
cannotBeNull: "Cannot be null!",
passwordNotSame: "passwords above not same!",
role: "Role",
singleFileSizeLimit: "Single File Limit",
totalFileSizeLimit: "Total Space Limit",
current: "Current",
noLimit: "No Limit",
totalFileSize: "Used Space",
status: "Status",
lastLogin: "Last Login",
lastLoginIp: "Last Login Ip",
lastLoginTime: "Last Login Time",
resetPassword: "Reset Password",
transfiguration: "Transfiguration",
changePassword: "Edit Password",
enterPassword: "Please Enter Password",
enterUsername: "Please Enter Username",
enterNewPassword: "Please Enter New Password",
profile: "Profile",
avatar: "Avatar",
username: "Username",
password: "Password",
confirmPassword: "Confirm Password",
disabled: "Disabled",
disableUser: "Disable this user",
disable: "Disable",
active: "Active",
sync: "Sync",
activeUser: "Active this user",
deleteUser: "Delete this user",
deleteHint: "This action will delete {}'s all records, including files,shares,user infos etc. Continue?",
welcomeLogin: "Welcome Login",
logining: "Login...",
login: "Login",
loginSuccess: "Login Success",
toToRegister: "Go To Register",
welcomeRegister: "Welcome Register",
registering: "Login...",
register: "Register",
goToLogin: "Go To Login",
roleGuest: "Guest",
roleUser: "User",
roleAdministrator: "Administrator",
statusActive: "Ok",
statusDisabled: "Disabled",
webdavLink: "WebDAV Link",
docLink: "Document Link",
createUser: "Create User",
editUser: "Edit User",
redirecting: 'Redirecting...',
oldPassword: 'Old Password',
newPassword: 'New Password',
confirmNewPassword: 'Confirm New Password',
cannotBeNull: 'Cannot be null!',
passwordNotSame: 'passwords above not same!',
role: 'Role',
singleFileSizeLimit: 'Single File Limit',
totalFileSizeLimit: 'Total Space Limit',
current: 'Current',
noLimit: 'No Limit',
totalFileSize: 'Used Space',
status: 'Status',
lastLogin: 'Last Login',
lastLoginIp: 'Last Login Ip',
lastLoginTime: 'Last Login Time',
resetPassword: 'Reset Password',
transfiguration: 'Transfiguration',
changePassword: 'Edit Password',
enterPassword: 'Please Enter Password',
enterUsername: 'Please Enter Username',
enterNewPassword: 'Please Enter New Password',
profile: 'Profile',
avatar: 'Avatar',
username: 'Username',
password: 'Password',
confirmPassword: 'Confirm Password',
disabled: 'Disabled',
disableUser: 'Disable this user',
disable: 'Disable',
active: 'Active',
sync: 'Sync',
activeUser: 'Active this user',
deleteUser: 'Delete this user',
deleteHint:
"This action will delete {}'s all records, including files,shares,user infos etc. Continue?",
welcomeLogin: 'Welcome Login',
logining: 'Login...',
login: 'Login',
loginSuccess: 'Login Success',
toToRegister: 'Go To Register',
welcomeRegister: 'Welcome Register',
registering: 'Login...',
register: 'Register',
goToLogin: 'Go To Login',
roleGuest: 'Guest',
roleUser: 'User',
roleUserRoleSpace: 'User Space Role',
roleAdministrator: 'Administrator',
statusActive: 'Ok',
statusDisabled: 'Disabled',
webdavLink: 'WebDAV Link',
docLink: 'Document Link',
createUser: 'Create User',
editUser: 'Edit User',
editSomebodyPassword: "Edit {}'s Password",
transfigurationPromptText: "Transfiguration Prompt",
transfigurationPrompt: "You will login as this user.Please visit this link in other browser, if in current browser, you will logout.",
allUsers: "All users",
partialUsers: "Partial users",
searchUser: "Search user"
transfigurationPromptText: 'Transfiguration Prompt',
transfigurationPrompt:
'You will login as this user.Please visit this link in other browser, if in current browser, you will logout.',
searchUser: 'Search user',
},
space: {
name: 'Shared space',
nameTip: 'Please enter space name, can not be modified later',
create: 'Create space',
totalSize: 'Already use',
sizeLimit: 'Single file limit',
sizeLimitTip: 'Please enter single file limit',
totalSizeLimit: 'Total space limit',
totalSizeLimitTip: 'Please enter total space limit',
deleteHint:
'This operation will permanently delete the space. Please delete all files and members of the space first. Do you want to continue?',
memberManage: 'Space member Manage',
member: 'member',
memberTip: 'Please enter member',
memberRole: 'Member role',
memberRoleTip: 'Please select member role',
bindMember: 'Bind space member',
unBindMember: 'Unbind space member',
unBindMemberHint:
'This operation will unbind the member from the space. Do you want to continue?',
memberRoleReadonly: 'Readonly',
memberRoleReadWrite: 'Read and write',
memberRoleAdmin: 'Admin',
emptyHint: 'No Space Yet',
allSpaces: 'All Spaces',
partialSpaces: 'Partial Spaces',
bin: 'Space recycle bin',
edit: 'Edit Space',
delete: 'Delete Space',
},
model: {
usernameRule: "only lowercase letter and number and _ is permitted.",
passwordRule: "Password must have more than 6 chars",
linkCodeText: "Link:{} Code:{}",
copyLinkCodeSuccess: "Copy Link and Code successfully",
usernameRule: 'only lowercase letter and number and _ is permitted.',
passwordRule: 'Password must have more than 6 chars',
linkCodeText: 'Link:{} Code:{}',
copyLinkCodeSuccess: 'Copy Link and Code successfully',
},
plugin: {
cannotPreview: "Cannot Preview",
emptyHintDefault: "No Items",
everyPage: "Every Page",
items: "Items",
total: "Total",
clickRefresh: "Click To Refresh",
cannotPreview: 'Cannot Preview',
emptyHintDefault: 'No Items',
everyPage: 'Every Page',
items: 'Items',
total: 'Total',
clickRefresh: 'Click To Refresh',
},
loading: "Loading",
selectAll: "All",
edit: "Edit",
createTime: "Create Time",
download: "Download",
close: "Close",
required: "Required",
cancel: "Cancel",
delete: "Delete",
actionCanNotRevertConfirm: "This action cannot be reverted, confirm?",
actionDeleteConfirm: "Delete, confirm?",
actionRecoveryConfirm: "Recovery, confirm?",
prompt: "Prompt",
confirm: "Confirm",
copy: "Copy",
copySuccess: "Copy Success!",
copyError: "Copy Error!",
showMore: "Show More",
username: "Username",
password: "Password",
submit: "Submit",
save: "Save",
create: "Create",
finish: "Finish",
operationSuccess: "Operation success",
operation: "Operation",
notFound: "404 Not Found",
login: "Login",
logout: "Logout",
yes: "Yes",
no: "No",
all: "All",
refresh: "refresh",
inputRequired: "Input required",
}
loading: 'Loading',
selectAll: 'All',
edit: 'Edit',
createTime: 'Create Time',
download: 'Download',
close: 'Close',
required: 'Required',
cancel: 'Cancel',
delete: 'Delete',
actionCanNotRevertConfirm: 'This action cannot be reverted, confirm?',
actionDeleteConfirm: 'Delete, confirm?',
actionRecoveryConfirm: 'Recovery, confirm?',
prompt: 'Prompt',
confirm: 'Confirm',
copy: 'Copy',
copySuccess: 'Copy Success!',
copyError: 'Copy Error!',
showMore: 'Show More',
username: 'Username',
password: 'Password',
submit: 'Submit',
save: 'Save',
create: 'Create',
finish: 'Finish',
operationSuccess: 'Operation success',
operation: 'Operation',
notFound: '404 Not Found',
login: 'Login',
logout: 'Logout',
yes: 'Yes',
no: 'No',
all: 'All',
refresh: 'refresh',
inputRequired: 'Input required',
selectRequired: 'Select required',
more: 'More',
};
export default LangEn
export default LangEn;
+403 -329
View File
@@ -1,5 +1,5 @@
let LangZh = {
eyeblueTank: "蓝眼云盘",
eyeblueTank: '蓝眼云盘',
dashboard: {
totalInvokeNum: '总PV',
weekRate: '周环比',
@@ -21,354 +21,428 @@ let LangZh = {
reRun: '立即重跑',
},
install: {
configMysql: "配置MySQL",
port: "端口",
schema: "库名",
username: "用户名",
password: "密码",
charset: "编码",
mysqlConnectionPass: "MySQL连接测试通过",
testMysqlConnection: "测试MySQL连接",
notice: "注意",
mysqlNotice1: "如果数据库和蓝眼云盘安装在同一台服务器,Host可以直接填写 127.0.0.1。",
mysqlNotice2: "数据库账户的权限要求要能够创建表,否则第二步\"创建表\"操作会出错",
validateMysqlFirst: "请首先验证数据库连接",
preStep: "上一步",
nextStep: "下一步",
createTable: "创建表",
installed: "已安装",
installedButMissing: "已安装,字段缺失",
toBeInstalled: "待安装",
allFields: "所有字段",
missingFields: "缺失字段",
tableNotice: "点击\"一键建表\"后会按照以下逻辑执行操作:",
tableNotice1: "如果某表不存在,则直接创建表。",
tableNotice2: "如果某表存在并且字段齐全,那么不会对该表做任何操作。",
tableNotice3: "如果某表存在但是部分字段缺失,那么会在该表中增加缺失字段。",
tableNotice4: "如果表中有多余的字段(多余字段即不是蓝眼云盘需要的字段),不会做删除处理,而会维持原样。",
oneKeyCreate: "一键建表",
createFinish: "建表完成",
createTableSuccess: "建表成功",
configMysql: '配置数据库',
dbType: '数据库类型',
port: '端口',
schema: '库名',
username: '用户名',
password: '密码',
charset: '编码',
mysqlConnectionPass: 'MySQL连接测试通过',
testMysqlConnection: '测试MySQL连接',
notice: '注意',
sqliteNotice1: 'sqlite是一个轻量的文件数据库,无需安装即可使用',
mysqlNotice1:
'如果数据库和蓝眼云盘安装在同一台服务器,Host可以直接填写 127.0.0.1。',
mysqlNotice2:
'数据库账户的权限要求要能够创建表,否则第二步"创建表"操作会出错',
validateMysqlFirst: '请首先验证数据库连接',
preStep: '上一步',
nextStep: '下一步',
createTable: '创建表',
installed: '已安装',
installedButMissing: '已安装,字段缺失',
toBeInstalled: '待安装',
allFields: '所有字段',
missingFields: '缺失字段',
tableNotice: '点击"一键建表"后会按照以下逻辑执行操作:',
tableNotice1: '如果某表不存在,则直接创建表。',
tableNotice2: '如果某表存在并且字段齐全,那么不会对该表做任何操作。',
tableNotice3: '如果某表存在但是部分字段缺失,那么会在该表中增加缺失字段。',
tableNotice4:
'如果表中有多余的字段(多余字段即不是蓝眼云盘需要的字段),不会做删除处理,而会维持原样。',
oneKeyCreate: '一键建表',
createFinish: '建表完成',
createTableSuccess: '建表成功',
crateTableFirst: "请首先点击'一键建表'",
setAdministrator: "设置管理员",
detectAdministrator: "检测到系统中已经存在有以下管理员:",
useOrCreateAdministrator: "你可以使用其中一位管理员的用户名和密码进行验证,或者创建一位新的管理员账户",
validateAdministrator: "验证管理员账户",
createAdministrator: "创建管理员账户",
administratorUsername: "管理员用户名",
administratorPassword: "管理员密码",
administratorRePassword: "再次输入密码",
usernameRule: "由于用户名将作为文件上传的目录,因此只允许字母数字以及\"_\"。",
congratulationInstall: "恭喜,安装成功!",
configAdminFirst: "请首先配置管理员信息!",
createAdminSuccess: "创建管理员成功!",
validateAdminSuccess: "验证管理员成功!",
pressToHome: "点击下方按钮来完成安装过程并进入首页。",
enterHome: "完成,并进入首页",
finish: "完成",
setAdministrator: '设置管理员',
detectAdministrator: '检测到系统中已经存在有以下管理员:',
useOrCreateAdministrator:
'你可以使用其中一位管理员的用户名和密码进行验证,或者创建一位新的管理员账户',
validateAdministrator: '验证管理员账户',
createAdministrator: '创建管理员账户',
administratorUsername: '管理员用户名',
administratorPassword: '管理员密码',
administratorRePassword: '再次输入密码',
usernameRule: '由于用户名将作为文件上传的目录,因此只允许字母数字以及"_"。',
congratulationInstall: '恭喜,安装成功!',
configAdminFirst: '请首先配置管理员信息!',
createAdminSuccess: '创建管理员成功!',
validateAdminSuccess: '验证管理员成功!',
pressToHome: '点击下方按钮来完成安装过程并进入首页。',
enterHome: '完成,并进入首页',
finish: '完成',
},
layout: {
allFiles: "全部文件",
myShare: "我的分享",
bin: "回收站",
setting: "网站设置",
dashboard: "监控统计",
users: "用户管理",
logout: "退出登录",
about: "关于",
install: "安装网站",
dragMouseUp: "可以松手啦~"
allFiles: '个人文件',
myShare: '我的分享',
bin: '回收站',
setting: '网站设置',
dashboard: '监控统计',
users: '用户管理',
logout: '退出登录',
about: '关于',
install: '安装网站',
dragMouseUp: '可以松手啦~',
space: '共享空间',
},
matter: {
file: "文件",
directory: "文件夹",
rename: "重命名",
download: "下载",
delete: "删除",
hardDelete: "彻底删除",
recovery: "恢复",
more: "更多",
share: "分享",
close: "关闭",
size: "大小",
preview: "预览",
move: "移动",
moveTo: "移动到",
upload: "上传",
create: "新建",
createTime: "创建时间",
updateTime: "修改时间",
deleteTime: "删除时间",
root: "根目录",
fillInPicLink: "请填写图片链接",
rePick: "重新选择",
chooseImage: "选择图片",
uploadMode: "上传模式",
fillMode: "填写模式",
sizeExceedLimit: "文件大小超过了限制{}>{}",
setPublic: "设置为公有文件",
setPrivate: "设置为私有文件",
copyLink: "复制下载链接",
enterName: "请输入名称",
publicFileEveryoneCanVisit: "公有文件,任何人可以访问",
fileDetail: "文件详情",
expire: "有效期",
copyLinkAndCode: "复制链接+提取码",
uploaded: "已上传",
file: '文件',
directory: '文件夹',
rename: '重命名',
download: '下载',
delete: '删除',
hardDelete: '彻底删除',
recovery: '恢复',
recoverySuccess: '恢复成功',
more: '更多',
share: '分享',
close: '关闭',
size: '大小',
preview: '预览',
move: '移动',
moveTo: '移动到',
upload: '上传',
create: '新建',
createTime: '创建时间',
updateTime: '修改时间',
deleteTime: '删除时间',
uploadUserNickname: '上传者',
root: '根目录',
fillInPicLink: '请填写图片链接',
rePick: '重新选择',
chooseImage: '选择图片',
uploadMode: '上传模式',
fillMode: '填写模式',
sizeExceedLimit: '文件大小超过了限制{}>{}',
dropNotDirectory: '不可拖拽文件夹进行上传',
setPublic: '设置为公有文件',
setPrivate: '设置为私有文件',
copyLink: '复制下载链接',
enterName: '请输入名称',
publicFileEveryoneCanVisit: '公有文件,任何人可以访问',
fileDetail: '文件详情',
expire: '有效期',
copyLinkAndCode: '复制链接+提取码',
uploaded: '已上传',
uploadDir: '上传文件夹',
uploadInfo: '上传信息',
uploadErrorInfo: '部分文件上传失败,可导出csv文件查看',
exportCSV: '导出上传错误详情',
speed: "速度",
fileInfo: "文件基本信息",
fileName: "文件名",
path: "路径",
copyPath: "复制路径",
publicOrPrivate: "文件公开性",
privateInfo: "私有文件,只有自己或者授权的用户可以下载",
publicInfo: "公有文件,任何人可以通过链接下载",
downloadTimes: "下载次数",
operations: "操作",
oneTimeLink: "一次性链接",
oneTimeLinkInfo: "使用一次性链接下载后链接立即失效,可以分享这个链接给朋友,点击复制",
imageCache: "图片缓存",
searchFile: "搜索文件",
noContentYet: "该目录下暂无任何内容",
allFiles: "全部文件",
newDirectory: "新建文件夹",
notChoose: "没有选择文件",
exceed1000: "最多只能同时选取1000个文件",
noImageCache: "暂无图片缓存数据",
recycleBin: "回收站",
deleted: "已删除",
unCompatibleBrowser: "当前浏览器不支持,请切换浏览器尝试",
canIUse: "查看当前浏览器是否支持,点击跳转",
intoRecycleBin: "放入回收站",
finishingTip: "后台文件整理中,请稍候..."
speed: '速度',
fileInfo: '文件基本信息',
fileName: '文件名',
path: '路径',
copyPath: '复制路径',
publicOrPrivate: '文件公开性',
privateInfo: '私有文件,只有自己或者授权的用户可以下载',
publicInfo: '公有文件,任何人可以通过链接下载',
downloadTimes: '下载次数',
operations: '操作',
oneTimeLink: '一次性链接',
oneTimeLinkInfo:
'使用一次性链接下载后链接立即失效,可以分享这个链接给朋友,点击复制',
imageCache: '图片缓存',
searchFile: '搜索文件',
noContentYet: '该目录下暂无任何内容',
allFiles: '全部文件',
newDirectory: '新建文件夹',
notChoose: '没有选择文件',
exceed1000: '最多只能同时选取1000个文件',
noImageCache: '暂无图片缓存数据',
recycleBin: '回收站',
deleted: '已删除',
unCompatibleBrowser: '当前浏览器不支持,请切换浏览器尝试',
canIUse: '查看当前浏览器是否支持,点击跳转',
intoRecycleBin: '放入回收站',
finishingTip: '后台文件整理中,请稍候...',
crawl: '抓取文件',
crawlLink: '链接',
crawlLinkTip: '请输入链接',
crawlFilename: '文件名',
crawlFilenameTip: '请输入文件名',
crawlBackground: '后台抓取',
crawlSuccessTip: '抓取成功',
crawlDescription:
'抓取文件指后台将从指定资源链接直接下载到蓝眼云盘当前目录下',
// Chunked upload related
chunks: '分片',
chunkedUpload: '分片上传',
mergingChunks: '合并分片中...',
paused: '已暂停',
completed: '已完成',
uploadFailed: '上传失败',
cancelled: '已取消',
pause: '暂停',
resume: '继续',
cancel: '取消',
// MD5 verification related
calculatingMd5: '计算MD5校验中...',
md5VerificationEnabled: 'MD5校验已启用',
},
router: {
allFiles: "全部文件",
fileDetail: "文件详情",
login: "登录",
autoLogin: "自动登录",
register: "注册",
users: "用户列表",
userDetail: "用户详情",
changePassword: "修改密码",
editUser: "编辑用户",
createUser: "创建用户",
shareDetail: "分享详情",
myShare: "我的分享",
dashboard: "监控统计",
install: "安装网站",
setting: "网站设置"
allFiles: '全部文件',
fileDetail: '文件详情',
login: '登录',
autoLogin: '自动登录',
register: '注册',
users: '用户列表',
userDetail: '用户详情',
changePassword: '修改密码',
editUser: '编辑用户',
createUser: '创建用户',
shareDetail: '分享详情',
myShare: '我的分享',
dashboard: '监控统计',
install: '安装网站',
setting: '网站设置',
},
preference: {
basic: "基本信息",
preview: "预览引擎",
scan: "扫描磁盘",
websiteName: "网站名称",
logo: "Logo",
logoSquare: "logo请使用正方形图片,否则在显示时会裁剪成正方形",
onlyAllowIco: "只允许上传.ico图标",
copyright: "版权信息(支持html)",
extraInfo: "备案信息(支持html)",
zipMaxNumLimit: "zip下载数量限制",
zipMaxSizeLimit: "zip下载大小限制(B)",
current: "当前值",
noLimit: "无限制",
userDefaultSizeLimit: "用户默认总大小限制(B) ",
matterBinDefaultSaveDay: "回收站默认保留天数",
enterMatterBinDefaultSaveDay: "默认回收站默认保留天数必填!",
docLink: "文档链接",
tankDocLink: "https://tank-doc.eyeblue.cn/zh",
allowRegister: "允许自主注册",
systemCleanup: "重置系统",
systemCleanupDescription: "重置系统将清空除管理员账号外所有数据",
systemCleanupPrompt: "重置系统将清空除管理员账号外所有数据,事关重大,请输入登录密码",
previewConfig: "文件预览配置",
editPreference: "设置网站偏好",
editPreviewEngine: "设置预览引擎",
enterWebsiteName: "请输入网站名称!",
enterZipMaxNumLimit: "zip最大数量限制必填!",
enterZipMaxSizeLimit: "zip大小限制必填!",
enterUserDefaultSizeLimit: "默认用户空间大小必填!",
engine: "{}号预览引擎",
noEngine: "暂无在线预览引擎配置",
newEngine: "添加一个预览引擎",
engineReg: "引擎格式",
engineSuffix: "匹配后缀",
enginePreview: "预览方式",
defaultPreview: "默认引擎",
previewEngine: "{}号引擎",
defaultPreviewDesc: "默认预览引擎,不可移除",
engineRegHelper: "此处填写模板语法:{url}表示文件路径,预览时会自动替换成对应的文件url;{b64url}表示base64机密后的路径,适用于kkfileview;",
engineUsageHint: "对于一个文件的预览,使用第一个匹配到后缀名的引擎,没有配匹到则使用系统默认预览引擎",
engineRegPlaceHolder: "例如:https://xxx.xxx.xxx?url={url}",
engineSuffixPlaceHolder: '输入后缀,使用逗号分隔,不用带. 例如:doc,ppt,xls',
previewCurrent: "本站预览",
previewOpen: "新标签打开",
editScan: "设置磁盘扫描",
enableScan: "开启磁盘扫描",
disabledScan: "暂未开启磁盘扫描",
scanCron: "定时扫描",
cron: "cron表达式",
cronValidate: "cron表达式不能为空",
scanScope: "扫描范围",
scanUsers: "扫描用户",
scanPerTenSeconds: "每十秒",
scanPerThirtySeconds: "每三十秒",
scanPerMinute: "每分钟",
scanPerHour: "每小时",
scanCustom: "自定义",
chooseUsers: "模糊搜索用户",
chooseUsersValidate: "请选择至少一个用户",
scanLoading: "扫描中,请稍后...",
matterBinDefaultTip: "设置0表示关闭回收站功能"
basic: '基本信息',
preview: '预览引擎',
scan: '扫描磁盘',
websiteName: '网站名称',
logo: 'Logo',
logoSquare: 'logo请使用正方形图片,否则在显示时会裁剪成正方形',
onlyAllowIco: '只允许上传.ico图标',
copyright: '版权信息(支持html)',
extraInfo: '备案信息(支持html)',
zipMaxNumLimit: 'zip下载数量限制',
zipMaxSizeLimit: 'zip下载大小限制(B)',
current: '当前值',
noLimit: '无限制',
userDefaultSizeLimit: '用户默认总大小限制(B) ',
matterBinDefaultSaveDay: '回收站默认保留天数',
enterMatterBinDefaultSaveDay: '默认回收站默认保留天数必填!',
docLink: '文档链接',
tankDocLink: 'https://tank-doc.eyeblue.cn/zh',
allowRegister: '允许自主注册',
allowChunkUpload: '允许分片上传',
allowChunkUploadTip:
'分片上传适用于大文件(≥100MB),支持断点续传。建议在服务端支持HTTP/2协议时开启,否则可能因并发连接数限制导致上传变慢。',
systemCleanup: '重置系统',
systemCleanupDescription: '重置系统将清空除管理员账号外所有数据',
systemCleanupPrompt:
'重置系统将清空除管理员账号外所有数据,事关重大,请输入登录密码',
previewConfig: '文件预览配置',
editPreference: '设置网站偏好',
editPreviewEngine: '设置预览引擎',
enterWebsiteName: '请输入网站名称!',
enterZipMaxNumLimit: 'zip最大数量限制必填!',
enterZipMaxSizeLimit: 'zip大小限制必填!',
enterUserDefaultSizeLimit: '默认用户空间大小必填!',
engine: '{}号预览引擎',
noEngine: '暂无在线预览引擎配置',
newEngine: '添加一个预览引擎',
engineReg: '引擎格式',
engineSuffix: '匹配后缀',
enginePreview: '预览方式',
defaultPreview: '默认引擎',
previewEngine: '{}号引擎',
defaultPreviewDesc: '默认预览引擎,不可移除',
engineRegHelper:
'此处填写模板语法:{url}表示文件路径,预览时会自动替换成对应的文件url;{b64url}表示base64机密后的路径,适用于kkfileview;',
engineUsageHint:
'对于一个文件的预览,使用第一个匹配到后缀名的引擎,没有配匹到则使用系统默认预览引擎',
engineRegPlaceHolder: '例如:https://xxx.xxx.xxx?url={url}',
engineSuffixPlaceHolder:
'输入后缀,使用逗号分隔,不用带. 例如:doc,ppt,xls',
previewCurrent: '本站预览',
previewOpen: '新标签打开',
editScan: '设置磁盘扫描',
enableScan: '开启磁盘扫描',
disabledScan: '暂未开启磁盘扫描',
scanCron: '定时扫描',
scanCronValidate: '定时扫描不能为空',
cron: 'cron表达式',
cronValidate: 'cron表达式不能为空',
scanScope: '扫描范围',
scanScopeValidate: '扫描范围不能为空',
scanSpaces: '扫描空间',
scanPerTenSeconds: '每十秒',
scanPerThirtySeconds: '每三十秒',
scanPerMinute: '每分钟',
scanPerHour: '每小时',
scanCustom: '自定义',
chooseSpaces: '模糊搜索空间',
chooseSpacesValidate: '请选择至少一个空间',
scanLoading: '扫描中,请稍后...',
matterBinDefaultTip: '设置0表示关闭回收站功能',
scanImmediately: '立即扫描',
},
share: {
shareDetail: "分享详情",
shareTime: "分享时间",
expireTime: "失效时间",
noExpire: "永久有效",
expired: "已过期",
copyLinkAndCode: "复制链接+提取码",
shareSuccess: "分享成功",
sharer: "分享者",
link: "链接",
copyLink: "复制链接",
code: "提取码",
copyCode: "复制提取码",
copySuccess: "复制成功",
more: "更多",
cancelShare: "取消分享",
getLink: "获取链接",
allFiles: "全部文件",
noContent: "该目录下暂无任何内容",
enterCode: "请输入提取码",
getFiles: "提取文件",
codeError: "提取码错误",
cancelPrompt: "此操作将永久取消该分享, 是否继续?",
hour: "1小时",
day: "1天",
week: "1周",
month: "1个月",
year: "1年",
infinity: "永远有效",
emptyHint: "暂无任何分享",
shareDetail: '分享详情',
shareTime: '分享时间',
expireTime: '失效时间',
noExpire: '永久有效',
expired: '已过期',
copyLinkAndCode: '复制链接+提取码',
shareSuccess: '分享成功',
sharer: '分享者',
link: '链接',
copyLink: '复制链接',
code: '提取码',
copyCode: '复制提取码',
copySuccess: '复制成功',
more: '更多',
cancelShare: '取消分享',
getLink: '获取链接',
allFiles: '全部文件',
noContent: '该目录下暂无任何内容',
enterCode: '请输入提取码',
getFiles: '提取文件',
codeError: '提取码错误',
cancelPrompt: '此操作将永久取消该分享, 是否继续?',
hour: '1小时',
day: '1天',
week: '1周',
month: '1个月',
year: '1年',
infinity: '永远有效',
emptyHint: '暂无任何分享',
},
user: {
redirecting: "正在转跳...",
oldPassword: "旧密码",
newPassword: "新密码",
confirmNewPassword: "确认新密码",
cannotBeNull: "不能为空!",
passwordNotSame: "两次输入不一致!",
role: "角色",
singleFileSizeLimit: "单文件限制",
totalFileSizeLimit: "空间限制",
current: "当前值",
noLimit: "无限制",
totalFileSize: "已使用空间",
status: "状态",
lastLogin: "上次登录",
lastLoginIp: "上次登录IP",
lastLoginTime: "上次登录时间",
resetPassword: "重置密码",
transfiguration: "变身",
changePassword: "修改密码",
enterPassword: "输入密码",
enterUsername: "请输入用户名",
enterNewPassword: "请输入新密码",
profile: "个人详情",
avatar: "头像",
username: "用户名",
password: "密码",
confirmPassword: "确认密码",
disabled: "已禁用",
disableUser: "禁用该用户",
activeUser: "激活该用户",
deleteUser: "删除该用户",
deleteHint: "此操作将删除用户【{}】的所有记录,包括文件,分享,用户信息等内容,确定继续?",
disable: "禁用",
sync: "同步",
active: "激活",
welcomeLogin: "欢迎登录",
logining: "正在登录...",
login: "登录",
loginSuccess: "登录成功",
toToRegister: "立即注册",
welcomeRegister: "欢迎注册",
registering: "正在登录...",
register: "注册",
goToLogin: "前往登录",
roleGuest: "游客",
roleUser: "注册用户",
roleAdministrator: "管理员",
statusActive: "正常",
statusDisabled: "禁用",
webdavLink: "WebDAV 地址",
docLink: "文档链接",
createUser: "创建用户",
editUser: "编辑用户",
editSomebodyPassword: "修改{}的密码",
transfigurationPromptText: "变身提示",
transfigurationPrompt: "您将使用该用户的身份登录。请复制以下链接到其他浏览器访问,在当前浏览器访问会导致当前用户登录信息失效。",
allUsers: "全部用户",
partialUsers: "局部用户",
searchUser: "搜索用户"
redirecting: '正在转跳...',
oldPassword: '旧密码',
newPassword: '新密码',
confirmNewPassword: '确认新密码',
cannotBeNull: '不能为空!',
passwordNotSame: '两次输入不一致!',
role: '角色',
singleFileSizeLimit: '单文件限制',
totalFileSizeLimit: '空间限制',
current: '当前值',
noLimit: '无限制',
totalFileSize: '已使用空间',
status: '状态',
lastLogin: '上次登录',
lastLoginIp: '上次登录IP',
lastLoginTime: '上次登录时间',
resetPassword: '重置密码',
transfiguration: '变身',
changePassword: '修改密码',
enterPassword: '输入密码',
enterUsername: '请输入用户名',
enterNewPassword: '请输入新密码',
profile: '个人详情',
avatar: '头像',
username: '用户名',
password: '密码',
confirmPassword: '确认密码',
disabled: '已禁用',
disableUser: '禁用该用户',
activeUser: '激活该用户',
deleteUser: '删除该用户',
deleteHint:
'此操作将删除用户【{}】的所有记录,包括文件,分享,用户信息等内容,确定继续?',
disable: '禁用',
sync: '同步',
active: '激活',
welcomeLogin: '欢迎登录',
logining: '正在登录...',
login: '登录',
loginSuccess: '登录成功',
toToRegister: '立即注册',
welcomeRegister: '欢迎注册',
registering: '正在登录...',
register: '注册',
goToLogin: '前往登录',
roleGuest: '游客',
roleUser: '注册用户',
roleUserRoleSpace: '用户空间角色',
roleAdministrator: '管理员',
statusActive: '正常',
statusDisabled: '禁用',
webdavLink: 'WebDAV 地址',
docLink: '文档链接',
createUser: '创建用户',
editUser: '编辑用户',
editSomebodyPassword: '修改{}的密码',
transfigurationPromptText: '变身提示',
transfigurationPrompt:
'您将使用该用户的身份登录。请复制以下链接到其他浏览器访问,在当前浏览器访问会导致当前用户登录信息失效。',
searchUser: '搜索用户',
},
space: {
name: '共享空间',
nameTip: '请输入空间名称,不可修改',
create: '创建空间',
totalSize: '已使用',
sizeLimit: '单文件限制',
sizeLimitTip: '请输入单文件大小限制',
totalSizeLimit: '总大小限制',
totalSizeLimitTip: '请输入总大小限制',
deleteHint:
'此操作将永久删除该空间, 请先删除空间所有文件和空间成员, 是否继续?',
memberManage: '空间成员管理',
member: '空间成员',
memberTip: '请搜索选择用户',
memberRole: '成员角色',
memberRoleTip: '请选择成员角色',
bindMember: '绑定空间成员',
unBindMember: '解绑空间成员',
unBindMemberHint: '此操作将解绑该空间成员, 是否继续?',
memberRoleReadonly: '只读',
memberRoleReadWrite: '可读写',
memberRoleAdmin: '管理员',
emptyHint: '暂无任何空间',
allSpaces: '全部空间',
partialSpaces: '局部空间',
bin: '空间回收站',
edit: '编辑空间',
delete: '删除空间',
},
model: {
usernameRule: "用户名只能包含字母,数字和\"_\"",
passwordRule: "密码长度至少为6位",
linkCodeText: "链接:{} 提取码:{}",
copyLinkCodeSuccess: "复制链接提取码成功",
usernameRule: '用户名只能包含字母,数字和"_"',
passwordRule: '密码长度至少为6位',
linkCodeText: '链接:{} 提取码:{}',
copyLinkCodeSuccess: '复制链接提取码成功',
},
plugin: {
cannotPreview: "无法预览",
emptyHintDefault: "没有符合条件的项目",
everyPage: "每页",
items: "条",
total: "共",
clickRefresh: "点击刷新",
cannotPreview: '无法预览',
emptyHintDefault: '没有符合条件的项目',
everyPage: '每页',
items: '条',
total: '共',
clickRefresh: '点击刷新',
},
loading: "加载中",
selectAll: "全选",
edit: "修改",
createTime: "创建时间",
download: "下载",
close: "关闭",
required: "必填",
cancel: "取消",
delete: "删除",
deleteDirectly: "直接删除",
actionCanNotRevertConfirm: "此操作不可撤回, 是否继续?",
actionDeleteConfirm: "确定删除吗?",
actionRecoveryConfirm: "确定恢复吗?",
prompt: "提示",
confirm: "确定",
copy: "复制",
copySuccess: "复制成功!",
copyError: "复制失败!",
showMore: "显示更多",
username: "用户名",
password: "密码",
submit: "提交",
save: "保存",
create: "创建",
finish: "完成",
operationSuccess: "操作成功",
operation: "操作",
notFound: "404 页面找不到",
login: "登录",
logout: "退出",
yes: "是",
no: "否",
all: "所有",
refresh: "刷新",
inputRequired: "该项必填",
loading: '加载中',
selectAll: '全选',
edit: '修改',
createTime: '创建时间',
download: '下载',
close: '关闭',
required: '必填',
cancel: '取消',
delete: '删除',
deleteDirectly: '直接删除',
actionCanNotRevertConfirm: '此操作不可撤回, 是否继续?',
actionDeleteConfirm: '确定删除吗?',
actionRecoveryConfirm: '确定恢复吗?',
prompt: '提示',
confirm: '确定',
copy: '复制',
copySuccess: '复制成功!',
copyError: '复制失败!',
showMore: '显示更多',
username: '用户名',
password: '密码',
submit: '提交',
save: '保存',
create: '创建',
finish: '完成',
operationSuccess: '操作成功',
operation: '操作',
notFound: '404 页面找不到',
login: '登录',
logout: '退出',
yes: '是',
no: '否',
all: '所有',
refresh: '刷新',
inputRequired: '该项必填',
selectRequired: '该项必选',
more: '更多',
};
}
export default LangZh
export default LangZh;
+36 -24
View File
@@ -1,11 +1,10 @@
import BaseEntity from '../../base/BaseEntity'
import User from "../../user/User";
import Matter from "../../matter/Matter";
import InputFilter from "../../base/filter/InputFilter";
import SafeUtil from "../../../util/SafeUtil";
import BaseEntity from '../../base/BaseEntity';
import User from '../../user/User';
import Matter from '../../matter/Matter';
import InputFilter from '../../base/filter/InputFilter';
import SafeUtil from '../../../util/SafeUtil';
export default class ImageCache extends BaseEntity {
static URL_DELETE_BATCH = '/api/image/cache/delete/batch';
name: string | null = null;
@@ -18,8 +17,8 @@ export default class ImageCache extends BaseEntity {
path: number = 0;
/*
这部分是辅助UI的字段信息
*/
这部分是辅助UI的字段信息
*/
//作为勾选变量
check: boolean = false;
@@ -30,8 +29,8 @@ export default class ImageCache extends BaseEntity {
super(reactComponent);
}
getUrlPrefix() {
return "/api/image/cache"
getTAG(): string {
return 'imageCache';
}
assign(obj: any) {
@@ -41,31 +40,44 @@ export default class ImageCache extends BaseEntity {
getFilters() {
return [
...super.getFilters(),
new InputFilter("User Uuid", "userUuid", null, false),
new InputFilter("File Uuid", "matterUuid", null, false),
]
new InputFilter('User Uuid', 'userUuid', null, false),
new InputFilter('File Uuid', 'matterUuid', null, false),
];
}
getForm() {
return {
uuid: this.uuid ? this.uuid : null
}
uuid: this.uuid ? this.uuid : null,
};
}
getResizeUrl() {
return '/api/alien/preview/' + this.matterUuid + '/' + this.matterName + "?ir=" + this.mode
return (
'/api/alien/preview/' +
this.matterUuid +
'/' +
this.matterName +
'?ir=' +
this.mode
);
}
getOriginUrl() {
return '/api/alien/download/' + this.matterUuid + '/' + this.matterName
return '/api/alien/download/' + this.matterUuid + '/' + this.matterName;
}
httpDeleteBatch(uuids: string, successCallback?: () => any, errorCallback?: () => any) {
this.httpPost(ImageCache.URL_DELETE_BATCH, {'uuids': uuids}, function (response: any) {
SafeUtil.safeCallback(successCallback)(response)
}, errorCallback)
httpDeleteBatch(
uuids: string,
successCallback?: () => any,
errorCallback?: () => any
) {
this.httpPost(
ImageCache.URL_DELETE_BATCH,
{ uuids: uuids },
function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback
);
}
}
+165 -118
View File
@@ -1,66 +1,72 @@
import SafeUtil from '../../util/SafeUtil';
import BaseEntity from '../base/BaseEntity';
import User from "../user/User";
import User from '../user/User';
import InstallTableInfo from './InstallTableInfo';
import Base from '../base/Base';
export default class Install extends BaseEntity {
static URL_VERIFY = '/api/install/verify';
static URL_TABLE_INFO_LIST = '/api/install/table/info/list';
static URL_ADMIN_LIST = '/api/install/admin/list';
static URL_CREATE_TABLE = '/api/install/create/table';
static URL_CREATE_ADMIN = '/api/install/create/admin';
static URL_VALIDATE_ADMIN = '/api/install/validate/admin';
static URL_FINISH = '/api/install/finish';
static URL_VERIFY = '/api/install/verify'
static URL_TABLE_INFO_LIST = '/api/install/table/info/list'
static URL_ADMIN_LIST = '/api/install/admin/list'
static URL_CREATE_TABLE = '/api/install/create/table'
static URL_CREATE_ADMIN = '/api/install/create/admin'
static URL_VALIDATE_ADMIN = '/api/install/validate/admin'
static URL_FINISH = '/api/install/finish'
//数据库类型
dbType: string = 'mysql';
//数据库名
mysqlPort: number = 3306
mysqlHost: string = "127.0.0.1"
mysqlSchema: string = "tank"
mysqlUsername: string = "tank"
mysqlPassword: string | null = null
mysqlCharset: string = "utf8"
mysqlPort: number = 3306;
mysqlHost: string = '127.0.0.1';
mysqlSchema: string = 'tank';
mysqlUsername: string = 'tank';
mysqlPassword: string | null = null;
mysqlCharset: string = 'utf8';
//管理员用户名
adminUsername: string | null = null
adminPassword: string | null = null
adminRepassword: string | null = null
adminUsername: string | null = null;
adminPassword: string | null = null;
adminRepassword: string | null = null;
//表元信息
tableInfoList: any[] = []
tableInfoList: InstallTableInfo[] = [];
//管理员列表
adminList: User[] = []
adminList: User[] = [];
//数据库连接是否可用
verified: boolean = false
verified: boolean = false;
//管理员配置完毕
adminConfigured: boolean = false
adminConfigured: boolean = false;
constructor(reactComponent?: React.Component) {
super(reactComponent);
}
assign(obj: any) {
super.assign(obj);
}
getTAG(): string {
return 'install';
}
getMysqlForm() {
return {
dbType: this.dbType,
mysqlHost: this.mysqlHost,
mysqlPort: this.mysqlPort,
mysqlSchema: this.mysqlSchema,
mysqlUsername: this.mysqlUsername,
mysqlPassword: this.mysqlPassword,
mysqlCharset: this.mysqlCharset,
}
};
}
getForm() {
return {
dbType: this.dbType,
mysqlPort: this.mysqlPort,
mysqlHost: this.mysqlHost,
mysqlSchema: this.mysqlSchema,
@@ -69,23 +75,22 @@ export default class Install extends BaseEntity {
mysqlCharset: this.mysqlCharset,
adminUsername: this.adminUsername,
adminPassword: this.adminPassword,
}
};
}
//表创建完毕
tableCreated() {
if (!this.tableInfoList || this.tableInfoList.length === 0) {
return false
return false;
}
for (let i = 0; i < this.tableInfoList.length; i++) {
let tableInfo = this.tableInfoList[i]
let tableInfo = this.tableInfoList[i];
if (!tableInfo.tableExist) {
return false
return false;
}
if (tableInfo.tableExist && tableInfo.missingFields.length !== 0) {
return false
return false;
}
}
@@ -93,142 +98,184 @@ export default class Install extends BaseEntity {
}
httpVerify(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
let that = this;
this.httpPost(Install.URL_VERIFY, this.getMysqlForm(), function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback)
this.httpPost(
Install.URL_VERIFY,
this.getMysqlForm(),
function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback
);
}
httpTableInfoList(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
httpTableInfoList(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
let that = this;
if (!this.verified) {
this.defaultErrorHandler("Please verify mysql first", errorCallback)
return
this.defaultErrorHandler('Please verify mysql first', errorCallback);
return;
}
this.httpPost(Install.URL_TABLE_INFO_LIST, this.getForm(), function (response: any) {
this.httpPost(
Install.URL_TABLE_INFO_LIST,
this.getForm(),
function (response: any) {
that.tableInfoList.splice(0, that.tableInfoList.length);
let list = Base.renderList(response.data.data, InstallTableInfo);
that.tableInfoList.push(...list);
that.tableInfoList.splice(0, that.tableInfoList.length);
that.tableInfoList.push(...response.data.data)
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
httpCreateTable(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
httpCreateTable(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
let that = this;
if (!this.verified) {
this.defaultErrorHandler("Please verify mysql first", errorCallback)
return
this.defaultErrorHandler('Please verify mysql first', errorCallback);
return;
}
this.httpPost(Install.URL_CREATE_TABLE, this.getForm(), function (response: any) {
this.httpPost(
Install.URL_CREATE_TABLE,
this.getForm(),
function (response: any) {
that.tableInfoList.splice(0, that.tableInfoList.length);
that.tableInfoList.push(...response.data.data);
that.tableInfoList.splice(0, that.tableInfoList.length);
that.tableInfoList.push(...response.data.data)
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
//获取管理员列表
httpAdminList(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
httpAdminList(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
let that = this;
if (!this.tableCreated()) {
this.defaultErrorHandler("Please verify create table first", errorCallback)
return
this.defaultErrorHandler(
'Please verify create table first',
errorCallback
);
return;
}
let form = this.getForm();
let form = this.getForm()
this.httpPost(Install.URL_ADMIN_LIST, form, function (response: any) {
that.adminList = BaseEntity.renderList(response.data.data, User)
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
this.httpPost(
Install.URL_ADMIN_LIST,
form,
function (response: any) {
that.adminList = BaseEntity.renderList(response.data.data, User);
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
httpCreateAdmin(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
httpCreateAdmin(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
let that = this;
if (!this.tableCreated()) {
this.defaultErrorHandler("Please verify create table first", errorCallback)
return
this.defaultErrorHandler(
'Please verify create table first',
errorCallback
);
return;
}
if (this.adminPassword !== this.adminRepassword) {
this.defaultErrorHandler("password not same", errorCallback)
return
this.defaultErrorHandler('password not same', errorCallback);
return;
}
let form = this.getForm();
let form = this.getForm()
this.httpPost(Install.URL_CREATE_ADMIN, form, function (response: any) {
that.adminConfigured = true
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
this.httpPost(
Install.URL_CREATE_ADMIN,
form,
function (response: any) {
that.adminConfigured = true;
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
//验证管理员账号
httpValidateAdmin(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
httpValidateAdmin(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
let that = this;
if (!this.tableCreated()) {
this.defaultErrorHandler("Please verify create table first", errorCallback)
return
this.defaultErrorHandler(
'Please verify create table first',
errorCallback
);
return;
}
if (!this.adminUsername || !this.adminPassword) {
this.defaultErrorHandler("username and password required", errorCallback)
return
this.defaultErrorHandler('username and password required', errorCallback);
return;
}
let form = this.getForm()
let form = this.getForm();
this.httpPost(Install.URL_VALIDATE_ADMIN, form, function (response: any) {
that.adminConfigured = true
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
this.httpPost(
Install.URL_VALIDATE_ADMIN,
form,
function (response: any) {
that.adminConfigured = true;
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
//完成安装过程
httpFinish(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
let that = this;
let form = this.getForm();
let form = this.getForm()
this.httpPost(Install.URL_FINISH, form, function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
this.httpPost(
Install.URL_FINISH,
form,
function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
}
@@ -0,0 +1,10 @@
import Base from '../base/Base';
export default class InstallFieldInfo extends Base {
name: string = '';
dataType: boolean = false;
assign(obj: any) {
super.assign(obj);
}
}
@@ -0,0 +1,16 @@
import Base from '../base/Base';
import InstallFieldInfo from './InstallFieldInfo';
export default class InstallTableInfo extends Base {
name: string = '';
tableExist: boolean = false;
allFields: InstallFieldInfo[] = [];
missingFields: InstallFieldInfo[] = [];
assign(obj: any) {
super.assign(obj);
this.assignList('allFields', InstallFieldInfo);
this.assignList('missingFields', InstallFieldInfo);
}
}
+383 -161
View File
@@ -1,28 +1,35 @@
import { message } from "antd";
import axios, { CancelTokenSource } from "axios";
import BaseEntity from "../base/BaseEntity";
import Filter from "../base/filter/Filter";
import HttpUtil from "../../util/HttpUtil";
import FileUtil from "../../util/FileUtil";
import ImageUtil from "../../util/ImageUtil";
import EnvUtil from "../../util/EnvUtil";
import InputFilter from "../base/filter/InputFilter";
import CheckFilter from "../base/filter/CheckFilter";
import SortFilter from "../base/filter/SortFilter";
import MimeUtil from "../../util/MimeUtil";
import StringUtil from "../../util/StringUtil";
import NumberUtil from "../../util/NumberUtil";
import SafeUtil from "../../util/SafeUtil";
import ImagePreviewer from "../../../pages/widget/previewer/ImagePreviewer";
import MessageBoxUtil from "../../util/MessageBoxUtil";
import PreviewerHelper from "../../../pages/widget/previewer/PreviewerHelper";
import { message } from 'antd';
import axios, { CancelTokenSource } from 'axios';
import BaseEntity from '../base/BaseEntity';
import Filter from '../base/filter/Filter';
import HttpUtil from '../../util/HttpUtil';
import FileUtil from '../../util/FileUtil';
import ImageUtil from '../../util/ImageUtil';
import EnvUtil from '../../util/EnvUtil';
import InputFilter from '../base/filter/InputFilter';
import CheckFilter from '../base/filter/CheckFilter';
import SortFilter from '../base/filter/SortFilter';
import MimeUtil from '../../util/MimeUtil';
import StringUtil from '../../util/StringUtil';
import NumberUtil from '../../util/NumberUtil';
import SafeUtil from '../../util/SafeUtil';
import ImagePreviewer from '../../../pages/widget/previewer/ImagePreviewer';
import MessageBoxUtil from '../../util/MessageBoxUtil';
import PreviewerHelper from '../../../pages/widget/previewer/PreviewerHelper';
import HttpBase from '../base/HttpBase';
import User from '../user/User';
import ChunkUploader, {
UploadSessionStatus,
ChunkUploadProgress,
} from '../chunk/ChunkUploader';
import Moon from '../global/Moon';
export default class Matter extends BaseEntity {
puuid: string = "";
userUuid: string = "";
puuid: string = '';
userUuid: string = '';
dir: boolean = false;
alien: boolean = false;
name: string = "";
name: string = '';
md5: string | null = null;
size: number = 0;
privacy: boolean = true;
@@ -31,19 +38,21 @@ export default class Matter extends BaseEntity {
//上次访问时间
visitTime: Date | null = null;
parent: Matter | null = null;
user: User = new User();
// 文件是否被软删除
deleted: boolean = false;
deleteTime: Date | null = null;
spaceUuid: string | null = null;
/*
这部分是辅助UI的字段信息
*/
这部分是辅助UI的字段信息
*/
//作为勾选变量
check: boolean = false;
editMode: boolean = false;
//允许用户选择的文件类型
filter: string = "*";
filter: string = '*';
//本地字段
//给用户的提示文字
uploadHint: string | null = null;
@@ -56,26 +65,40 @@ export default class Matter extends BaseEntity {
//取消上传source
cancelSource: CancelTokenSource = axios.CancelToken.source();
static URL_MATTER_CREATE_DIRECTORY = "/api/matter/create/directory";
static URL_MATTER_SOFT_DELETE = "/api/matter/soft/delete";
static URL_MATTER_SOFT_DELETE_BATCH = "/api/matter/soft/delete/batch";
static URL_MATTER_RECOVERY = "/api/matter/recovery";
static URL_MATTER_RECOVERY_BATCH = "/api/matter/recovery/batch";
static URL_MATTER_DELETE = "/api/matter/delete";
static URL_MATTER_DELETE_BATCH = "/api/matter/delete/batch";
static URL_MATTER_RENAME = "/api/matter/rename";
static URL_CHANGE_PRIVACY = "/api/matter/change/privacy";
static URL_MATTER_MOVE = "/api/matter/move";
static URL_MATTER_DOWNLOAD = "/api/matter/download";
static URL_MATTER_UPLOAD = "/api/matter/upload";
static URL_MATTER_ZIP = "/api/matter/zip";
// Chunked upload related
chunkUploader: ChunkUploader | null = null;
// Uploaded chunks count (for UI display)
uploadedChunks: number = 0;
// Total chunks count (for UI display)
totalChunks: number = 0;
// Whether using chunked upload
useChunkedUpload: boolean = false;
// Upload status for chunked upload
uploadStatus: UploadSessionStatus = UploadSessionStatus.UPLOADING;
static MATTER_ROOT = "root";
static URL_MATTER_CREATE_DIRECTORY = '/api/matter/create/directory';
static URL_MATTER_SOFT_DELETE = '/api/matter/soft/delete';
static URL_MATTER_SOFT_DELETE_BATCH = '/api/matter/soft/delete/batch';
static URL_MATTER_RECOVERY = '/api/matter/recovery';
static URL_MATTER_RECOVERY_BATCH = '/api/matter/recovery/batch';
static URL_MATTER_DELETE = '/api/matter/delete';
static URL_MATTER_DELETE_BATCH = '/api/matter/delete/batch';
static URL_MATTER_RENAME = '/api/matter/rename';
static URL_CHANGE_PRIVACY = '/api/matter/change/privacy';
static URL_MATTER_MOVE = '/api/matter/move';
static URL_MATTER_UPLOAD = '/api/matter/upload';
static URL_MATTER_ZIP = '/api/matter/zip';
static URL_MATTER_CRAWL = '/api/matter/crawl';
static URL_MATTER_SEARCH = '/api/matter/search';
static MATTER_ROOT = 'root';
//下载zip包
static downloadZip(uuidsString: string) {
static downloadZip(uuidsString: string, spaceUuid: string) {
window.open(
EnvUtil.currentHost() + Matter.URL_MATTER_ZIP + "?uuids=" + uuidsString
`${EnvUtil.currentHost()}${
Matter.URL_MATTER_ZIP
}?uuids=${uuidsString}&spaceUuid=${spaceUuid}`
);
}
@@ -85,9 +108,14 @@ export default class Matter extends BaseEntity {
assign(obj: any) {
super.assign(obj);
this.assignEntity("parent", Matter);
this.assignEntity("visitTime", Date);
this.assignEntity("deleteTime", Date);
this.assignEntity('parent', Matter);
this.assignEntity('user', User);
this.assignEntity('visitTime', Date);
this.assignEntity('deleteTime', Date);
}
getTAG(): string {
return 'matter';
}
getForm(): any {
@@ -110,64 +138,33 @@ export default class Matter extends BaseEntity {
getFilters(): Filter[] {
return [
...super.getFilters(),
new InputFilter("父级菜单uuid", "puuid", null, false),
new InputFilter("用户", "userUuid", null, false),
new InputFilter("关键字", "name"),
new CheckFilter("文件夹", "dir"),
new CheckFilter("应用数据", "alien"),
new CheckFilter("删除", "deleted"),
new SortFilter("文件夹", "orderDir"),
new SortFilter("下载次数", "orderTimes"),
new SortFilter("大小", "orderSize"),
new SortFilter("名称", "orderName"),
new InputFilter("后缀名", "extensions"),
new InputFilter("分享uuid", "shareUuid"),
new InputFilter("提取码", "shareCode"),
new InputFilter("分享根目录", "shareRootUuid"),
new SortFilter("删除时间排序", "orderDeleteTime"),
new InputFilter('父级菜单uuid', 'puuid', null, false),
new InputFilter('用户', 'userUuid', null, false),
new InputFilter('关键字', 'name'),
new CheckFilter('文件夹', 'dir'),
new CheckFilter('应用数据', 'alien'),
new CheckFilter('删除', 'deleted'),
new SortFilter('文件夹', 'orderDir'),
new SortFilter('下载次数', 'orderTimes'),
new SortFilter('大小', 'orderSize'),
new SortFilter('名称', 'orderName'),
new InputFilter('后缀名', 'extensions'),
new InputFilter('分享uuid', 'shareUuid'),
new InputFilter('提取码', 'shareCode'),
new InputFilter('分享根目录', 'shareRootUuid'),
new SortFilter('删除时间排序', 'orderDeleteTime'),
new InputFilter('空间uuid', 'spaceUuid'),
];
}
getUrlPrefix() {
return "/api/matter";
return '/api/matter';
}
isImage() {
return FileUtil.isImage(this.name);
}
isPdf() {
return FileUtil.isPdf(this.name);
}
isText() {
return FileUtil.isText(this.name);
}
isDoc() {
return FileUtil.isDoc(this.name);
}
isPpt() {
return FileUtil.isPpt(this.name);
}
isXls() {
return FileUtil.isXls(this.name);
}
isAudio() {
return FileUtil.isAudio(this.name);
}
isVideo() {
return FileUtil.isVideo(this.name);
}
isPsd() {
return FileUtil.isPsd(this.name);
}
getIcon() {
if (FileUtil.isImage(this.name)) {
return ImageUtil.handleImageUrl(this.getPreviewUrl(), false, 100, 100);
@@ -211,14 +208,18 @@ export default class Matter extends BaseEntity {
finallyCallback?: any
) {
let that = this;
let form = { userUuid: that.userUuid, name: that.name, puuid: that.puuid };
let form = {
name: this.name,
puuid: this.puuid,
spaceUuid: this.spaceUuid,
};
return this.httpPost(
Matter.URL_MATTER_CREATE_DIRECTORY,
form,
function (response: any) {
that.assign(response.data.data);
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback,
finallyCallback
@@ -228,24 +229,25 @@ export default class Matter extends BaseEntity {
httpSoftDelete(successCallback?: any, errorCallback?: any) {
this.httpPost(
Matter.URL_MATTER_SOFT_DELETE,
{ uuid: this.uuid },
{ uuid: this.uuid, spaceUuid: this.spaceUuid },
function (response: any) {
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback
);
}
httpSoftDeleteBatch(
static httpSoftDeleteBatch(
uuids: string,
spaceUuid: string,
successCallback?: any,
errorCallback?: any
) {
this.httpPost(
new HttpBase().httpPost(
Matter.URL_MATTER_SOFT_DELETE_BATCH,
{ uuids: uuids },
{ uuids, spaceUuid },
function (response: any) {
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback
);
@@ -254,20 +256,69 @@ export default class Matter extends BaseEntity {
httpDelete(successCallback?: any, errorCallback?: any) {
this.httpPost(
Matter.URL_MATTER_DELETE,
{ uuid: this.uuid },
{ uuid: this.uuid, spaceUuid: this.spaceUuid },
function (response: any) {
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback
);
}
httpDeleteBatch(uuids: string, successCallback?: any, errorCallback?: any) {
this.httpPost(
Matter.URL_MATTER_DELETE_BATCH,
{ uuids: uuids },
static httpSearch(
params: {
puuid: string;
spaceUuid: string;
keyword?: string;
},
successCallback?: any,
errorCallback?: any,
finallyCallback?: any
) {
new HttpBase().httpGet(
Matter.URL_MATTER_SEARCH,
params,
(res: any) => {
const list = BaseEntity.renderList(res.data.data, Matter);
successCallback?.(list);
},
errorCallback,
finallyCallback
);
}
static httpCrawl(
body: {
url: string;
puuid: string;
filename: string;
spaceUuid: string;
},
successCallback?: any,
errorCallback?: any,
finallyCallback?: any
) {
new HttpBase().httpPost(
Matter.URL_MATTER_CRAWL,
body,
function (response: any) {
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback,
finallyCallback
);
}
static httpDeleteBatch(
uuids: string,
spaceUuid: string,
successCallback?: any,
errorCallback?: any
) {
new HttpBase().httpPost(
Matter.URL_MATTER_DELETE_BATCH,
{ uuids, spaceUuid },
function (response: any) {
typeof successCallback === 'function' && successCallback(response);
},
errorCallback
);
@@ -276,25 +327,73 @@ export default class Matter extends BaseEntity {
httpRecovery(successCallback?: any, errorCallback?: any) {
this.httpPost(
Matter.URL_MATTER_RECOVERY,
{ uuid: this.uuid },
{ uuid: this.uuid, spaceUuid: this.spaceUuid },
function (response: any) {
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback
);
}
httpRecoveryBatch(uuids: string, successCallback?: any, errorCallback?: any) {
this.httpPost(
static httpRecoveryBatch(
uuids: string,
spaceUuid: string,
successCallback?: any,
errorCallback?: any
) {
new HttpBase().httpPost(
Matter.URL_MATTER_RECOVERY_BATCH,
{ uuids: uuids },
{ uuids, spaceUuid },
function (response: any) {
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback
);
}
httpDetail(
successCallback?: any,
errorCallback?: any,
finallyCallback?: any
) {
let that = this;
if (!this.uuid) {
this.errorMessage = 'uuid未指定,无法获取到详情!';
this.defaultErrorHandler(this.errorMessage, errorCallback);
return;
}
this.detailLoading = true;
this.httpGet(
this.getUrlDetail(),
{
uuid: this.uuid,
spaceUuid: this.spaceUuid,
},
function (response: any) {
that.detailLoading = false;
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
},
function (response: any) {
that.detailLoading = false;
if (typeof errorCallback === 'function') {
errorCallback(that.getErrorMessage(response), response);
} else {
//没有传入错误处理的方法就采用默认处理方法:toast弹出该错误信息。
that.defaultErrorHandler(response);
}
},
finallyCallback
);
}
httpRename(
name: string,
successCallback?: any,
@@ -304,10 +403,10 @@ export default class Matter extends BaseEntity {
let that = this;
this.httpPost(
Matter.URL_MATTER_RENAME,
{ uuid: this.uuid, name: name },
{ uuid: this.uuid, spaceUuid: this.spaceUuid, name: name },
function (response: any) {
that.assign(response.data.data);
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback,
finallyCallback
@@ -322,10 +421,10 @@ export default class Matter extends BaseEntity {
let that = this;
this.httpPost(
Matter.URL_CHANGE_PRIVACY,
{ uuid: this.uuid, privacy: privacy },
{ uuid: this.uuid, privacy: privacy, spaceUuid: this.spaceUuid },
function (response: any) {
that.privacy = privacy;
if (typeof successCallback === "function") {
if (typeof successCallback === 'function') {
successCallback(response);
message.success(response.data.msg);
} else {
@@ -336,42 +435,43 @@ export default class Matter extends BaseEntity {
);
}
httpMove(
static httpMove(
spaceUuid: string,
srcUuids: string,
destUuid: string,
successCallback?: any,
errorCallback?: any
) {
let form: any = { srcUuids: srcUuids };
let form: any = { spaceUuid, srcUuids };
if (destUuid) {
form.destUuid = destUuid;
} else {
form.destUuid = "root";
form.destUuid = 'root';
}
this.httpPost(
new HttpBase().httpPost(
Matter.URL_MATTER_MOVE,
form,
function (response: any) {
typeof successCallback === "function" && successCallback(response);
typeof successCallback === 'function' && successCallback(response);
},
errorCallback
);
}
/*
以下是和上传相关的内容。
*/
以下是和上传相关的内容。
*/
//从file中装填metaData
validate() {
if (!this.file) {
this.errorMessage = "请选择上传文件";
this.errorMessage = '请选择上传文件';
return false;
}
this.name = this.file.name;
if (!this.name) {
this.errorMessage = "请选择上传文件";
this.errorMessage = '请选择上传文件';
return false;
}
@@ -384,12 +484,12 @@ export default class Matter extends BaseEntity {
//验证过滤器有没有误填写,这个方法主要给开发者使用。
validateFilter() {
let filter = this.filter;
if (filter === null || filter === "") {
this.errorMessage = "过滤器设置错误,请检查-1";
console.error("过滤器设置错误,请检查.-1");
if (filter === null || filter === '') {
this.errorMessage = '过滤器设置错误,请检查-1';
console.error('过滤器设置错误,请检查.-1');
return false;
}
if (filter !== "*") {
if (filter !== '*') {
let regex1 = /^(image|audio|video|text)(\|(image|audio|video|text))*$/g;
let regex2 = /^(\.[\w]+)(\|\.[\w]+)*$/;
// 测试几种特殊类型 image|audio|video|text
@@ -397,8 +497,8 @@ export default class Matter extends BaseEntity {
if (!regex1.test(filter)) {
//测试后缀名
if (!regex2.test(filter)) {
this.errorMessage = "过滤器设置错误,请检查-2";
console.error("过滤器设置错误,请检查.-2");
this.errorMessage = '过滤器设置错误,请检查-2';
console.error('过滤器设置错误,请检查.-2');
return false;
}
}
@@ -408,8 +508,8 @@ export default class Matter extends BaseEntity {
let privacy = this.privacy;
if (!privacy) {
if (privacy !== false) {
this.errorMessage = "privacy属性为Boolean类型";
console.error("privacy属性为Boolean类型.");
this.errorMessage = 'privacy属性为Boolean类型';
console.error('privacy属性为Boolean类型.');
return false;
}
}
@@ -420,28 +520,28 @@ export default class Matter extends BaseEntity {
//验证用户上传的文件是否符合过滤器
validateFileType() {
if (!this.filter) {
this.errorMessage = "该过滤条件有问题";
this.errorMessage = '该过滤条件有问题';
return false;
}
if (this.filter === "*") {
if (this.filter === '*') {
this.errorMessage = null;
return true;
}
let type = MimeUtil.getMimeType(this.name);
let extension = MimeUtil.getExtension(this.name);
let simpleType = type.substring(0, type.indexOf("/"));
let simpleType = type.substring(0, type.indexOf('/'));
//专门解决android微信浏览器中名字乱命名的bug.
if (StringUtil.startWith(this.name, "image%3A")) {
extension = "jpg";
simpleType = "image";
} else if (StringUtil.startWith(this.name, "video%3A")) {
extension = "mp4";
simpleType = "video";
} else if (StringUtil.startWith(this.name, "audio%3A")) {
extension = "mp3";
simpleType = "audio";
if (StringUtil.startWith(this.name, 'image%3A')) {
extension = 'jpg';
simpleType = 'image';
} else if (StringUtil.startWith(this.name, 'video%3A')) {
extension = 'mp4';
simpleType = 'video';
} else if (StringUtil.startWith(this.name, 'audio%3A')) {
extension = 'mp3';
simpleType = 'audio';
}
if (StringUtil.containStr(this.filter, extension)) {
@@ -455,7 +555,7 @@ export default class Matter extends BaseEntity {
return true;
}
}
this.errorMessage = "您上传的文件格式不符合要求";
this.errorMessage = '您上传的文件格式不符合要求';
return false;
}
@@ -475,18 +575,18 @@ export default class Matter extends BaseEntity {
//验证是否满足过滤器
if (!this.validateFileType()) {
MessageBoxUtil.error("文件类型不满足,请重试");
MessageBoxUtil.error('文件类型不满足,请重试');
return;
}
//(兼容性:chrome,ff,IE9及以上)
let formData = new FormData();
formData.append("userUuid", that.userUuid);
formData.append("puuid", that.puuid);
formData.append("file", that.file!);
formData.append("alien", that.alien.toString());
formData.append("privacy", that.privacy.toString());
formData.append('spaceUuid', that.spaceUuid!);
formData.append('puuid', that.puuid);
formData.append('file', that.file!);
formData.append('alien', that.alien.toString());
formData.append('privacy', that.privacy.toString());
//闭包
let lastTimeStamp = new Date().getTime();
@@ -544,7 +644,129 @@ export default class Matter extends BaseEntity {
}
cancelUpload() {
this.cancelSource.cancel();
if (this.chunkUploader) {
this.chunkUploader.cancel();
this.loading = false;
this.uploadStatus = UploadSessionStatus.CANCELLED;
this.updateUI();
} else {
this.cancelSource.cancel();
}
}
// Pause chunked upload
pauseUpload() {
if (this.chunkUploader && this.uploadStatus === UploadSessionStatus.UPLOADING) {
this.chunkUploader.pause();
this.uploadStatus = UploadSessionStatus.PAUSED;
// Force UI update to show resume button
if (this.reactComponent) {
this.reactComponent.setState({ _forceUpdate: Date.now() });
}
}
}
// Resume chunked upload
resumeUpload() {
if (this.chunkUploader && this.uploadStatus === UploadSessionStatus.PAUSED) {
this.uploadStatus = UploadSessionStatus.UPLOADING;
this.chunkUploader.resume();
// Force UI update to show pause button
if (this.reactComponent) {
this.reactComponent.setState({ _forceUpdate: Date.now() });
}
}
}
// Check if using chunked upload and is paused
canResume(): boolean {
return this.useChunkedUpload && this.uploadStatus === UploadSessionStatus.PAUSED;
}
// Check if using chunked upload and is uploading (not merging, as merging cannot be paused)
canPause(): boolean {
return this.useChunkedUpload && this.uploadStatus === UploadSessionStatus.UPLOADING;
}
// Smart upload: use chunked upload for large files, regular upload for small files
httpSmartUpload(successCallback?: any, failureCallback?: any) {
if (!this.file) {
this.errorMessage = '请选择上传文件';
return;
}
// Check if chunk upload is enabled in preference and file is large enough
const allowChunkUpload = Moon.getSingleton().preference.allowChunkUpload;
if (allowChunkUpload && ChunkUploader.shouldUseChunkedUpload(this.file.size)) {
this.httpChunkedUpload(successCallback, failureCallback);
} else {
this.httpUpload(successCallback, failureCallback);
}
}
// Chunked upload for large files
// MD5 verification is always enabled when spark-md5 is available
httpChunkedUpload(successCallback?: any, failureCallback?: any) {
let that = this;
// Validate
if (!this.validate()) {
return;
}
if (!this.validateFilter()) {
return;
}
if (!this.validateFileType()) {
MessageBoxUtil.error('文件类型不满足,请重试');
return;
}
this.useChunkedUpload = true;
this.loading = true;
this.uploadStatus = UploadSessionStatus.UPLOADING;
// Create chunk uploader
this.chunkUploader = new ChunkUploader(
this.file!,
this.puuid,
this.spaceUuid!,
this.privacy,
undefined,
this.reactComponent!
);
// Set callbacks
this.chunkUploader.onProgress = (progress: ChunkUploadProgress) => {
that.progress = progress.progress;
that.speed = progress.speed;
that.uploadedChunks = progress.uploadedChunks;
that.totalChunks = progress.totalChunks;
// Only update status if not manually paused (avoid race condition with progress events)
if (that.uploadStatus !== UploadSessionStatus.PAUSED) {
that.uploadStatus = progress.status;
}
that.updateUI();
};
this.chunkUploader.onComplete = (matter: any) => {
that.uuid = matter.uuid;
that.loading = false;
that.uploadStatus = UploadSessionStatus.COMPLETED;
SafeUtil.safeCallback(successCallback)({ data: { data: matter } });
};
this.chunkUploader.onError = (error: string) => {
that.errorMessage = error;
that.loading = false;
that.uploadStatus = UploadSessionStatus.ERROR;
that.defaultErrorHandler(error);
SafeUtil.safeCallback(failureCallback)(error);
};
// Start upload
this.chunkUploader.start();
}
//清除文件
@@ -561,13 +783,13 @@ export default class Matter extends BaseEntity {
getPreviewUrl(downloadTokenUuid?: string): string {
return `${EnvUtil.currentHost()}/api/alien/preview/${this.uuid}/${
this.name
}${downloadTokenUuid ? "?downloadTokenUuid=" + downloadTokenUuid : ""}`;
}${downloadTokenUuid ? '?downloadTokenUuid=' + downloadTokenUuid : ''}`;
}
getDownloadUrl(downloadTokenUuid?: string): string {
return `${EnvUtil.currentHost()}/api/alien/download/${this.uuid}/${
this.name
}${downloadTokenUuid ? "?downloadTokenUuid=" + downloadTokenUuid : ""}`;
}${downloadTokenUuid ? '?downloadTokenUuid=' + downloadTokenUuid : ''}`;
}
getShareDownloadUrl(
@@ -575,9 +797,9 @@ export default class Matter extends BaseEntity {
shareCode: string,
shareRootUuid: string
): string {
return `${EnvUtil.currentHost()}/api/alien/download/${this.uuid}/${
this.name
}?shareUuid=${shareUuid}&shareCode=${shareCode}&shareRootUuid=${shareRootUuid}`;
return `${EnvUtil.currentHost()}/api/share/matter/download?matterUuid=${
this.uuid
}&shareUuid=${shareUuid}&shareCode=${shareCode}&shareRootUuid=${shareRootUuid}`;
}
getSharePreviewUrl(
@@ -585,8 +807,8 @@ export default class Matter extends BaseEntity {
shareCode: string,
shareRootUuid: string
): string {
return `${EnvUtil.currentHost()}/api/alien/preview/${this.uuid}/${
this.name
}?shareUuid=${shareUuid}&shareCode=${shareCode}&shareRootUuid=${shareRootUuid}`;
return `${EnvUtil.currentHost()}/api/share/matter/preview?matterUuid=${
this.uuid
}&shareUuid=${shareUuid}&shareCode=${shareCode}&shareRootUuid=${shareRootUuid}`;
}
}
+112 -80
View File
@@ -1,69 +1,69 @@
import SafeUtil from '../../util/SafeUtil';
import BaseEntity from '../base/BaseEntity';
import PreviewConfig from "./model/PreviewConfig";
import JsonUtil from "../../util/JsonUtil";
import ScanConfig from "./model/ScanConfig";
import PreviewConfig from './model/PreviewConfig';
import JsonUtil from '../../util/JsonUtil';
import ScanConfig from './model/ScanConfig';
export default class Preference extends BaseEntity {
//获取当前登录者的信息
static URL_API_PREFERENCE_FETCH = '/api/preference/fetch'
static URL_API_SYSTEM_CLEANUP = '/api/preference/system/cleanup'
static URL_API_PREFERENCE_EDIT_PREVIEW_CONFIG = '/api/preference/edit/preview/config'
static URL_API_PREFERENCE_EDIT_SCAN = '/api/preference/edit/scan/config'
static URL_API_PREFERENCE_SCAN_ONCE = '/api/preference/scan/once'
static URL_API_PREFERENCE_FETCH = '/api/preference/fetch';
static URL_API_SYSTEM_CLEANUP = '/api/preference/system/cleanup';
static URL_API_PREFERENCE_EDIT_PREVIEW_CONFIG =
'/api/preference/edit/preview/config';
static URL_API_PREFERENCE_EDIT_SCAN = '/api/preference/edit/scan/config';
static URL_API_PREFERENCE_SCAN_ONCE = '/api/preference/scan/once';
//网站名称
name: string = ""
name: string = '';
//logo
logoUrl: string | null = null
faviconUrl: string | null = null
logoUrl: string | null = null;
faviconUrl: string | null = null;
//版权信息
copyright: string = ""
record: string = ""
copyright: string = '';
record: string = '';
//大小限制
downloadDirMaxSize: number = -1
downloadDirMaxSize: number = -1;
//文件数量
downloadDirMaxNum: number = -1
downloadDirMaxNum: number = -1;
//用户默认总大小限制
defaultTotalSizeLimit: number = -1
defaultTotalSizeLimit: number = -1;
//回收站保存文件天数
deletedKeepDays: number = 7
deletedKeepDays: number = 7;
//是否允许自主注册
allowRegister: boolean = false
allowRegister: boolean = false;
//是否允许分片上传
allowChunkUpload: boolean = false;
//预览配置
previewConfig: PreviewConfig = new PreviewConfig()
previewConfig: PreviewConfig = new PreviewConfig();
//扫描磁盘配置
scanConfig: ScanConfig = new ScanConfig()
scanConfig: ScanConfig = new ScanConfig();
//后台版本
version: string | null = null
version: string | null = null;
//*****************前端辅助变量***********************/
//网站是否完成了安装
installed: boolean = true
installed: boolean = true;
constructor(reactComponent?: React.Component) {
super(reactComponent);
}
assign(obj: any) {
super.assign(obj);
this.assignEntity("previewConfig", PreviewConfig)
this.assignEntity("scanConfig", ScanConfig)
this.assignEntity('previewConfig', PreviewConfig);
this.assignEntity('scanConfig', ScanConfig);
}
getTAG(): string {
return 'preference';
}
getRecycleBinStatus() {
return this.deletedKeepDays > 0
return this.deletedKeepDays > 0;
}
getForm(): any {
@@ -78,82 +78,114 @@ export default class Preference extends BaseEntity {
defaultTotalSizeLimit: this.defaultTotalSizeLimit,
deletedKeepDays: this.deletedKeepDays,
allowRegister: this.allowRegister,
allowChunkUpload: this.allowChunkUpload,
previewConfig: JsonUtil.toJson(this.previewConfig.getForm()),
scanConfig: JsonUtil.toJson(this.scanConfig.getForm())
scanConfig: JsonUtil.toJson(this.scanConfig.getForm()),
};
}
//修改title和favicon
updateTitleAndFavicon() {
if (this.faviconUrl) {
//修改favicon
let link: any = document.querySelector("link[rel*='icon']") || document.createElement('link');
let link: any =
document.querySelector("link[rel*='icon']") ||
document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = this.faviconUrl;
document.getElementsByTagName('head')[0].appendChild(link);
}
document.title = this.name
document.title = this.name;
}
httpFetch(successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
this.httpPost(Preference.URL_API_PREFERENCE_FETCH, {}, function (response: any) {
let that = this;
this.httpPost(
Preference.URL_API_PREFERENCE_FETCH,
{},
function (response: any) {
that.assign(response.data.data);
that.assign(response.data.data);
that.updateTitleAndFavicon();
that.updateTitleAndFavicon()
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
httpSystemCleanup(password: string, successCallback?: any, errorCallback?: any, finalCallback?: any) {
let that = this
this.httpPost(Preference.URL_API_SYSTEM_CLEANUP, {password}, function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
httpSystemCleanup(
password: string,
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
let that = this;
this.httpPost(
Preference.URL_API_SYSTEM_CLEANUP,
{ password },
function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
httpSavePreviewEngine(successCallback?: any, errorCallback?: any, finalCallback?: any) {
const that = this
this.httpPost(Preference.URL_API_PREFERENCE_EDIT_PREVIEW_CONFIG, this.getForm(), function(response: any) {
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback);
httpSavePreviewEngine(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
const that = this;
this.httpPost(
Preference.URL_API_PREFERENCE_EDIT_PREVIEW_CONFIG,
this.getForm(),
function (response: any) {
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
httpSaveScan(successCallback?: any, errorCallback?: any, finalCallback?: any) {
const that = this
this.httpPost(Preference.URL_API_PREFERENCE_EDIT_SCAN, this.getForm(), function(response: any) {
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback);
httpSaveScan(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
const that = this;
this.httpPost(
Preference.URL_API_PREFERENCE_EDIT_SCAN,
this.getForm(),
function (response: any) {
that.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
httpScanOnce(successCallback?: any, errorCallback?: any, finalCallback?: any) {
this.httpGet(Preference.URL_API_PREFERENCE_SCAN_ONCE, {}, function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
}, errorCallback, finalCallback)
httpScanOnce(
successCallback?: any,
errorCallback?: any,
finalCallback?: any
) {
this.httpGet(
Preference.URL_API_PREFERENCE_SCAN_ONCE,
{},
function (response: any) {
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback,
finalCallback
);
}
}
@@ -2,48 +2,37 @@
* 预览配置
* 支持自定义不同的文件预览类型
*/
import Base from "../../base/Base";
import PreviewEngine from "./PreviewEngine";
import MimeUtil from "../../../util/MimeUtil";
import MessageBoxUtil from "../../../util/MessageBoxUtil";
import Base from '../../base/Base';
import PreviewEngine from './PreviewEngine';
/**
* 预览配置
*/
export default class PreviewConfig extends Base {
/**
* 预览引擎
*/
previewEngines: PreviewEngine[] = []
previewEngines: PreviewEngine[] = [];
constructor() {
super()
super();
}
assign(obj: any) {
super.assign(obj);
this.assignList("previewEngines", PreviewEngine)
this.assignList('previewEngines', PreviewEngine);
}
getForm(): any {
let forms = this.previewEngines.map((item: PreviewEngine, index: number) => {
return item.getForm()
})
let forms = this.previewEngines.map(
(item: PreviewEngine, index: number) => {
return item.getForm();
}
);
return {
previewEngines: forms
previewEngines: forms,
};
}
}
@@ -2,24 +2,24 @@
* 预览配置
* 支持自定义不同的文件预览类型
*/
import Base from "../../base/Base";
import MimeUtil from "../../../util/MimeUtil";
import Base from '../../base/Base';
import MimeUtil from '../../../util/MimeUtil';
export default class PreviewEngine extends Base {
/**
* 此预览引擎的url
* {originUrl} 文件原始的url地址。eg: https://tank.eyeblue.cn/api/alien/download/2a0ceee1-744c-4c82-4215-69c382597a50/abstract-free-photo-2210x1473.jpg
* {url} 对于公有文件publicUrl=originUrl,对于私有文件publicUrl是originUrl带上downloadToken。 eg: https://tank.eyeblue.cn/api/alien/download/2a0ceee1-744c-4c82-4215-69c382597a50/abstract-free-photo-2210x1473.jpg?downloadTokenUuid=6bdba52f-af6b-49ae-5a5d-fd80bfb01d3b
* {originUrl} 文件原始的url地址。eg: https://tanker.eyeblue.cn/api/alien/download/2a0ceee1-744c-4c82-4215-69c382597a50/abstract-free-photo-2210x1473.jpg
* {url} 对于公有文件publicUrl=originUrl,对于私有文件publicUrl是originUrl带上downloadToken。 eg: https://tanker.eyeblue.cn/api/alien/download/2a0ceee1-744c-4c82-4215-69c382597a50/abstract-free-photo-2210x1473.jpg?downloadTokenUuid=6bdba52f-af6b-49ae-5a5d-fd80bfb01d3b
* {b64Url} 文件原始地址
*/
url: string = "";
url: string = '';
/**
* 此预览引擎支持的文件后缀名。
* 星好*表示匹配所有的后缀名。
* 逗号分隔,不带. 例如: xls,doc,ppt,xlsx,docx,pptx
*/
extensions: string = "";
extensions: string = '';
/**
* 本站预览,新标签打开
@@ -45,15 +45,15 @@ export default class PreviewEngine extends Base {
//添加office默认预览引擎,使用微软开放接口
let officeEngine: PreviewEngine = new PreviewEngine();
officeEngine.url =
"https://view.officeapps.live.com/op/embed.aspx?src={url}";
officeEngine.extensions = "doc,ppt,xls,docx,pptx,xlsx";
'https://view.officeapps.live.com/op/embed.aspx?src={url}';
officeEngine.extensions = 'doc,ppt,xls,docx,pptx,xlsx';
officeEngine.previewInSite = true;
defaultEngines.push(officeEngine);
//添加全局预览引擎,使用浏览器预览能力。
let globalEngine: PreviewEngine = new PreviewEngine();
globalEngine.url = "{originUrl}";
globalEngine.extensions = "*";
globalEngine.url = '{originUrl}';
globalEngine.extensions = '*';
globalEngine.previewInSite = true;
defaultEngines.push(globalEngine);
@@ -68,14 +68,14 @@ export default class PreviewEngine extends Base {
}
let canHandle = false;
if (this.extensions === "*") {
if (this.extensions === '*') {
canHandle = true;
} else {
let parts: string[] = this.extensions.split(",");
let parts: string[] = this.extensions.split(',');
for (let part of parts) {
if (
extension === part.toLowerCase() ||
"." + extension === part.toLowerCase()
'.' + extension === part.toLowerCase()
) {
canHandle = true;
break;
@@ -2,9 +2,9 @@
* 扫描配置
*/
import { LabeledValue } from 'antd/lib/select';
import Base from "../../base/Base";
import { ScanScopeType } from "./ScanScopeType";
import { ScanCronType } from "./ScanCronType";
import Base from '../../base/Base';
import { ScanScopeType } from './ScanScopeType';
import { ScanCronType } from './ScanCronType';
export default class ScanConfig extends Base {
// 是否开启
@@ -13,13 +13,11 @@ export default class ScanConfig extends Base {
cron: string | null = null;
// 扫描磁盘开启范围(全部用户 or 局部用户)
scope: ScanScopeType = ScanScopeType.ALL;
// 开启扫描磁盘功能的用户们
usernames: string[] = [];
// 开启扫描磁盘功能的空间
spaceNames: string[] = [];
// 前端辅助字段
cronType: string | null = null;
users: LabeledValue[] = [];
constructor() {
super();
@@ -34,7 +32,7 @@ export default class ScanConfig extends Base {
enable: this.enable,
scope: this.scope,
cron: this.cronType === ScanCronType.CUSTOM ? this.cron : this.cronType,
usernames: this.usernames,
spaceNames: this.spaceNames,
};
}
}
@@ -1,40 +1,41 @@
import SelectionOption from "../../base/option/SelectionOption";
import Lang from "../../global/Lang";
import SelectionOption from '../../base/option/SelectionOption';
import Lang from '../../global/Lang';
enum ScanCronType {
TEN_SECONDS = "TEN_SECONDS",
THIRTY_SECONDS = "THIRTY_SECONDS",
MINUTE = "MINUTE",
HOUR = "HOUR",
CUSTOM = "CUSTOM",
TEN_SECONDS = 'TEN_SECONDS',
THIRTY_SECONDS = 'THIRTY_SECONDS',
MINUTE = 'MINUTE',
HOUR = 'HOUR',
CUSTOM = 'CUSTOM',
}
const ScanCronTypes: ScanCronType[] = Object.keys(ScanCronType).map(
(k) => k as ScanCronType
);
const ScanCronTypeMap: { [key in keyof typeof ScanCronType]: SelectionOption } = {
TEN_SECONDS: {
name: Lang.t("preference.scanPerTenSeconds"),
value: "@every 10s",
},
THIRTY_SECONDS: {
name: Lang.t("preference.scanPerThirtySeconds"),
value: "@every 30s",
},
MINUTE: {
name: Lang.t("preference.scanPerMinute"),
value: "@every 1m",
},
HOUR: {
name: Lang.t("preference.scanPerHour"),
value: "0 * * * *",
},
CUSTOM: {
name: Lang.t("preference.scanCustom"),
value: "CUSTOM",
},
};
const ScanCronTypeMap: { [key in keyof typeof ScanCronType]: SelectionOption } =
{
TEN_SECONDS: {
name: Lang.t('preference.scanPerTenSeconds'),
value: '@every 10s',
},
THIRTY_SECONDS: {
name: Lang.t('preference.scanPerThirtySeconds'),
value: '@every 30s',
},
MINUTE: {
name: Lang.t('preference.scanPerMinute'),
value: '@every 1m',
},
HOUR: {
name: Lang.t('preference.scanPerHour'),
value: '0 * * * *',
},
CUSTOM: {
name: Lang.t('preference.scanCustom'),
value: 'CUSTOM',
},
};
const ScanCronTypeList: SelectionOption[] = [];
ScanCronTypes.forEach((type: ScanCronType) => {
@@ -46,4 +47,9 @@ ScanCronTypes.forEach((type: ScanCronType) => {
ScanCronTypeValueList.push(ScanCronTypeMap[type].value);
});
export { ScanCronType, ScanCronTypeMap, ScanCronTypeList, ScanCronTypeValueList };
export {
ScanCronType,
ScanCronTypeMap,
ScanCronTypeList,
ScanCronTypeValueList,
};
@@ -1,23 +1,25 @@
import SelectionOption from "../../base/option/SelectionOption";
import Lang from "../../global/Lang";
import SelectionOption from '../../base/option/SelectionOption';
import Lang from '../../global/Lang';
enum ScanScopeType {
ALL = "ALL",
CUSTOM = "CUSTOM",
ALL = 'ALL',
CUSTOM = 'CUSTOM',
}
const ScanScopeTypes: ScanScopeType[] = Object.keys(ScanScopeType).map(
(k) => k as ScanScopeType
);
const ScanScopeTypeMap: { [key in keyof typeof ScanScopeType]: SelectionOption } = {
const ScanScopeTypeMap: {
[key in keyof typeof ScanScopeType]: SelectionOption;
} = {
ALL: {
name: Lang.t("user.allUsers"),
value: "ALL",
name: Lang.t('space.allSpaces'),
value: 'ALL',
},
CUSTOM: {
name: Lang.t("user.partialUsers"),
value: "CUSTOM",
name: Lang.t('space.partialSpaces'),
value: 'CUSTOM',
},
};
+26 -26
View File
@@ -1,12 +1,12 @@
import BaseEntity from "../base/BaseEntity";
import Matter from "../matter/Matter";
import FileUtil from "../../util/FileUtil";
import EnvUtil from "../../util/EnvUtil";
import { ShareType } from "./ShareType";
import { ShareExpireOption, ShareExpireOptionMap } from "./ShareExpireOption";
import DateUtil from "../../util/DateUtil";
import SafeUtil from "../../util/SafeUtil";
import MessageBoxUtil from "../../util/MessageBoxUtil";
import BaseEntity from '../base/BaseEntity';
import Matter from '../matter/Matter';
import FileUtil from '../../util/FileUtil';
import EnvUtil from '../../util/EnvUtil';
import { ShareType } from './ShareType';
import { ShareExpireOption, ShareExpireOptionMap } from './ShareExpireOption';
import DateUtil from '../../util/DateUtil';
import SafeUtil from '../../util/SafeUtil';
import MessageBoxUtil from '../../util/MessageBoxUtil';
export default class Share extends BaseEntity {
name: string | null = null;
@@ -22,17 +22,16 @@ export default class Share extends BaseEntity {
dirMatter: Matter = new Matter();
//当前share对应的matters
matters: Matter[] = [];
//当前分享正在查看的根目录matter的uuid。前端辅助字段。
rootUuid: string = Matter.MATTER_ROOT;
spaceUuid: string | null = null;
//本地临时字段
expireOption: ShareExpireOption = ShareExpireOption.MONTH;
static URL_CREATE = "/api/share/create";
static URL_BROWSE = "/api/share/browse";
static URL_DELETE_BATCH = "/api/share/delete/batch";
static URL_ZIP = "/api/share/zip";
static URL_CREATE = '/api/share/create';
static URL_BROWSE = '/api/share/browse';
static URL_DELETE_BATCH = '/api/share/delete/batch';
static URL_ZIP = '/api/share/zip';
static URL_MATTER_PAGE = '/api/share/matter/page';
constructor(reactComponent?: React.Component) {
super(reactComponent);
@@ -40,13 +39,13 @@ export default class Share extends BaseEntity {
assign(obj: any) {
super.assign(obj);
super.assignEntity("expireTime", Date);
super.assignEntity("dirMatter", Matter);
super.assignList("matters", Matter);
super.assignEntity('expireTime', Date);
super.assignEntity('dirMatter', Matter);
super.assignList('matters', Matter);
}
getUrlPrefix() {
return "/api/share";
getTAG(): string {
return 'share';
}
getFilters() {
@@ -62,7 +61,7 @@ export default class Share extends BaseEntity {
getIcon() {
if (this.shareType === ShareType.MIX) {
return require("../../../assets/image/file/archive.svg");
return FileUtil.getIcon('zip', false);
} else {
return FileUtil.getIcon(
this.name,
@@ -72,7 +71,7 @@ export default class Share extends BaseEntity {
}
getLink() {
return EnvUtil.currentHost() + "/share/detail/" + this.uuid;
return EnvUtil.currentHost() + '/share/detail/' + this.uuid;
}
hasExpired() {
@@ -95,11 +94,11 @@ export default class Share extends BaseEntity {
}
//下载zip包
downloadZip(puuid?: string) {
downloadZip(rootUuid: string, puuid?: string) {
window.open(
`${EnvUtil.currentHost()}${Share.URL_ZIP}?shareUuid=${this.uuid}&code=${
this.code
}&puuid=${puuid}&rootUuid=${this.rootUuid}`
}&puuid=${puuid}&rootUuid=${rootUuid}`
);
}
@@ -112,7 +111,8 @@ export default class Share extends BaseEntity {
let that = this;
const form = {
matterUuids: matterUuids,
matterUuids,
spaceUuid: this.spaceUuid,
expireInfinity: this.expireOption === ShareExpireOption.INFINITY,
expireTime: DateUtil.simpleDateTime(this.getExpireTime()),
};
+19 -19
View File
@@ -1,12 +1,12 @@
import SelectionOption from "../base/option/SelectionOption";
import SelectionOption from '../base/option/SelectionOption';
enum ShareExpireOption {
HOUR = "HOUR",
DAY = "DAY",
WEEK = "WEEK",
MONTH = "MONTH",
YEAR = "YEAR",
INFINITY = "INFINITY",
HOUR = 'HOUR',
DAY = 'DAY',
WEEK = 'WEEK',
MONTH = 'MONTH',
YEAR = 'YEAR',
INFINITY = 'INFINITY',
}
interface ShareExpireSelection extends SelectionOption {
@@ -21,33 +21,33 @@ const ShareExpireOptionMap: {
[key in keyof typeof ShareExpireOption]: ShareExpireSelection;
} = {
HOUR: {
name: "share.hour",
value: "HOUR",
name: 'share.hour',
value: 'HOUR',
deltaMillisecond: 60 * 60 * 1000,
},
DAY: {
name: "share.day",
value: "DAY",
name: 'share.day',
value: 'DAY',
deltaMillisecond: 24 * 60 * 60 * 1000,
},
WEEK: {
name: "share.week",
value: "WEEK",
name: 'share.week',
value: 'WEEK',
deltaMillisecond: 7 * 24 * 60 * 60 * 1000,
},
MONTH: {
name: "share.month",
value: "MONTH",
name: 'share.month',
value: 'MONTH',
deltaMillisecond: 30 * 24 * 60 * 60 * 1000,
},
YEAR: {
name: "share.year",
value: "YEAR",
name: 'share.year',
value: 'YEAR',
deltaMillisecond: 365 * 24 * 60 * 60 * 1000,
},
INFINITY: {
name: "share.infinity",
value: "INFINITY",
name: 'share.infinity',
value: 'INFINITY',
deltaMillisecond: 0,
},
};
+10 -10
View File
@@ -1,9 +1,9 @@
import SelectionOption from "../base/option/SelectionOption";
import SelectionOption from '../base/option/SelectionOption';
enum ShareType {
FILE = "FILE",
DIRECTORY = "DIRECTORY",
MIX = "MIX",
FILE = 'FILE',
DIRECTORY = 'DIRECTORY',
MIX = 'MIX',
}
const ShareTypes: ShareType[] = Object.keys(ShareType).map(
@@ -12,16 +12,16 @@ const ShareTypes: ShareType[] = Object.keys(ShareType).map(
const ShareTypeMap: { [key in keyof typeof ShareType]: SelectionOption } = {
FILE: {
name: "文件",
value: "FILE",
name: '文件',
value: 'FILE',
},
DIRECTORY: {
name: "文件夹",
value: "DIRECTORY",
name: '文件夹',
value: 'DIRECTORY',
},
MIX: {
name: "混合",
value: "MIX",
name: '混合',
value: 'MIX',
},
};
+71
View File
@@ -0,0 +1,71 @@
import BaseEntity from '../base/BaseEntity';
import User from '../user/User';
import FileUtil from '../../util/FileUtil';
import Filter from '../base/filter/Filter';
import InputFilter from '../base/filter/InputFilter';
export interface SpaceFormValues {
name: string;
sizeLimit: number;
totalSizeLimit: number;
uuid: string | null;
}
export default class Space extends BaseEntity {
name: string | null = null;
totalSize: number = 0;
sizeLimit: number = 0;
totalSizeLimit: number = 0;
userUuid: string | null = null;
user: User | null = null;
type: 'PRIVATE' | 'SHARED' | null = null;
constructor(reactComponent?: React.Component) {
super(reactComponent);
}
getTAG(): string {
return 'space';
}
getFilters(): Filter[] {
return [
...super.getFilters(),
new InputFilter('空间名称', 'name'),
new InputFilter('空间类型', 'type'),
];
}
assign(obj: any) {
super.assign(obj);
this.assignEntity('user', User);
}
getForm(): SpaceFormValues {
return {
name: this.name!,
sizeLimit: this.sizeLimit,
totalSizeLimit: this.totalSizeLimit,
uuid: this.uuid ? this.uuid : null,
};
}
getHumanizeTotalSize() {
return FileUtil.humanFileSize(this.totalSize);
}
getHumanizeSizeLimit() {
return FileUtil.humanFileSize(this.sizeLimit);
}
getHumanizeTotalSizeLimit() {
return FileUtil.humanFileSize(this.totalSizeLimit);
}
getUsedPercent() {
if (this.totalSizeLimit === 0) {
return 0;
}
return (this.totalSize / this.totalSizeLimit) * 100;
}
}
@@ -0,0 +1,79 @@
import BaseEntity from '../../base/BaseEntity';
import User from '../../user/User';
import { SpaceMemberRole } from './SpaceMemberRole';
import Filter from '../../base/filter/Filter';
import InputFilter from '../../base/filter/InputFilter';
import SafeUtil from '../../../util/SafeUtil';
export default class SpaceMember extends BaseEntity {
spaceUuid: string | null = null;
userUuid: string | null = null;
role: SpaceMemberRole | null = null;
user: User | null = null;
static URL_SPACE_MEMBER_MINE = '/api/space/member/mine';
constructor(reactComponent?: React.Component) {
super(reactComponent);
}
getTAG(): string {
return 'spaceMember';
}
assign(obj: any) {
super.assign(obj);
this.assignEntity('user', User);
}
getFilters(): Filter[] {
return [
...super.getFilters(),
new InputFilter('空间ID', 'spaceUuid', '按空间ID搜索', false),
];
}
getForm() {
return {
spaceUuid: this.spaceUuid!,
userUuid: this.userUuid!,
role: this.role!,
uuid: this.uuid ? this.uuid : null,
};
}
httpCreate(
body: {
spaceUuid: string;
userUuids: string;
role: SpaceMemberRole;
},
successCallback?: any
) {
this.httpPost(this.getUrlCreate(), body, successCallback);
}
httpEdit(
body: {
uuid: string;
spaceUuid: string;
userUuid: string;
role: SpaceMemberRole;
},
successCallback?: any
) {
this.httpPost(this.getUrlEdit(), body, successCallback);
}
httpMine(spaceUuid: string, successCallback?: any, errorCallback?: any) {
this.httpGet(
SpaceMember.URL_SPACE_MEMBER_MINE,
{ spaceUuid },
(response: any) => {
this.assign(response.data.data);
SafeUtil.safeCallback(successCallback)(response);
},
errorCallback
);
}
}
@@ -0,0 +1,43 @@
import Lang from '../../global/Lang';
import ColorSelectionOption from '../../base/option/ColorSelectionOption';
import Color from '../../base/option/Color';
enum SpaceMemberRole {
READ_ONLY = 'READ_ONLY',
READ_WRITE = 'READ_WRITE',
ADMIN = 'ADMIN',
}
const SpaceMemberRoles: SpaceMemberRole[] = Object.keys(SpaceMemberRole).map(
(k) => k as SpaceMemberRole
);
const SpaceMemberRoleMap: Record<SpaceMemberRole, ColorSelectionOption> = {
[SpaceMemberRole.READ_ONLY]: {
name: Lang.t('space.memberRoleReadonly'),
value: SpaceMemberRole.READ_ONLY,
color: Color.SUCCESS,
},
[SpaceMemberRole.READ_WRITE]: {
name: Lang.t('space.memberRoleReadWrite'),
value: SpaceMemberRole.READ_WRITE,
color: Color.PRIMARY,
},
[SpaceMemberRole.ADMIN]: {
name: Lang.t('space.memberRoleAdmin'),
value: SpaceMemberRole.ADMIN,
color: Color.DANGER,
},
};
const SpaceMemberRoleList: ColorSelectionOption[] = [];
SpaceMemberRoles.forEach((type: SpaceMemberRole, index: number) => {
SpaceMemberRoleList.push(SpaceMemberRoleMap[type]);
});
export {
SpaceMemberRole,
SpaceMemberRoles,
SpaceMemberRoleMap,
SpaceMemberRoleList,
};

Some files were not shown because too many files have changed in this diff Show More