├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json └── src ├── App.css ├── App.js ├── App.test.js ├── components ├── Label │ ├── CheckboxLabel │ │ └── index.jsx │ └── LabelTags │ │ └── index.jsx ├── Marker │ ├── MarkerCreate │ │ └── index.jsx │ ├── MarkerList │ │ └── index.jsx │ └── ModalView │ │ └── index.jsx ├── Statistic │ └── index.jsx └── common │ ├── HighOrderComponents │ └── AsyncComponent.js │ ├── IconLike │ └── index.jsx │ ├── LoginForm │ ├── index.jsx │ └── index.less │ └── UserAvatar │ └── index.jsx ├── config ├── api.config.js ├── list.config.js ├── marker.config.js └── routes.config.js ├── css └── main.less ├── index.css ├── index.js ├── logo.svg ├── pages ├── Layouts.jsx ├── Menus.jsx ├── PageHome.jsx ├── PageLabel.jsx ├── PageList.jsx ├── PageLogin.jsx ├── PageMarker.jsx └── PageSetting.jsx ├── registerServiceWorker.js └── utils ├── asyncFetch.js └── common.js /README.md: -------------------------------------------------------------------------------- 1 | 使用React+Ant Design+react-router-dom,构建菜单栏及点击跳转的单页应用。 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marker", 3 | "version": "0.1.0", 4 | "private": true, 5 | "babel": { 6 | "presets": [ 7 | "react-app" 8 | ], 9 | "plugins": [ 10 | "react-hot-loader/babel", 11 | "transform-runtime", 12 | [ 13 | "import", 14 | { 15 | "libraryName": "antd", 16 | "style": true 17 | } 18 | ] 19 | ] 20 | }, 21 | "dependencies": { 22 | "antd": "^3.13.0", 23 | "babel-core": "6.26.0", 24 | "babel-eslint": "7.2.3", 25 | "babel-jest": "20.0.3", 26 | "babel-loader": "7.1.2", 27 | "babel-preset-react-app": "^3.1.0", 28 | "babel-runtime": "6.26.0", 29 | "less": "^2.7.3", 30 | "less-loader": "^4.0.5", 31 | "react": "^16.3.2", 32 | "react-dom": "^16.3.2", 33 | "react-hot-loader": "^3.0.0", 34 | "react-router-dom": "^4.2.2", 35 | "react-scripts": "1.1.4" 36 | }, 37 | "scripts": { 38 | "start": "react-scripts start", 39 | "build": "react-scripts build", 40 | "test": "react-scripts test --env=jsdom", 41 | "eject": "react-scripts eject" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoldiChen/marker-react/bc0b635351571c4d6b9e32ae1ab636d76d7c90df/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /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": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | .login-card { 26 | position: absolute; 27 | top: 50%; 28 | left: 50%; 29 | width: 500px; 30 | padding: 20px; 31 | background: #393; 32 | color: #fff; 33 | transform: translate(-50%, 40%); 34 | } 35 | 36 | @keyframes App-logo-spin { 37 | from { transform: rotate(0deg); } 38 | to { transform: rotate(360deg); } 39 | } 40 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import PropTypes from "prop-types"; 4 | import './App.css'; 5 | import "./css/main.less"; 6 | import Layouts from "./pages/Layouts"; 7 | import PageLogin from "./pages/PageLogin"; 8 | import { LocaleProvider, message } from "antd"; 9 | import enUs from 'antd/lib/locale-provider/zh_CN' 10 | import { API } from "./config/api.config"; 11 | import asyncFetch from "./utils/asyncFetch"; 12 | 13 | class App extends Component { 14 | 15 | constructor(props) { 16 | console.log("App.js@constructor"); 17 | console.log(props); 18 | super(props); 19 | this.state = { 20 | login: false, 21 | userInfo: {}, 22 | token: '' 23 | } 24 | } 25 | 26 | componentDidMount() { 27 | console.log("App.js@componentDidMount"); 28 | console.log(this.state.token); 29 | console.log(this.state.userInfo); 30 | let url = API.get_current_user; 31 | asyncFetch('GET', url, {}, 32 | (res) => { 33 | console.log(res); 34 | if (res.code === 0) { 35 | message.success("current user."); 36 | message.success(JSON.stringify(res.data)); 37 | this.setState( 38 | { 39 | login: true, 40 | userInfo: res.data 41 | } 42 | ); 43 | } else { 44 | message.error(res.message); 45 | } 46 | }, {}, 'cors', () => {}, false, localStorage.getItem("markerToken")); 47 | } 48 | 49 | getChildContext() { 50 | return { 51 | login: this.state.login, 52 | userInfo: this.state.userInfo, 53 | token: this.state.token, 54 | setLoginInfo: this.setLoginInfo 55 | }; 56 | } 57 | 58 | setLoginInfo = (login, userInfo, token) => { 59 | console.log('App.js@setLoginInfo'); 60 | console.log(login); 61 | console.log(userInfo); 62 | console.log(token); 63 | localStorage.setItem("markerToken", token); 64 | this.setState( 65 | { 66 | login: login, 67 | userInfo: userInfo, 68 | token: token 69 | } 70 | ); 71 | }; 72 | 73 | render() { 74 | console.log("App.js@render"); 75 | console.log(this.state.login); 76 | return ( 77 | 78 | { 79 | this.state.login? 80 | 83 | : 84 | 87 | } 88 | 89 | 90 | ); 91 | } 92 | } 93 | 94 | App.childContextTypes = { 95 | login: PropTypes.bool, 96 | userInfo: PropTypes.object, 97 | setLoginInfo: PropTypes.func, 98 | token: PropTypes.string 99 | }; 100 | 101 | 102 | export default App; 103 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/Label/CheckboxLabel/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Checkbox, message } from "antd"; 3 | import { API } from "../../../config/api.config"; 4 | import asyncFetch from "../../../utils/asyncFetch"; 5 | 6 | class CheckboxLabel extends React.Component { 7 | 8 | constructor(props) { 9 | super(props); 10 | this.state = { 11 | options: [] 12 | }; 13 | } 14 | 15 | componentDidMount() { 16 | this.getLabels(); 17 | } 18 | 19 | handleCheckOnChange = (e) => { 20 | console.log(e); 21 | this.props.onChange(e); // 调用父组件的函数 22 | }; 23 | 24 | getLabels() { 25 | let url = API.get_labels; 26 | asyncFetch('GET', url, {}, 27 | (res) => { 28 | if (res.code === 0) { 29 | let options = []; 30 | res.data.map(function(label) { 31 | options.push({ 32 | value: label.id, 33 | label: label.name 34 | }); 35 | }); 36 | this.setState({ 37 | options: options 38 | }); 39 | } else { 40 | message.error(res.message); 41 | } 42 | }, {}, 'cors'); 43 | } 44 | 45 | render() { 46 | const CheckboxGroup = Checkbox.Group; 47 | 48 | return ( 49 | 50 | ); 51 | } 52 | 53 | } 54 | 55 | export default CheckboxLabel; -------------------------------------------------------------------------------- /src/components/Label/LabelTags/index.jsx: -------------------------------------------------------------------------------- 1 | 2 | import React from "react"; 3 | import { Tag, message, Input } from 'antd'; 4 | import { API } from "../../../config/api.config"; 5 | import asyncFetch from "../../../utils/asyncFetch"; 6 | import getColor from "../../../utils/common"; 7 | import _ from "lodash"; 8 | 9 | class LabelTags extends React.Component { 10 | 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | labels: [] 15 | } 16 | } 17 | 18 | componentDidMount() { 19 | this.getLabels(); 20 | } 21 | 22 | getLabels() { 23 | console.log('LabelTags@getLabels'); 24 | let url = API.get_labels; 25 | asyncFetch('GET', url, {}, 26 | (res) => { 27 | if (res.code === 0) { 28 | let labels = []; 29 | res.data.map(function(row) { 30 | labels.push({ 31 | key: row.id, 32 | name: row.name 33 | }); 34 | return 0; 35 | }); 36 | this.setState({ 37 | labels: labels 38 | }); 39 | } else { 40 | message.error(res.message); 41 | } 42 | }, {}, 'cors'); 43 | } 44 | 45 | handleOnAdd = (value) => { 46 | let url = API.create_labels; 47 | let params = { 48 | name: value 49 | }; 50 | asyncFetch('POST', url, params, 51 | (res) => { 52 | console.log(res); 53 | if (res.code === 0) { 54 | message.success("add label success."); 55 | let newLabelId = res.data; 56 | const labels = _.clone(this.state.labels); 57 | labels.push({key: newLabelId, name: value}); 58 | this.setState({labels: labels}); 59 | } else { 60 | message.error(res.message); 61 | } 62 | }, {}, 'cors'); 63 | }; 64 | 65 | render() { 66 | 67 | const { Search } = Input; 68 | 69 | return ( 70 |
71 | 72 | this.handleOnAdd(value)} 75 | enterButton="Add" 76 | style={{marginTop: "6px", marginBottom: "8px"}} 77 | /> 78 | 79 | { 80 | this.state.labels.map(label => ( 81 | {label.name} 82 | )) 83 | } 84 |
85 | ); 86 | } 87 | } 88 | 89 | export default LabelTags; -------------------------------------------------------------------------------- /src/components/Marker/MarkerCreate/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { API } from "../../../config/api.config"; 3 | import { Input, message } from "antd"; 4 | import asyncFetch from "../../../utils/asyncFetch"; 5 | import CheckboxLabel from "../../Label/CheckboxLabel/index"; 6 | import moment from "moment"; 7 | 8 | const DATE_FORMAT = 'yyyy-MM-dd hh:mm:ss'; 9 | 10 | class MarkerCreate extends React.Component { 11 | 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | content: "", 16 | checkLabelIds: [] 17 | }; 18 | } 19 | 20 | handleOnSearch = (value) => { 21 | let url = API.post_create; 22 | let labelIds = []; 23 | this.state.checkLabelIds.map(function(id) { 24 | labelIds.push({ 25 | id: id 26 | }); 27 | return 0; 28 | }); 29 | let now = moment().format('YYYY-MM-DD HH:mm:ss'); 30 | let params = { 31 | title: value, 32 | content: this.state.content, 33 | labels: labelIds, 34 | users: [{id: 1}], // todo: 获取当前登陆用户的id, 35 | update_date: now 36 | }; 37 | asyncFetch('POST', url, params, 38 | (res) => { 39 | console.log(res); 40 | if (res.code === 0) { 41 | message.success("create success."); 42 | } else { 43 | message.error(res.message); 44 | } 45 | }, {}, 'cors'); 46 | }; 47 | 48 | handleTextAreaOnChange = (e) => { 49 | this.setState({ 50 | content: e.target.value 51 | }); 52 | }; 53 | 54 | handleCheckboxOnChange = (values) => { 55 | console.log("handleCheckboxOnChange"); 56 | console.log(values); 57 | this.setState({ 58 | checkLabelIds: values 59 | }); 60 | }; 61 | 62 | render() { 63 | const { TextArea } = Input; 64 | const { Search } = Input; 65 | return ( 66 |
67 | 68 | this.handleOnSearch(value)} 71 | enterButton="Submit" 72 | /> 73 | 74 |