├── . gitattributes ├── README.md ├── react ├── .env ├── .gitignore ├── README.md ├── color.js ├── config-overrides.js ├── package.json ├── public │ ├── color.less │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── assets │ │ ├── iconfont │ │ │ ├── demo.css │ │ │ ├── demo_index.html │ │ │ ├── iconfont.css │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.js │ │ │ ├── iconfont.svg │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ └── iconfont.woff2 │ │ └── images │ │ │ ├── antd.svg │ │ │ ├── bg1.jpg │ │ │ ├── bg2.jpg │ │ │ ├── bg3.jpg │ │ │ ├── dark.svg │ │ │ └── light.svg │ ├── components │ │ ├── AnimatedBook │ │ │ ├── index.js │ │ │ └── style.less │ │ ├── Background │ │ │ └── index.js │ │ ├── ColorPicker │ │ │ └── index.js │ │ ├── Loading │ │ │ ├── index.js │ │ │ └── style.css │ │ ├── MyIcon │ │ │ └── index.js │ │ ├── Phone │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── PrivateRoute │ │ │ └── index.js │ │ ├── PromptBox │ │ │ └── index.js │ │ └── Typing │ │ │ └── index.js │ ├── config │ │ └── secret.js │ ├── index.css │ ├── index.js │ ├── pages │ │ ├── About │ │ │ └── index.js │ │ ├── ButtonDemo │ │ │ └── index.js │ │ ├── Chat │ │ │ ├── imgs │ │ │ │ ├── administrator.png │ │ │ │ ├── header1.png │ │ │ │ ├── header2.png │ │ │ │ ├── react.png │ │ │ │ └── zanwu.png │ │ │ ├── index.js │ │ │ └── style.less │ │ ├── Collection │ │ │ ├── CreateModal.js │ │ │ ├── index.js │ │ │ └── style.less │ │ ├── FeedbackDemo │ │ │ └── index.js │ │ ├── IconDemo │ │ │ ├── icons.js │ │ │ ├── index.js │ │ │ └── style.less │ │ ├── Index │ │ │ ├── EditInfoModal.js │ │ │ ├── EditPasswordModal.js │ │ │ ├── MyContent.js │ │ │ ├── MyHeader.js │ │ │ ├── MySider.js │ │ │ ├── index.js │ │ │ └── style.less │ │ ├── Login │ │ │ ├── LoginForm.js │ │ │ ├── RegisterForm.js │ │ │ ├── example.html │ │ │ ├── index.js │ │ │ └── style.less │ │ ├── MessageBoard │ │ │ ├── Score.js │ │ │ ├── index.js │ │ │ └── style.less │ │ ├── MobileUI │ │ │ ├── config.js │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── Users │ │ │ ├── CreateUserModal.js │ │ │ ├── InfoModal.js │ │ │ └── index.js │ │ └── tabs.js │ ├── serviceWorker.js │ ├── store │ │ ├── actions.js │ │ ├── index.js │ │ └── reducers.js │ ├── styles │ │ ├── main.less │ │ └── vars.less │ └── utils │ │ ├── LoadableComponent.js │ │ ├── ajax.js │ │ ├── asyncComponent.js │ │ ├── history.js │ │ ├── session.js │ │ └── util.js └── yarn.lock └── server ├── .gitignore ├── app.js ├── bin └── www ├── chat.js ├── config ├── db.js └── secret.js ├── controller ├── chat.js ├── message.js ├── score.js ├── user.js └── works.js ├── db └── mysql.js ├── middlewares └── errorHandle.js ├── model └── resModel.js ├── package.json ├── public ├── images │ ├── avatar.png │ ├── bg1.jpg │ ├── bg2.jpg │ ├── bg3.jpg │ ├── default.png │ ├── login_bg1.jpg │ ├── login_bg2.jpg │ └── login_bg3.jpg ├── stylesheets │ └── style.css └── upload-files │ ├── chat │ ├── 01.jpeg │ ├── 1590399528000-ZZrRkP.jpeg │ └── c6e0629a36c94106be63f8fc72dd977a.jpeg │ └── myUpload │ ├── 01.jpeg │ ├── 217ad66f504747ca9b613036d3dcd453.jpg │ ├── 67be71bd1a20486289f45005773386f2.jpg │ ├── ece8c6fae0012978195c0ff364bc4c81.jpg │ └── 浪子康,杨洪才 - Xun(易硕成)-可惜我不是她(咚鼓版)(浪子康 / 杨洪才 remix).mp3 ├── routes ├── index.js ├── message.js ├── score.js ├── user.js └── works.js ├── sql └── admin.sql ├── utils ├── upload.js └── util.js ├── views ├── error.pug ├── index.pug └── layout.pug └── yarn.lock /. gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=JavaScript 2 | *.css linguist-language=JavaScript 3 | *.html linguist-language=JavaScript -------------------------------------------------------------------------------- /react/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_BASE_URL = http://localhost:8888 -------------------------------------------------------------------------------- /react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | #忽略vscode的配置 9 | jsconfig.json 10 | 11 | # testing 12 | /coverage 13 | 14 | # production 15 | /build 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /react/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /react/color.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { generateTheme, getLessVars } = require('antd-theme-generator'); 3 | 4 | const options = { 5 | stylesDir: path.join(__dirname, './src/styles'), 6 | antDir: path.join(__dirname, './node_modules/antd'), 7 | varFile: path.join(__dirname, './src/styles/vars.less'), 8 | mainLessFile: path.join(__dirname, './src/styles/main.less'), 9 | themeVariables: [ 10 | '@primary-color', 11 | ], 12 | indexFileName: 'index.html', 13 | outputFilePath: path.join(__dirname, './public/color.less'), 14 | } 15 | 16 | generateTheme(options).then(less => { 17 | console.log('Theme generated successfully'); 18 | }) 19 | .catch(error => { 20 | console.log('Error', error); 21 | }); 22 | -------------------------------------------------------------------------------- /react/config-overrides.js: -------------------------------------------------------------------------------- 1 | /* config-overrides.js */ 2 | const path = require('path'); 3 | const { injectBabelPlugin } = require('react-app-rewired'); 4 | const rewireLess = require('react-app-rewire-less'); 5 | const { getLessVars } = require('antd-theme-generator'); 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '.', dir) 9 | } 10 | 11 | module.exports = function override(config, env) { 12 | //do stuff with the webpack config... 13 | 14 | //按需加载 15 | config = injectBabelPlugin( 16 | ['import', { libraryName: 'antd', style: true }], 17 | config 18 | ); 19 | 20 | //配置antd主题 21 | config = rewireLess.withLoaderOptions({ 22 | modifyVars: getLessVars(path.join(__dirname, './src/styles/vars.less')), 23 | javascriptEnabled: true 24 | })(config, env); 25 | 26 | //配置别名 27 | config.resolve.alias = { 28 | '@': resolve('src') 29 | } 30 | 31 | config.devtool = false; // 关掉 sourceMap 32 | 33 | //启用ES7的修改器语法(babel 7) 34 | config = injectBabelPlugin(['@babel/plugin-proposal-decorators', { "legacy": true }], config) 35 | 36 | return config; 37 | } 38 | -------------------------------------------------------------------------------- /react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "t", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "antd": "^3.20.0", 7 | "antd-theme-generator": "^1.1.6", 8 | "babel-plugin-import": "^1.11.0", 9 | "braft-editor": "^2.3.7", 10 | "braft-extensions": "^0.0.18", 11 | "crypto-js": "^3.1.9-1", 12 | "draft-js-prism": "^1.0.6", 13 | "gsap": "^2.1.2", 14 | "prismjs": "^1.16.0", 15 | "react": "^16.8.6", 16 | "react-app-rewire-less": "^2.1.3", 17 | "react-app-rewired": "2.0.2-next.0", 18 | "react-color": "^2.17.3", 19 | "react-dom": "^16.8.6", 20 | "react-loadable": "^5.5.0", 21 | "react-redux": "^7.1.0", 22 | "react-router-dom": "^5.0.0", 23 | "react-scripts": "3.0.0", 24 | "react-virtualized": "^9.21.2", 25 | "redux": "^4.0.1", 26 | "redux-thunk": "^2.3.0", 27 | "screenfull": "^4.2.0" 28 | }, 29 | "scripts": { 30 | "start": "npm run color && react-app-rewired start", 31 | "build": "react-app-rewired build", 32 | "color": "node color.js", 33 | "test": "react-app-rewired test", 34 | "eject": "react-scripts eject" 35 | }, 36 | "eslintConfig": { 37 | "extends": "react-app" 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.2%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | }, 51 | "devDependencies": { 52 | "@babel/plugin-proposal-decorators": "^7.4.4" 53 | }, 54 | "homepage": "." 55 | } 56 | -------------------------------------------------------------------------------- /react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/public/favicon.ico -------------------------------------------------------------------------------- /react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | 后台管理系统 23 | 24 | 25 | 26 | 32 | 33 | 34 |
35 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /react/src/App.css: -------------------------------------------------------------------------------- 1 | .markdown { 2 | color: #314659; 3 | font-size: 14px; 4 | line-height: 2; 5 | } 6 | 7 | .markdown ol { 8 | list-style: circle inside; 9 | } 10 | 11 | .markdown code { 12 | margin: 0 1px; 13 | padding: .2em .4em; 14 | font-size: .9em; 15 | background: #f2f4f5; 16 | border: 1px solid #eee; 17 | border-radius: 3px; 18 | } 19 | 20 | .markdown h2, 21 | .markdown h3, 22 | .markdown h4, 23 | .markdown h5, 24 | .markdown h6 { 25 | clear: both; 26 | /* margin: 1.6em 0 .6em; */ 27 | color: #0d1a26; 28 | font-weight: 500; 29 | font-family: Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', sans-serif; 30 | } 31 | 32 | .markdown h3 { 33 | font-size: 18px; 34 | } 35 | 36 | .primary-color { 37 | color: var(--primaryColor); 38 | } 39 | 40 | .my-a { 41 | color: var(--primaryColor); 42 | cursor: pointer; 43 | } 44 | 45 | .ellipsis { 46 | overflow: hidden; 47 | text-overflow: ellipsis; 48 | white-space: nowrap; 49 | } -------------------------------------------------------------------------------- /react/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './App.css' 3 | import './index.css' 4 | import { Switch, Route, withRouter } from 'react-router-dom' 5 | import PrivateRoute from './components/PrivateRoute' 6 | import './assets/iconfont/iconfont.css' 7 | import LoadableComponent from '@/utils/LoadableComponent' 8 | 9 | const Index = LoadableComponent(import('./pages/Index')) 10 | const Login = LoadableComponent(import('./pages/Login')) 11 | 12 | @withRouter 13 | class App extends React.Component { 14 | render() { 15 | return ( 16 | 17 | 18 | 19 | 20 | ) 21 | } 22 | } 23 | 24 | export default App 25 | -------------------------------------------------------------------------------- /react/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /react/src/assets/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1562140191837'); /* IE9 */ 3 | src: url('iconfont.eot?t=1562140191837#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), 5 | url('iconfont.woff?t=1562140191837') format('woff'), 6 | url('iconfont.ttf?t=1562140191837') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1562140191837#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-fenlei:before { 19 | content: "\e60a"; 20 | } 21 | 22 | .icon-fenlei1:before { 23 | content: "\e611"; 24 | } 25 | 26 | .icon-shouye:before { 27 | content: "\e612"; 28 | } 29 | 30 | .icon-commentoutline:before { 31 | content: "\e778"; 32 | } 33 | 34 | .icon-suo:before { 35 | content: "\e625"; 36 | } 37 | 38 | .icon-shouye1:before { 39 | content: "\e60b"; 40 | } 41 | 42 | .icon-user:before { 43 | content: "\e743"; 44 | } 45 | 46 | .icon-emiyanzhengma:before { 47 | content: "\e61b"; 48 | } 49 | 50 | .icon-user1:before { 51 | content: "\e600"; 52 | } 53 | 54 | .icon-User:before { 55 | content: "\e67b"; 56 | } 57 | 58 | .icon-lajitong1:before { 59 | content: "\e610"; 60 | } 61 | 62 | .icon-yanzhengmatianchong:before { 63 | content: "\e636"; 64 | } 65 | 66 | .icon-suo1:before { 67 | content: "\e644"; 68 | } 69 | 70 | .icon-securityCode-b:before { 71 | content: "\e60d"; 72 | } 73 | 74 | .icon-comment:before { 75 | content: "\e728"; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /react/src/assets/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/assets/iconfont/iconfont.eot -------------------------------------------------------------------------------- /react/src/assets/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/assets/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /react/src/assets/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/assets/iconfont/iconfont.woff -------------------------------------------------------------------------------- /react/src/assets/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/assets/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /react/src/assets/images/antd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 28 Copy 5 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 42 | 43 | -------------------------------------------------------------------------------- /react/src/assets/images/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/assets/images/bg1.jpg -------------------------------------------------------------------------------- /react/src/assets/images/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/assets/images/bg2.jpg -------------------------------------------------------------------------------- /react/src/assets/images/bg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/assets/images/bg3.jpg -------------------------------------------------------------------------------- /react/src/assets/images/dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react/src/assets/images/light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react/src/components/AnimatedBook/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './style.less' 3 | import PropTypes from 'prop-types'; 4 | 5 | class AnimatedBooks extends React.Component { 6 | static propTypes = { 7 | content: PropTypes.any, //内容 8 | cover: PropTypes.any, //封面 9 | } 10 | static defaultProps = { 11 | content: '', 12 | cover: '' 13 | } 14 | 15 | render() { 16 | const { cover, content, className = '', style = {} } = this.props 17 | return ( 18 |
19 |
20 | {/* 封面 */} 21 |
    22 |
  • {cover}
  • 23 |
  • 24 |
25 | {/* 书页 */} 26 |
    27 |
  • 28 |
  • {content}
  • 29 |
  • 30 |
  • 31 |
  • 32 |
33 | {/* 背面 */} 34 |
    35 |
  • 36 |
  • 37 |
38 |
39 |
40 | ) 41 | } 42 | } 43 | 44 | export default AnimatedBooks -------------------------------------------------------------------------------- /react/src/components/AnimatedBook/style.less: -------------------------------------------------------------------------------- 1 | .book-container { 2 | padding-left: 200px; 3 | 4 | .book { 5 | position: relative; 6 | width: 160px; 7 | height: 220px; 8 | perspective: 1000px; 9 | transform-style: preserve-3d; 10 | 11 | &:hover { 12 | .hardcover_front { 13 | transform: rotateY(-145deg) translateZ(0); 14 | z-index: 0; 15 | } 16 | 17 | .page { 18 | &>li:nth-child(1) { 19 | transform: rotateY(-30deg); 20 | transition-duration: 1.5s; 21 | } 22 | 23 | &>li:nth-child(2) { 24 | transform: rotateY(-35deg); 25 | transition-duration: 1.8s; 26 | } 27 | 28 | &>li:nth-child(3) { 29 | transform: rotateY(-118deg); 30 | transition-duration: 1.6s; 31 | } 32 | 33 | &>li:nth-child(4) { 34 | transform: rotateY(-130deg); 35 | transition-duration: 1.4s; 36 | } 37 | 38 | &>li:nth-child(5) { 39 | transform: rotateY(-140deg); 40 | transition-duration: 1.2s; 41 | } 42 | } 43 | 44 | } 45 | 46 | //公共样式 47 | li::before, 48 | li::after { 49 | content: ""; 50 | } 51 | ul{ 52 | margin-bottom: 0; 53 | } 54 | 55 | .hardcover_front, 56 | .hardcover_back, 57 | .book_spine, 58 | .hardcover_front>li, 59 | .hardcover_back>li, 60 | .book_spine>li, 61 | .page>li { 62 | position: absolute; 63 | top: 0; 64 | left: 0; 65 | width: 100%; 66 | height: 100%; 67 | transform-style: preserve-3d; 68 | } 69 | 70 | .hardcover_front>li:first-child:after, 71 | .hardcover_front>li:first-child:before, 72 | .hardcover_front>li:last-child:after, 73 | .hardcover_front>li:last-child:before, 74 | .hardcover_back>li:first-child:after, 75 | .hardcover_back>li:first-child:before, 76 | .hardcover_back>li:last-child:after, 77 | .hardcover_back>li:last-child:before, 78 | .book_spine>li:first-child:after, 79 | .book_spine>li:first-child:before, 80 | .book_spine>li:last-child:after, 81 | .book_spine>li:last-child:before { 82 | position: absolute; 83 | top: 0; 84 | left: 0; 85 | } 86 | } 87 | 88 | // 封面 89 | .hardcover_front { 90 | transition: all 0.8s ease; 91 | transform-origin: 0% 100%; 92 | transform: rotateY(-34deg) translateZ(8px); 93 | z-index: 100; 94 | 95 | &>li:first-child { 96 | transform: translateZ(2px); 97 | background-color: #eee; 98 | 99 | &::before, 100 | &::after { 101 | width: 3px; 102 | height: 100%; 103 | background: #999; 104 | } 105 | 106 | &::before { 107 | transform: rotateY(90deg) translateZ(-2px) translateX(2px); 108 | } 109 | 110 | &::after { 111 | transform: rotateY(90deg) translateZ(158px) translateX(2px); 112 | } 113 | } 114 | 115 | &>li:last-child { 116 | transform: rotateY(180deg) translateZ(2px); 117 | background: #fffbec; 118 | 119 | &::before { 120 | box-shadow: 0px 0px 30px 5px #333; 121 | transform: rotateX(90deg) rotateZ(90deg) translateZ(-140px) translateX(-2px) translateY(-78px); 122 | width: 4px; 123 | height: 160px; 124 | } 125 | } 126 | } 127 | 128 | // 书页 129 | .page { 130 | position: relative; 131 | width: 100%; 132 | height: 98%; 133 | top: 1%; 134 | left: 3%; 135 | z-index: 10; 136 | transform-style: preserve-3d; //父元素加了这个属性,子元素尽管用position属性设置z-index也不管用 137 | 138 | &>li { 139 | transform-origin: left center; 140 | transition-property: transform; 141 | transition-timing-function: ease; 142 | border-radius: 0px 5px 5px 0px; 143 | background: -webkit-linear-gradient(left, #e1ddd8 0%, #fffbf6 100%); 144 | box-shadow: inset 0px -1px 2px rgba(50, 50, 50, 0.1), inset -1px 0px 1px rgba(150, 150, 150, 0.2); 145 | } 146 | 147 | &>li:nth-child(1) { 148 | transition-duration: 0.6s; 149 | transform: rotateY(-28deg); 150 | } 151 | 152 | &>li:nth-child(2) { 153 | transition-duration: 0.6s; 154 | transform: rotateY(-30deg); 155 | } 156 | 157 | &>li:nth-child(3) { 158 | transition-duration: 0.4s; 159 | transform: rotateY(-32deg); 160 | } 161 | 162 | &>li:nth-child(4) { 163 | transition-duration: 0.5s; 164 | transform: rotateY(-34deg); 165 | } 166 | 167 | &>li:nth-child(5) { 168 | transition-duration: 0.6s; 169 | transform: rotateY(-36deg); 170 | } 171 | 172 | } 173 | 174 | // 背面 175 | .hardcover_back { 176 | transform-origin: 0% 100%; 177 | transform: rotateY(-15deg) translateZ(-8px); 178 | 179 | li:first-child { 180 | transform: translateZ(2px); 181 | background: #fffbec; 182 | 183 | &::before, 184 | &::after { 185 | width: 4px; 186 | height: 100%; 187 | background: #999; 188 | } 189 | 190 | &::before { 191 | transform: rotateY(90deg) translateZ(-2px) translateX(2px); 192 | } 193 | 194 | &::after { 195 | transform: rotateY(90deg) translateZ(158px) translateX(2px); 196 | } 197 | } 198 | 199 | li:last-child { 200 | transform: translateZ(-2px); 201 | background: #fffbec; 202 | 203 | &::before, 204 | &::after { 205 | width: 4px; 206 | height: 160px; //书的宽度 207 | background: #999; 208 | } 209 | 210 | &::before { 211 | transform: rotateX(90deg) rotateZ(90deg) translateZ(-140px) translateX(2px) translateY(-78px); 212 | box-shadow: 10px -1px 80px 20px #666; 213 | } 214 | } 215 | } 216 | } -------------------------------------------------------------------------------- /react/src/components/Background/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { TweenLite, Circ } from "gsap/all"; 4 | import { throttle } from '@/utils/util' 5 | import Loading from '@/components/Loading' 6 | 7 | class Background extends React.Component { 8 | static propTypes = { 9 | url: PropTypes.string 10 | } 11 | static defaultProps = { 12 | url: 'https://github.com/zhangZhiHao1996/image-store/blob/master/react-admin-master/bg1.jpg?raw=true', 13 | } 14 | 15 | constructor(props) { 16 | super(props) 17 | this.points = [] //背景粒子 18 | this.width = window.innerWidth 19 | this.height = window.innerHeight 20 | this.canvas = null 21 | this.ctx = null 22 | this.target = {} //当前鼠标在屏幕的位置 23 | 24 | this.state = { 25 | loading: false //背景图太大,所以加一个loading 26 | } 27 | } 28 | componentDidMount() { 29 | this.setState({ 30 | loading: true 31 | }) 32 | //当图片载入完成后再显示背景 33 | this.loadImageAsync(this.props.url).then(() => { 34 | this.setState({ 35 | loading: false 36 | }) 37 | }).then(() => { 38 | this.canvas && this.initPage() 39 | }) 40 | } 41 | componentWillUnmount() { 42 | this.destory() 43 | } 44 | /** 45 | * 登录背景图太大,所以加了一个loading效果 46 | */ 47 | loadImageAsync = (url) => { 48 | return new Promise((resolve, reject) => { 49 | const image = new Image(); 50 | image.onload = function () { 51 | resolve(url); 52 | }; 53 | image.onerror = function () { 54 | console.log('图片载入错误') 55 | }; 56 | image.src = url; 57 | }) 58 | } 59 | /** 60 | * 创建背景粒子 61 | */ 62 | _createPoints() { 63 | const { width, height } = this 64 | //创建粒子和粒子的起始位置 65 | for (let x = 0; x < width; x = x + width / 20) { 66 | for (let y = 0; y < height; y = y + height / 20) { 67 | let px = x + Math.random() * width / 20; 68 | let py = y + Math.random() * height / 20; 69 | let p = { x: px, originX: px, y: py, originY: py }; 70 | this.points.push(p); 71 | } 72 | } 73 | //给每个粒子添加新属性closest、radius 74 | for (let i = 0; i < this.points.length; i++) { 75 | let closest = []; 76 | let p1 = this.points[i]; 77 | for (let j = i + 1; j < this.points.length; j++) { 78 | let p2 = this.points[j] 79 | let placed = false; 80 | for (let k = 0; k < 5; k++) { 81 | if (!placed) { 82 | if (closest[k] === undefined) { 83 | closest[k] = p2; 84 | placed = true; 85 | } 86 | } 87 | } 88 | for (let k = 0; k < 5; k++) { 89 | if (!placed) { 90 | if (this._getDistance(p1, p2) < this._getDistance(p1, closest[k])) { 91 | closest[k] = p2; 92 | placed = true; 93 | } 94 | } 95 | } 96 | } 97 | p1.closest = closest; 98 | p1.radius = 2 + Math.random() * 2 99 | //给粒子添加抖动 100 | this._shakePoint(p1); 101 | } 102 | } 103 | /** 104 | * 粒子抖动 105 | * @param {object} point 106 | */ 107 | _shakePoint(point) { 108 | TweenLite.to(point, 1 + 1 * Math.random(), { 109 | x: point.originX - 50 + Math.random() * 100, 110 | y: point.originY - 50 + Math.random() * 100, ease: Circ.easeInOut, 111 | onComplete: () => { 112 | this._shakePoint(point); 113 | } 114 | }); 115 | } 116 | /** 117 | * 绘制单个粒子 118 | * @param {*} point 119 | * @param {*} ctx 120 | */ 121 | _drawPoint(point, ctx) { 122 | if (!point.pointActive) return; 123 | ctx.beginPath(); 124 | ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI, false); 125 | ctx.fillStyle = 'rgba(156,217,249,' + point.pointActive + ')'; 126 | ctx.fill(); 127 | } 128 | /** 129 | * 绘制线条 130 | */ 131 | _drawLines(point, ctx) { 132 | if (!point.lineActive) return; 133 | for (let item of point.closest) { 134 | ctx.beginPath(); 135 | ctx.moveTo(point.x, point.y); 136 | ctx.lineTo(item.x, item.y); 137 | ctx.strokeStyle = 'rgba(156,217,249,' + point.lineActive + ')'; 138 | ctx.stroke(); 139 | } 140 | } 141 | /** 142 | * 获取两个粒子之间的距离 143 | * @param {object} p1 144 | * @param {object} p2 145 | */ 146 | _getDistance(p1, p2) { 147 | return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2); 148 | } 149 | /** 150 | * 鼠标移动事件处理 151 | * @param {*} e 152 | */ 153 | handleMouseMove = (e) => { 154 | let posx = 0, posy = 0; 155 | if (e.pageX || e.pageY) { 156 | posx = e.pageX; 157 | posy = e.pageY; 158 | } 159 | else if (e.clientX || e.clientY) { 160 | posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 161 | posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 162 | } 163 | this.target.x = posx; 164 | this.target.y = posy; 165 | } 166 | /** 167 | * 开始函数 168 | */ 169 | start = () => { 170 | const { width, height, points, ctx, target } = this 171 | ctx.clearRect(0, 0, width, height); 172 | for (let point of points) { 173 | if (Math.abs(this._getDistance(target, point)) < 4000) { 174 | point.lineActive = 0.3; 175 | point.pointActive = 0.6; 176 | } else if (Math.abs(this._getDistance(target, point)) < 20000) { 177 | point.lineActive = 0.1; 178 | point.pointActive = 0.3; 179 | } else if (Math.abs(this._getDistance(target, point)) < 40000) { 180 | point.lineActive = 0.02; 181 | point.pointActive = 0.1; 182 | } else { 183 | point.lineActive = 0; 184 | point.pointActive = 0; 185 | } 186 | this._drawLines(point, ctx) 187 | this._drawPoint(point, ctx); 188 | } 189 | this.myReq = window.requestAnimationFrame(() => this.start()); 190 | } 191 | initPage = () => { 192 | this.ctx = this.canvas.getContext('2d') 193 | this._createPoints() 194 | this.start() 195 | window.onmousemove = throttle(this.handleMouseMove, 50) //函数节流优化 196 | } 197 | destory = () => { 198 | window.cancelAnimationFrame(this.myReq) 199 | window.onmousemove = null 200 | } 201 | 202 | render() { 203 | const { url } = this.props 204 | const { loading } = this.state 205 | return ( 206 |
207 | { 208 | loading ? ( 209 |
210 | 211 |
212 | ) : ( 213 |
214 | this.canvas = el} 216 | style={styles.canvasStyle} 217 | width={this.width} 218 | height={this.height} /> 219 | {this.props.children} 220 |
221 | ) 222 | } 223 |
224 | ) 225 | } 226 | } 227 | 228 | const styles = { 229 | backgroundBox: { 230 | position: 'fixed', 231 | top: '0', 232 | left: '0', 233 | width: '100vw', 234 | height: '100vh', 235 | backgroundSize: '100% 100%', 236 | }, 237 | canvasStyle: { 238 | display: 'block', //防止全屏的canvas出现滚动条 239 | position: 'fixed', 240 | top: '0', 241 | left: '0', 242 | } 243 | } 244 | 245 | export default Background -------------------------------------------------------------------------------- /react/src/components/ColorPicker/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { SketchPicker } from 'react-color' 4 | 5 | class ColorPicker extends React.Component { 6 | static propTypes = { 7 | color: PropTypes.string, 8 | onChange: PropTypes.func, 9 | presetColors: PropTypes.array, 10 | disableAlpha:PropTypes.bool, 11 | } 12 | static defaultProps = { 13 | color: '', 14 | onChange: () => { }, 15 | presetColors: [ 16 | '#F5222D', 17 | '#FA541C', 18 | '#FA8C16', 19 | '#FAAD14', 20 | '#FADB14', 21 | '#A0D911', 22 | '#52C41A', 23 | '#13C2C2', 24 | '#1890FF', 25 | '#2F54EB', 26 | '#722ED1', 27 | '#EB2F96', 28 | ], 29 | disableAlpha:true //禁用rgba 30 | } 31 | static getDerivedStateFromProps(nextProps, prevState) { 32 | if (nextProps.color && nextProps.color !== prevState.color) { 33 | return { 34 | color: nextProps.color 35 | } 36 | } 37 | return null 38 | } 39 | state = { 40 | displayColorPicker: false, 41 | color: '' 42 | }; 43 | 44 | handleClick = () => { 45 | this.setState({ displayColorPicker: !this.state.displayColorPicker }) 46 | }; 47 | 48 | handleClose = () => { 49 | this.setState({ displayColorPicker: false }) 50 | }; 51 | 52 | handleChange = (color) => { 53 | this.props.onChange(color.hex) 54 | this.setState({ color: color.hex }) 55 | }; 56 | 57 | render() { 58 | const styles = { 59 | color: { 60 | width: '36px', 61 | height: '14px', 62 | borderRadius: '2px', 63 | background: this.state.color, 64 | }, 65 | swatch: { 66 | padding: '5px', 67 | background: '#fff', 68 | borderRadius: '1px', 69 | boxShadow: '0 0 0 1px rgba(0,0,0,.1)', 70 | display: 'inline-block', 71 | cursor: 'pointer', 72 | }, 73 | popover: { 74 | position: 'absolute', 75 | zIndex: '2', 76 | }, 77 | cover: { 78 | position: 'fixed', 79 | top: '0', 80 | right: '0', 81 | bottom: '0', 82 | left: '0', 83 | }, 84 | }; 85 | 86 | return ( 87 |
88 |
89 |
90 |
91 | {this.state.displayColorPicker ?
92 |
93 | 94 |
: null} 95 |
96 | ) 97 | } 98 | } 99 | 100 | export default ColorPicker -------------------------------------------------------------------------------- /react/src/components/Loading/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './style.css' 3 | 4 | //此效果来源于https://codepen.io/MarioDesigns/pen/LLrVLK 5 | 6 | class Loading extends React.Component { 7 | render() { 8 | const { className = '', style = {} } = this.props 9 | return ( 10 |
11 |
12 |
13 |
14 | ) 15 | } 16 | } 17 | 18 | export default Loading -------------------------------------------------------------------------------- /react/src/components/MyIcon/index.js: -------------------------------------------------------------------------------- 1 | import { Icon } from 'antd' 2 | 3 | // 刚开始我还没发现antd的Icon可以自定义图标,后来发现可以这样使用,非常方便,使用方法和Icon一致,svg也是未来图标的主流 4 | // 5 | 6 | const MyIcon = Icon.createFromIconfontCN({ 7 | scriptUrl: '//at.alicdn.com/t/font_731989_0s6wteco74wa.js', // 在 iconfont.cn 上生成 8 | }); 9 | 10 | export default MyIcon -------------------------------------------------------------------------------- /react/src/components/Phone/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react' 2 | import PropTypes from 'prop-types'; 3 | import './index.less' 4 | 5 | function Phone(props) { 6 | const [time, setTime] = useState(handleTime) 7 | const timer = useRef() 8 | 9 | useEffect(() => { 10 | timer.current = setInterval(() => { 11 | setTime(handleTime) 12 | }, 1000); 13 | return () => { 14 | clearInterval(timer.current) 15 | } 16 | }) 17 | 18 | function handleTime() { 19 | const date = new Date() 20 | const hour = String(date.getHours()) 21 | const minute = String(date.getMinutes()) 22 | return `${hour.padStart(2, 0)}:${minute.padStart(2, 0)}` 23 | } 24 | return ( 25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | T-Mobile 36 |
37 |
{time}
38 |
39 |
40 | 46 |
47 |
48 |
49 | ) 50 | } 51 | 52 | Phone.propTypes = { 53 | title: PropTypes.string, 54 | src: PropTypes.string 55 | } 56 | 57 | Phone.defaultProps = { 58 | title: 'UI', 59 | src: '' 60 | } 61 | 62 | 63 | export default Phone 64 | -------------------------------------------------------------------------------- /react/src/components/Phone/index.less: -------------------------------------------------------------------------------- 1 | /* phone */ 2 | #phone { 3 | position: relative; 4 | width: 310px; 5 | // height: 640px; 6 | height: 600px; 7 | border: 2px solid #ccc; 8 | border-radius: 30px; 9 | background: #fff; 10 | color: #595959; 11 | 12 | .screen { 13 | position: absolute; 14 | width: 290px; 15 | // height: 520px; 16 | height: 480px; 17 | border: 1px solid #ccc; 18 | left: 50%; 19 | top: 50%; 20 | transform: translate3d(-50%, -50%, 0); 21 | box-sizing: border-box; 22 | overflow: hidden; 23 | 24 | .status-bar { 25 | text-align: center; 26 | padding: 5px 10px 5px; 27 | font-size: 12px; 28 | line-height: 1; 29 | 30 | .signal { 31 | position: absolute; 32 | left: 10px; 33 | top: 5px; 34 | 35 | span { 36 | width: 7px; 37 | height: 7px; 38 | border: 1px solid #595959; 39 | display: inline-block; 40 | margin: 0 2px 0 0; 41 | border-radius: 50%; 42 | box-sizing: border-box; 43 | } 44 | 45 | .full { 46 | background-color: #595959; 47 | } 48 | } 49 | 50 | 51 | 52 | .battery { 53 | position: absolute; 54 | right: 10px; 55 | top: 6px; 56 | width: 22px; 57 | height: 10px; 58 | border: 1px solid #000; 59 | display: inline-block; 60 | border-radius: 2px; 61 | background-clip: padding-box; 62 | box-sizing: border-box; 63 | 64 | &::before { 65 | content: ''; 66 | position: absolute; 67 | right: -3px; 68 | width: 3px; 69 | height: 4px; 70 | top: 2px; 71 | background: #000; 72 | display: inline-block; 73 | border-radius: 0 1px 1px 0; 74 | } 75 | 76 | &::after { 77 | content: ''; 78 | position: absolute; 79 | top: 0; 80 | left: 0; 81 | width: 7px; 82 | height: 8px; 83 | background: #000; 84 | } 85 | } 86 | } 87 | } 88 | 89 | .home-btn { 90 | width: 36px; 91 | height: 36px; 92 | border: 1px solid #ccc; 93 | position: absolute; 94 | bottom: 10px; 95 | left: 50%; 96 | margin: 0 -18px; 97 | border-radius: 50%; 98 | } 99 | 100 | .speaker { 101 | width: 50px; 102 | height: 6px; 103 | border: 1px solid #ccc; 104 | border-radius: 6px; 105 | position: absolute; 106 | left: 50%; 107 | top: 25px; 108 | margin: 0 -25px; 109 | } 110 | } -------------------------------------------------------------------------------- /react/src/components/PrivateRoute/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route, Redirect, } from 'react-router-dom' 3 | import { isAuthenticated } from '@/utils/session' 4 | 5 | const PrivateRoute = ({ component: Component, ...rest }) => ( 6 | ( 7 | !!isAuthenticated() 8 | ? 9 | : 13 | )} /> 14 | ) 15 | 16 | export default PrivateRoute -------------------------------------------------------------------------------- /react/src/components/PromptBox/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | class PromptBox extends React.Component { 5 | static propTypes = { 6 | info: PropTypes.string 7 | } 8 | static defaultProps = { 9 | info: '' 10 | } 11 | componentDidUpdate(prevProps) { 12 | if (this.props.info && this.props.info !== prevProps.info) { 13 | this._renderContent() 14 | } 15 | } 16 | _renderContent = () => { 17 | const ctx = this.canvas.getContext('2d') 18 | const width = this._calcWidth() 19 | ctx.strokeStyle = '#fff' 20 | ctx.shadowOffsetX = -2 21 | ctx.shadowOffsetY = 2 22 | ctx.shadowBlur = 2 23 | ctx.shadowColor = 'rgba(0,0,0,.3)' 24 | ctx.beginPath() 25 | ctx.moveTo(0, 20) 26 | ctx.lineTo(8, 16) 27 | ctx.lineTo(8, 1) 28 | ctx.lineTo(width - 1, 1) 29 | ctx.lineTo(width - 1, 39) 30 | ctx.lineTo(8, 39) 31 | ctx.lineTo(8, 23) 32 | ctx.closePath() 33 | ctx.stroke(); 34 | ctx.fillStyle = '#D3D7F7' 35 | ctx.textBaseline = 'middle' 36 | ctx.font = '14px sans-serif' 37 | ctx.beginPath() 38 | ctx.fillText(this.props.info, 20, 20); 39 | } 40 | _calcWidth = () => { 41 | return 30 + this.props.info.length * 15 42 | } 43 | render() { 44 | const { className = '', style = {} } = this.props 45 | return ( 46 |
47 | this.canvas = el} /> 48 |
49 | ) 50 | } 51 | } 52 | 53 | export default PromptBox -------------------------------------------------------------------------------- /react/src/components/Typing/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | // 如何将react节点转换为dom,比如拥有className、style、onClick的react元素,我们生成的dom如何保留这些。 5 | // 本来想直接用React.createElement来代替document.createElement。但是ReactDOM.render()方法插入的位置节点必须是dom所以此方法不行 6 | function isObject(obj){ 7 | return Object.prototype.toString.call(obj) === '[object Object]' 8 | } 9 | 10 | class Typing extends React.Component { 11 | static propTypes = { 12 | delay: PropTypes.number, //设置打印延时,单位为毫秒 13 | frequency: PropTypes.number, //设置打印频率 14 | done: PropTypes.func //打印结束的函数 15 | } 16 | static defaultProps = { 17 | delay: 0, 18 | frequency: 30, 19 | done: () => { } 20 | } 21 | 22 | componentDidMount() { 23 | this.chain = { //此变量就是将要打印的对象 24 | parent: null, 25 | dom: this.wrapper, 26 | val: [] 27 | }; 28 | this.chain.val = this._convert(this.props.children, this.chain.val) 29 | setTimeout(() => { 30 | this._play(this.chain) 31 | }, this.props.delay) 32 | } 33 | /** 34 | * children转换为符合打印的数组 35 | * @param {*} children Object、Array、String、undefined、Null 36 | * @param {array} arr 保存打印的数组 37 | */ 38 | _convert(children, arr = []) { 39 | let list = arr.slice() 40 | if(Array.isArray(children)){ 41 | for(let item of children){ 42 | list = list.concat(this._convert(item)) 43 | } 44 | } 45 | if(isObject(children)){ 46 | const dom = this._createDom({ 47 | ...children.props, 48 | type:children.type 49 | }) 50 | const val = this._convert(children.props.children,[]) 51 | list.push({ 52 | dom, 53 | val 54 | }) 55 | } 56 | if(typeof children === 'string'){ 57 | list = list.concat(children.split('')) 58 | } 59 | return list 60 | } 61 | /** 62 | * 打印字符 63 | * @param {*} dom 父节点 64 | * @param {*} val 打印内容 65 | * @param {*} callback 打印完成的回调 66 | */ 67 | _print(dom, val, callback) { 68 | setTimeout(function () { 69 | dom.appendChild(document.createTextNode(val)); 70 | callback(); 71 | }, this.props.frequency); 72 | } 73 | /** 74 | * 打印节点 75 | * @param {*} node 76 | */ 77 | _play = (node) => { 78 | //当打印最后一个字符时,动画完毕,执行done 79 | if (!node.val.length) { 80 | if (node.parent) this._play(node.parent); 81 | else this.props.done(); 82 | return; 83 | } 84 | let current = node.val.shift() //获取第一个元素,并从打印列表中删除 85 | if (typeof current === 'string') { 86 | this._print(node.dom, current, () => { 87 | this._play(node) 88 | }) 89 | } else { 90 | let dom = current.dom 91 | node.dom.appendChild(dom) 92 | this._play({ 93 | parent: node, 94 | dom, 95 | val: current.val 96 | }) 97 | } 98 | } 99 | /** 100 | * 根据信息生成dom节点 101 | * @param {object} info 102 | */ 103 | _createDom(info) { 104 | info = { ...info } 105 | let dom = document.createElement(info.type) 106 | 107 | delete info.children 108 | 109 | for (let [key, value] of Object.entries(info)) { 110 | if (key === 'className') { 111 | key = 'class' 112 | } 113 | dom.setAttribute(key, value) 114 | } 115 | if (info.style) { 116 | let cssText = '' 117 | for (let [key, value] of Object.entries(info.style)) { 118 | cssText += `${key}:${value};` 119 | } 120 | dom.style.cssText = cssText 121 | } 122 | 123 | return dom 124 | } 125 | 126 | render() { 127 | const { className = '', style = {} } = this.props 128 | return ( 129 |
this.wrapper = el} className={className} style={style}> 130 | 131 |
132 | ) 133 | } 134 | } 135 | 136 | export default Typing 137 | -------------------------------------------------------------------------------- /react/src/config/secret.js: -------------------------------------------------------------------------------- 1 | 2 | //前台加密主要是为了防止post请求明文传递密码 3 | 4 | 5 | //使用例子 6 | // var CryptoJS = require("crypto-js"); 7 | 8 | // var data = [{id: 1}, {id: 2}] 9 | 10 | // // Encrypt 11 | // var ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123').toString(); 12 | 13 | // // Decrypt 14 | // var bytes = CryptoJS.AES.decrypt(ciphertext, 'secret key 123'); 15 | // var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)); 16 | 17 | // console.log(decryptedData); // [{id: 1}, {id: 2}] 18 | 19 | export const SECRETKEY = 'front_666666' -------------------------------------------------------------------------------- /react/src/index.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | import { Router } from 'react-router-dom' 7 | import { LocaleProvider } from 'antd' 8 | import zh_CN from 'antd/lib/locale-provider/zh_CN'; 9 | import moment from 'moment'; 10 | import 'moment/locale/zh-cn'; 11 | import history from './utils/history' 12 | import { Provider } from 'react-redux' 13 | import store from './store' 14 | 15 | moment.locale('zh-cn'); 16 | 17 | ReactDOM.render( 18 | 19 | 20 | 21 | 22 | 23 | 24 | , 25 | document.getElementById('root')); 26 | 27 | // If you want your app to work offline and load faster, you can change 28 | // unregister() to register() below. Note this comes with some pitfalls. 29 | // Learn more about service workers: https://bit.ly/CRA-PWA 30 | serviceWorker.unregister(); 31 | -------------------------------------------------------------------------------- /react/src/pages/About/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import {Card} from 'antd' 3 | import Typing from '../../components/Typing/index' 4 | 5 | class About extends Component { 6 | state = { } 7 | render() { 8 | return ( 9 |
10 | 11 | 12 |

关于

13 | 这个人很懒,什么也没留下... 14 |
15 |
16 |
17 | ); 18 | } 19 | } 20 | 21 | export default About; -------------------------------------------------------------------------------- /react/src/pages/ButtonDemo/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Button, Card, Row, Col, Menu, Dropdown, Icon } from 'antd' 3 | import Typing from '../../components/Typing/index' 4 | 5 | 6 | class ButtonDemo extends Component { 7 | state = {} 8 | render() { 9 | const menu = ( 10 | 11 | 1st item 12 | 2nd item 13 | 3rd item 14 | 15 | ); 16 | return ( 17 |
18 | 19 | 20 |

何时使用

21 | 标记了一个(或封装一组)操作命令,响应用户点击行为,触发相应的业务逻辑。 22 |
23 |
24 | 25 | 26 | 27 |
28 |   29 |   30 |   31 | 32 |
33 |
34 |   35 |   36 |   37 | 38 |
39 |
40 | 41 | 42 | 43 |
44 |   46 | 48 |
49 |
50 |   52 | 54 |
55 |
56 | 57 | 58 | 59 |
60 | 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 |   70 |   71 | 72 | 75 | 76 | 77 | 78 |
79 |
80 | ); 81 | } 82 | } 83 | 84 | export default ButtonDemo; -------------------------------------------------------------------------------- /react/src/pages/Chat/imgs/administrator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/pages/Chat/imgs/administrator.png -------------------------------------------------------------------------------- /react/src/pages/Chat/imgs/header1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/pages/Chat/imgs/header1.png -------------------------------------------------------------------------------- /react/src/pages/Chat/imgs/header2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/pages/Chat/imgs/header2.png -------------------------------------------------------------------------------- /react/src/pages/Chat/imgs/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/pages/Chat/imgs/react.png -------------------------------------------------------------------------------- /react/src/pages/Chat/imgs/zanwu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-9527/admin/72aaf2dd05cf4ec2e98f390668b41e128eec5ad2/react/src/pages/Chat/imgs/zanwu.png -------------------------------------------------------------------------------- /react/src/pages/Chat/style.less: -------------------------------------------------------------------------------- 1 | .chat-container { 2 | .chat-box { 3 | position: absolute; 4 | top: 20px; 5 | left: 25%; 6 | display: flex; 7 | flex-direction: column; 8 | width: 780px; 9 | height: 520px; 10 | border-radius: 5px; 11 | border: 1px solid #d1d1d1; 12 | background: #fff; 13 | box-shadow: 0 15px 40px rgba(0, 0, 0, .2); 14 | 15 | .chat-header { 16 | position: relative; 17 | display: flex; 18 | justify-content: center; 19 | align-items: center; 20 | width: 100%; 21 | height: 60px; 22 | background: #e6e6e6; 23 | flex-shrink: 0; 24 | 25 | .header-left { 26 | position: absolute; 27 | left: 0; 28 | height: 80%; 29 | 30 | img { 31 | height: 100%; 32 | } 33 | } 34 | 35 | .header-center { 36 | height: 80%; 37 | 38 | img { 39 | height: 100%; 40 | } 41 | } 42 | 43 | .header-right { 44 | position: absolute; 45 | right: 30px; 46 | } 47 | } 48 | 49 | .chat-body { 50 | display: flex; 51 | flex-grow: 1; 52 | display: flex; 53 | 54 | .left { 55 | flex-shrink: 0; 56 | width: 200px; 57 | height: 100%; 58 | border-right: 1px solid #f3f3f3; 59 | } 60 | 61 | .main { 62 | flex-grow: 1; 63 | height: 100%; 64 | 65 | .chat-list { 66 | height: 328px; 67 | overflow: auto; 68 | } 69 | 70 | .chat-editor-wrapper { 71 | height: 130px; 72 | flex-shrink: 0; 73 | border-top: 1px solid #f3f3f3; 74 | } 75 | } 76 | 77 | .right { 78 | display: flex; 79 | flex-direction: column; 80 | flex-shrink: 0; 81 | width: 135px; 82 | height: 100%; 83 | background: #f6f7f9; 84 | border-left: 1px solid #f3f3f3; 85 | 86 | .member { 87 | padding: 8px 10px 3px; 88 | } 89 | 90 | .user-item { 91 | display: flex; 92 | align-items: center; 93 | color: #787677; 94 | font-size: 12px; 95 | padding: 5px 10px; 96 | 97 | .avatar-box { 98 | position: relative; 99 | flex-shrink: 0; 100 | width: 24px; 101 | height: 24px; 102 | border-radius: 50%; 103 | overflow: hidden; 104 | 105 | &.mask { 106 | img { 107 | opacity: .7; 108 | } 109 | 110 | div { 111 | position: absolute; 112 | top: 0; 113 | left: 0; 114 | width: 100%; 115 | height: 100%; 116 | background: rgba(0, 0, 0, 0.2); 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | 127 | //避免css嵌套太深就从上面的提出来了,css没有模块化,所以要注意此样式会影响其他页面相同样式名的样式 128 | 129 | 130 | .left-item { 131 | display: flex; 132 | align-items: center; 133 | padding: 10px; 134 | 135 | &:hover { 136 | background: #f3f3f3; 137 | } 138 | 139 | .left-item-text { 140 | flex-grow: 1; 141 | overflow: hidden; 142 | margin-left: 10px; 143 | 144 | .group-name { 145 | display: flex; 146 | justify-content: space-between; 147 | align-items: center; 148 | 149 | span:first-child { 150 | color: rgba(0, 0, 0, 0.75); 151 | font-size: 15px; 152 | line-height: 1.5; 153 | } 154 | 155 | span:last-child { 156 | font-size: 12px; 157 | color: #abaaab; 158 | } 159 | } 160 | 161 | .group-message { 162 | display: flex; 163 | color: #777777; 164 | font-size: 13px; 165 | height: 21px; 166 | overflow: hidden; 167 | 168 | &:last-child { 169 | flex-grow: 1; 170 | overflow: hidden; 171 | } 172 | 173 | p { 174 | margin-bottom: 0; 175 | overflow: hidden; 176 | text-overflow: ellipsis; 177 | white-space: nowrap; 178 | } 179 | } 180 | } 181 | } 182 | 183 | .chat-item { 184 | padding: 15px; 185 | 186 | .time { 187 | margin: 10px 0; 188 | text-align: center; 189 | font-size: 12px; 190 | color: #d0cfcf; 191 | } 192 | 193 | .chat-item-info { 194 | display: flex; 195 | justify-content: flex-start; 196 | 197 | &.chat-right { 198 | flex-direction: row-reverse; 199 | 200 | .chat-main { 201 | text-align: right; 202 | margin-right: 12px; 203 | 204 | .username { 205 | color: #9f9f9f; 206 | } 207 | 208 | .chat-content { 209 | background: #d9f4fe; 210 | text-align: left; 211 | 212 | &::before{ 213 | display: none; 214 | } 215 | 216 | &::after { 217 | content: ''; 218 | position: absolute; 219 | top: 8px; 220 | left: 100%; 221 | width: 0; 222 | height: 0; 223 | border-top: 3px solid transparent; 224 | border-bottom: 6px solid transparent; 225 | border-left: 6px solid #d9f4fe; 226 | } 227 | } 228 | } 229 | } 230 | 231 | .chat-main { 232 | flex-grow: 1; 233 | margin-left: 12px; 234 | 235 | .username { 236 | color: #a0abca; 237 | font-size: 14px; 238 | } 239 | 240 | .chat-content { 241 | position: relative; 242 | display: inline-block; 243 | max-width: 80%; 244 | padding: 12px; 245 | border-radius: 5px; 246 | background: #f3f3f3; 247 | word-wrap: break-word; 248 | word-break: break-all; 249 | 250 | &::before { 251 | content: ''; 252 | position: absolute; 253 | top: 8px; 254 | right: 100%; 255 | width: 0; 256 | height: 0; 257 | border-top: 3px solid transparent; 258 | border-bottom: 6px solid transparent; 259 | border-right: 6px solid #f3f3f3; 260 | } 261 | 262 | img { 263 | max-width: 100% !important; 264 | height: 200px !important; 265 | object-fit: contain; 266 | } 267 | 268 | p { 269 | margin-bottom: 0; 270 | } 271 | } 272 | } 273 | } 274 | } -------------------------------------------------------------------------------- /react/src/pages/Collection/CreateModal.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Form, Input, Modal, Icon, } from 'antd' 3 | import { json } from '../../utils/ajax' 4 | import { connect } from 'react-redux' 5 | 6 | const { TextArea } = Input; 7 | 8 | const store = connect( 9 | (state) => ({ user: state.user }) 10 | ) 11 | 12 | @store @Form.create() 13 | class CreateModal extends Component { 14 | state = {} 15 | onCancel = () => { 16 | this.props.form.resetFields() 17 | this.props.toggleVisible(false) 18 | } 19 | onOk = () => { 20 | this.props.form.validateFieldsAndScroll((errors, values) => { 21 | if (!errors) { 22 | this.onCreate(values) 23 | } 24 | }) 25 | } 26 | onCreate = async (values) => { 27 | const res = await json.post('/works/create', values) 28 | if (res.status === 0) { 29 | this.props.onCreated() //更新外面的数据 30 | this.onCancel() 31 | } 32 | } 33 | render() { 34 | const { getFieldDecorator } = this.props.form 35 | const formItemLayout = { 36 | labelCol: { span: 6 }, 37 | wrapperCol: { span: 14 }, 38 | } 39 | return ( 40 | 48 |
49 | 50 | {getFieldDecorator('title', { 51 | rules: [ 52 | { required: true, message: '请输入项目名称' }, 53 | ] 54 | })( 55 | 56 | )} 57 | 58 | 59 | {getFieldDecorator('description', { 60 | rules: [ 61 | { required: true, message: '请输入项目描述' }, 62 | ] 63 | })( 64 |