├── .gitignore ├── IceBackend └── LmmIceBackend │ ├── .gitignore │ ├── README.md │ ├── mock │ ├── .gitkeep │ ├── bar-line-chart.json │ ├── blank-layout.json │ ├── complex-progress-table.json │ ├── detail-table.json │ └── time-filter-table.json │ ├── package-lock.json │ ├── package.json │ ├── public │ └── index.html │ └── src │ ├── components │ ├── BasicNotFound │ │ ├── BasicNotFound.jsx │ │ └── index.js │ ├── Footer.jsx │ ├── Header.jsx │ ├── HeaderWithLogo.jsx │ ├── LmmBaseTable.jsx │ ├── LmmBreadCrumb.jsx │ ├── LmmFormItem.jsx │ ├── LmmQiniuUpload.jsx │ ├── LmmTreeSelect.jsx │ └── Logo.jsx │ ├── index.js │ ├── layouts │ ├── AsideLayout │ │ ├── Layout.jsx │ │ ├── index.js │ │ └── scss │ │ │ ├── base.scss │ │ │ └── light.scss │ ├── AsideLayoutWithHeader │ │ ├── Layout.jsx │ │ ├── index.js │ │ └── scss │ │ │ ├── base.scss │ │ │ └── light.scss │ └── BlankLayout │ │ ├── Layout.jsx │ │ ├── Layout.scss │ │ └── index.js │ ├── menuConfig.js │ ├── pages │ ├── Auth │ │ ├── Auth.jsx │ │ ├── Auth.scss │ │ ├── components │ │ │ └── UserLogin │ │ │ │ ├── UserLogin.jsx │ │ │ │ ├── UserLogin.scss │ │ │ │ └── index.js │ │ └── index.js │ ├── Home │ │ ├── Home.jsx │ │ ├── components │ │ │ ├── ReviewDataChart │ │ │ │ ├── ReviewDataChart.jsx │ │ │ │ └── index.js │ │ │ ├── ReviewDetailInfo │ │ │ │ ├── ReviewDetailInfo.jsx │ │ │ │ └── index.js │ │ │ ├── ReviewOverview │ │ │ │ ├── Progress.jsx │ │ │ │ ├── ReviewOverview.jsx │ │ │ │ └── index.js │ │ │ └── ReviewRequestTable │ │ │ │ ├── ReviewRequestTable.jsx │ │ │ │ └── index.js │ │ └── index.js │ ├── Menu │ │ ├── Menu.jsx │ │ ├── Menu.scss │ │ ├── components │ │ │ ├── LmmOperate │ │ │ │ ├── LmmOperate.jsx │ │ │ │ └── index.js │ │ │ └── SimpleFormDialog │ │ │ │ ├── SimpleFormDialog.jsx │ │ │ │ └── index.js │ │ └── index.js │ ├── NotFound │ │ ├── NotFound.jsx │ │ ├── NotFound.scss │ │ └── index.js │ ├── Role │ │ ├── Role.jsx │ │ ├── Role.scss │ │ ├── components │ │ │ ├── LmmOperate │ │ │ │ ├── LmmOperate.jsx │ │ │ │ └── index.js │ │ │ ├── RoleMenuDialog.jsx │ │ │ └── SimpleFormDialog │ │ │ │ ├── SimpleFormDialog.jsx │ │ │ │ └── index.js │ │ └── index.js │ └── User │ │ ├── User.jsx │ │ ├── User.scss │ │ ├── components │ │ ├── LmmOperate │ │ │ ├── LmmOperate.jsx │ │ │ └── index.js │ │ ├── SimpleFormDialog │ │ │ ├── SimpleFormDialog.jsx │ │ │ └── index.js │ │ └── UserRoleDialog.jsx │ │ └── index.js │ ├── router.jsx │ ├── routerConfig.js │ └── utils │ ├── graphqlUtil.js │ ├── lmmNet.js │ └── treeConvertUtil.js ├── IceFrame.md ├── NewLmmFrame.sql ├── README.md ├── backend ├── package.json ├── src │ ├── actions │ │ ├── actionType.js │ │ ├── common │ │ │ ├── commonActionType.js │ │ │ ├── formAction.js │ │ │ └── tableAction.js │ │ ├── frameMenuAction.js │ │ ├── frameRoleAction.js │ │ ├── infoAction.js │ │ ├── infoCategoryAction.js │ │ ├── loginAction.js │ │ └── userAction.js │ ├── api │ │ └── index.js │ ├── components │ │ ├── CLComplexMenu.js │ │ ├── CLContentCard.js │ │ ├── CLContentCard.less │ │ ├── CLContentCardWithClose.js │ │ ├── CLForm.js │ │ ├── CLForm.less │ │ ├── CLModalForm.js │ │ ├── CLSimditor.js │ │ ├── CLTableViewCell.js │ │ ├── CLTopMenu.js │ │ ├── CLTopMenu.less │ │ ├── CLTopMenus.js │ │ ├── CLTree.js │ │ ├── CLTreeSelect.js │ │ ├── CLUploadFiles.js │ │ ├── CLUploadImage.js │ │ ├── CLUploadImageToQiniu.js │ │ ├── LMMCommonCURD.js │ │ ├── LmmTableView.js │ │ ├── LmmTableViewSearch.js │ │ └── LmmTableViewSearch.less │ ├── favicon.ico │ ├── img │ │ ├── LOGO.jpg │ │ ├── LOGO.png │ │ ├── account.png │ │ ├── activity.png │ │ ├── bg1.jpg │ │ ├── config.png │ │ ├── head.png │ │ ├── imgtag.png │ │ ├── order.png │ │ ├── rightTo.png │ │ ├── sep.png │ │ ├── shop.png │ │ ├── text.png │ │ └── user.png │ ├── index.html │ ├── layouts │ │ ├── BaseLayout │ │ │ ├── ListWithTreeLayout.js │ │ │ └── ListWithTreeLayout.less │ │ └── MainLayout │ │ │ ├── MainLayout.js │ │ │ └── MainLayout.less │ ├── libs │ │ ├── common.js │ │ ├── constant.js │ │ ├── fetchUtil.js │ │ ├── preact.js │ │ ├── qiniuUtil.js │ │ ├── react-dom.js │ │ └── react.js │ ├── page │ │ ├── frame │ │ │ ├── FrameMenu.js │ │ │ ├── FrameRole.js │ │ │ ├── FrameRoleMenu.js │ │ │ ├── FrameUserRole.js │ │ │ └── Users.js │ │ ├── index │ │ │ ├── index.less │ │ │ └── main.js │ │ ├── info │ │ │ ├── InfoAdd.js │ │ │ ├── InfoCategory.js │ │ │ └── InfoList.js │ │ ├── signIn.js │ │ ├── signIn.less │ │ └── style │ │ │ └── pageStyle.less │ ├── reducers │ │ ├── common │ │ │ ├── formReducer.js │ │ │ └── tableReducer.js │ │ ├── frameMenuReducer.js │ │ ├── frameRoleReducer.js │ │ ├── infoCReducer.js │ │ ├── infoReducer.js │ │ ├── loginReducer.js │ │ └── userReducer.js │ └── routes │ │ ├── index.js │ │ └── store.js ├── tools │ ├── config.js │ ├── gulpfile.js │ ├── sprite-template │ │ ├── less.template.handlebars │ │ └── scss.template.handlebars │ ├── utils.js │ ├── webpack.config.js │ ├── webpack.dev.js │ ├── webpack.pub.js │ └── webpack.server.js └── 组件用法.md ├── clantd.sql ├── doc ├── curd控件.md ├── form表单.md └── table控件.md ├── eggserver ├── README.md ├── README.zh-CN.md ├── app │ ├── controller │ │ ├── menu.js │ │ ├── qiniu.js │ │ ├── role.js │ │ ├── upload.js │ │ └── user.js │ ├── core │ │ ├── base_controller.js │ │ └── base_service.js │ ├── graphql │ │ ├── common │ │ │ ├── resolver.js │ │ │ ├── scalars │ │ │ │ └── date.js │ │ │ └── schema.graphql │ │ ├── menu │ │ │ ├── connector.js │ │ │ ├── resolver.js │ │ │ └── schema.graphql │ │ ├── mutation │ │ │ └── schema.graphql │ │ ├── query │ │ │ └── schema.graphql │ │ ├── role │ │ │ ├── connector.js │ │ │ ├── resolver.js │ │ │ └── schema.graphql │ │ └── user │ │ │ ├── connector.js │ │ │ ├── resolver.js │ │ │ └── schema.graphql │ ├── router.js │ ├── router │ │ ├── menu.js │ │ ├── qiniu.js │ │ ├── role.js │ │ ├── upload.js │ │ └── user.js │ └── service │ │ ├── menu.js │ │ ├── role.js │ │ └── user.js ├── appveyor.yml ├── config │ ├── config.default.js │ └── plugin.js ├── package-lock.json ├── package.json └── test │ └── app │ └── controller │ └── home.test.js ├── img ├── b1.png ├── configproxy.jpeg ├── database.png ├── login.jpeg ├── login.png ├── main.jpeg ├── page1.jpeg ├── page2.jpeg ├── startproject.jpeg ├── tiaoshi.jpeg └── webpackserverconfig.png ├── koaFrame.md └── server ├── config └── config.js ├── database └── mysqlUtil.js ├── package.json ├── process.json ├── routers ├── api │ ├── frame │ │ ├── auth.js │ │ ├── menu.js │ │ ├── role.js │ │ └── user.js │ ├── index.js │ ├── info │ │ ├── info.js │ │ └── infoCategory.js │ └── media.js ├── index.js └── mapi │ └── info │ └── minfo.js ├── server.js └── service ├── frame ├── menuService.js ├── roleService.js └── userService.js └── info ├── infoCategoryService.js └── infoService.js /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # production 7 | /build 8 | /dist 9 | 10 | # misc 11 | .idea/ 12 | .happypack 13 | .DS_Store 14 | 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/README.md: -------------------------------------------------------------------------------- 1 | # ice-reviews-management 2 | 3 | > 使用文档 4 | 5 | 使用: 6 | 7 | * 启动调试服务: `npm start` 8 | * 构建 dist: `npm run build` 9 | 10 | 目录结构: 11 | 12 | * react-router @4.x 默认采用 hashHistory 的单页应用 13 | * 入口文件: `src/index.js` 14 | * 导航配置: `src/menuConfig.js` 15 | * 路由配置: `src/routerConfig.js` 16 | * 路由入口: `src/router.jsx` 17 | * 布局文件: `src/layouts` 18 | * 通用组件: `src/components` 19 | * 页面文件: `src/pages` 20 | 21 | 效果图: 22 | 23 | ![screenshot](https://img.alicdn.com/tfs/TB1w64itqmWBuNjy1XaXXXCbXXa-2840-1596.png) 24 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/mock/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/IceBackend/LmmIceBackend/mock/.gitkeep -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/mock/bar-line-chart.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "SUCCESS", 3 | "data": {} 4 | } 5 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/mock/blank-layout.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "SUCCESS", 3 | "data": {} 4 | } 5 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/mock/complex-progress-table.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "SUCCESS", 3 | "data": { 4 | "total": 100, 5 | "pageSize": 10, 6 | "currentPage": 1, 7 | "list": [ 8 | { 9 | "title": "如何实现前后端通信", 10 | "createTime": "2017/12/19", 11 | "progress": 10, 12 | "priority": "高" 13 | }, 14 | { 15 | "title": "ECMAScript 新增了哪些功能", 16 | "createTime": "2017/12/19", 17 | "progress": 0, 18 | "priority": "中" 19 | }, 20 | { 21 | "title": "函数的扩展", 22 | "createTime": "2017/12/19", 23 | "progress": 100, 24 | "priority": "低" 25 | }, 26 | { 27 | "title": "Set 和 Map 数据结构", 28 | "createTime": "2017/12/19", 29 | "progress": 10, 30 | "priority": "高" 31 | }, 32 | { 33 | "title": "Generator 函数的语法", 34 | "createTime": "2017/12/19", 35 | "progress": 10, 36 | "priority": "高" 37 | }, 38 | { 39 | "title": "编程风格", 40 | "createTime": "2017/12/19", 41 | "progress": 10, 42 | "priority": "高" 43 | }, 44 | { 45 | "title": "dule 的加载实现", 46 | "createTime": "2017/12/19", 47 | "progress": 50, 48 | "priority": "中" 49 | }, 50 | { 51 | "title": "调用优化", 52 | "createTime": "2017/12/19", 53 | "progress": 10, 54 | "priority": "高" 55 | }, 56 | { 57 | "title": "Promise 对象", 58 | "createTime": "2017/12/19", 59 | "progress": 70, 60 | "priority": "低" 61 | } 62 | ] 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/mock/detail-table.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "SUCCESS", 3 | "data": {} 4 | } 5 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/mock/time-filter-table.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "SUCCESS", 3 | "data": { 4 | "total": 100, 5 | "pageSize": 10, 6 | "currentPage": 1, 7 | "list": [ 8 | { 9 | "title": "活动的标题文案", 10 | "memo": "需要备注的说明文案", 11 | "validity": "长期有效", 12 | "owner": "姓名" 13 | }, 14 | { 15 | "title": "活动的标题文案", 16 | "memo": "需要备注的说明文案", 17 | "validity": "长期有效", 18 | "owner": "姓名" 19 | }, 20 | { 21 | "title": "活动的标题文案", 22 | "memo": "需要备注的说明文案", 23 | "validity": "长期有效", 24 | "owner": "姓名" 25 | }, 26 | { 27 | "title": "活动的标题文案", 28 | "memo": "需要备注的说明文案", 29 | "validity": "长期有效", 30 | "owner": "姓名" 31 | }, 32 | { 33 | "title": "活动的标题文案", 34 | "memo": "需要备注的说明文案", 35 | "validity": "长期有效", 36 | "owner": "姓名" 37 | }, 38 | { 39 | "title": "活动的标题文案", 40 | "memo": "需要备注的说明文案", 41 | "validity": "长期有效", 42 | "owner": "姓名" 43 | }, 44 | { 45 | "title": "活动的标题文案", 46 | "memo": "需要备注的说明文案", 47 | "validity": "长期有效", 48 | "owner": "姓名" 49 | } 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LMMFrameIce", 3 | "version": "1.0.0", 4 | "description": "该模板适用于用户反馈和评价管理,布局方式采用左侧固定,右侧自适应方式,适合大量数据展示和界面操作", 5 | "author": "ice-admin@alibaba-inc.com", 6 | "homepage": "https://alibaba.github.io/ice/scaffold-preview/ice-reviews-management.html", 7 | "dependencies": { 8 | "@antv/data-set": "^0.8.5", 9 | "@icedesign/base": "^0.2.2", 10 | "@icedesign/container": "^0.1.2", 11 | "@icedesign/data-binder": "^0.1.1", 12 | "@icedesign/ellipsis": "^0.1.1", 13 | "@icedesign/form-binder": "^0.1.2", 14 | "@icedesign/icon": "^0.1.1", 15 | "@icedesign/img": "^0.1.1", 16 | "@icedesign/label": "^0.1.1", 17 | "@icedesign/layout": "^0.1.1", 18 | "@icedesign/menu": "^0.1.1", 19 | "@icedesign/skin": "^0.1.0", 20 | "@icedesign/styled-menu": "^0.1.1", 21 | "apollo-boost": "^0.1.7", 22 | "apollo-cache-inmemory": "^1.2.2", 23 | "apollo-link-context": "^1.0.8", 24 | "apollo-link-http": "^1.5.4", 25 | "axios": "^0.17.1", 26 | "bizcharts": "^3.1.8", 27 | "classnames": "^2.2.5", 28 | "echarts": "^4.1.0", 29 | "echarts-for-react": "^2.0.11", 30 | "enquire-js": "^0.1.2", 31 | "foundation-symbol": "^0.1.0", 32 | "graphql": "^0.13.2", 33 | "graphql-tag": "^2.9.2", 34 | "immutable": "^3.8.2", 35 | "is-hotkey": "^0.1.2", 36 | "prop-types": "^15.5.8", 37 | "react-apollo": "^2.1.4", 38 | "react-document-title": "^2.0.3", 39 | "react-router": "^4.2.0", 40 | "react-router-dom": "^4.2.2", 41 | "slate": "^0.32.5", 42 | "slate-react": "^0.11.6" 43 | }, 44 | "publishConfig": { 45 | "registry": "http://registry.npmjs.com", 46 | "access": "public" 47 | }, 48 | "devDependencies": { 49 | "babel-eslint": "^8.0.3", 50 | "eslint": "^4.13.1", 51 | "eslint-config-airbnb": "^16.1.0", 52 | "eslint-plugin-babel": "^4.1.1", 53 | "eslint-plugin-import": "^2.8.0", 54 | "eslint-plugin-jsx-a11y": "^6.0.3", 55 | "eslint-plugin-react": "^7.5.1", 56 | "ice-scripts": "^1.1.1" 57 | }, 58 | "scripts": { 59 | "start": "ice dev", 60 | "build": "ice build", 61 | "lint": "eslint . --ext '.js,.jsx' --fix" 62 | }, 63 | "buildConfig": { 64 | "theme": "@icedesign/skin", 65 | "entry": "src/index.js" 66 | }, 67 | "scaffoldConfig": { 68 | "name": "ice-reviews-management", 69 | "title": "ICE Reviews Management", 70 | "screenshot": "https://img.alicdn.com/tfs/TB1FrmItDtYBeNjy1XdXXXXyVXa-2840-1596.png" 71 | }, 72 | "title": "LmmIce", 73 | "proxyConfig": { 74 | "/**": { 75 | "enable": true, 76 | "target": "http://127.0.0.1:7001" 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Ice Design Ecommerce 10 | 11 | 12 | 13 |
14 | <% if (NODE_ENV === 'production') { %> 15 | 16 | <% } else { %> 17 | 18 | <% } %> 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/components/BasicNotFound/BasicNotFound.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import IceContainer from '@icedesign/container'; 4 | 5 | export default class BasicNotFound extends Component { 6 | static displayName = 'BasicNotFound'; 7 | 8 | render() { 9 | return ( 10 |
11 | 12 |
13 | 页面不存在 18 |
19 |

抱歉,你访问的页面不存在

20 |

21 | 您要找的页面没有找到,请返回首页继续浏览 22 |

23 |
24 |
25 |
26 |
27 | ); 28 | } 29 | } 30 | 31 | const styles = { 32 | notfoundContent: { 33 | display: 'flex', 34 | justifyContent: 'center', 35 | alignItems: 'center', 36 | minHeight: '500px', 37 | }, 38 | imgNotfound: { 39 | marginRight: '50px', 40 | }, 41 | title: { 42 | color: '#333', 43 | fontSize: '24px', 44 | margin: '20px 0', 45 | }, 46 | description: { 47 | color: '#666', 48 | fontSize: '16px', 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/components/BasicNotFound/index.js: -------------------------------------------------------------------------------- 1 | import BasicNotFound from './BasicNotFound'; 2 | 3 | export default BasicNotFound; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import Layout from '@icedesign/layout'; 3 | import cx from 'classnames'; 4 | import Logo from './Logo'; 5 | 6 | export default class Footer extends PureComponent { 7 | render() { 8 | const { className, style } = this.props; 9 | return ( 10 | 17 |
18 |
19 | 20 |
21 |
22 | © 2018 Theme designed by{' '} 23 | 29 | ICE 30 | 31 |
32 |
33 |
34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/components/LmmBreadCrumb.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Breadcrumb } from '@icedesign/base'; 3 | import { Link} from "react-router-dom"; 4 | 5 | import { createHashHistory } from 'history'; 6 | const hashHistory = createHashHistory(); 7 | 8 | export default class LmmBreadCrumb extends Component { 9 | 10 | render() { 11 | let length = this.props.links.length; 12 | return ( 13 |
14 | 15 | { 16 | this.props.links.map((item,index) => { 17 | if (index == 0){ 18 | return 19 |
{hashHistory.goBack()}}>{item.name}
20 |
21 | }else if (length - 1 == index){ 22 | return 23 | {item.name} 24 | 25 | }else{ 26 | return 27 | {item.link != undefined ? 28 | {item.name} 29 | :item.name 30 | } 31 | 32 | } 33 | 34 | }) 35 | } 36 |
37 |
38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/components/LmmTreeSelect.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { TreeSelect } from "@icedesign/base"; 3 | 4 | class LmmTreeSelect extends React.Component { 5 | 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | value:undefined, 11 | } 12 | } 13 | 14 | handleChange = (value, data) => { 15 | this.setState({value}); 16 | this.props.handleChange(value); 17 | } 18 | 19 | render() { 20 | return ( 21 | 30 | 31 | ); 32 | } 33 | } 34 | 35 | export default LmmTreeSelect; -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/components/Logo.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | export default class Logo extends PureComponent { 5 | render() { 6 | return ( 7 |
8 | 13 | 14 | LOGO 15 | 16 |
17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/index.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | // 载入默认全局样式 normalize 、.clearfix 和一些 mixin 方法等 3 | import '@icedesign/base/reset.scss'; 4 | import router from './router'; 5 | 6 | import { Feedback } from '@icedesign/base'; 7 | 8 | import ApolloClient from 'apollo-boost'; 9 | import { ApolloProvider } from 'react-apollo'; 10 | 11 | import { createHashHistory } from 'history'; 12 | const hashHistory = createHashHistory(); 13 | 14 | const client = new ApolloClient({ 15 | uri: '/graphql', 16 | fetchOptions: { 17 | credentials: 'include' 18 | }, 19 | request: async (operation) => { 20 | const token = await sessionStorage.getItem('token'); 21 | operation.setContext({ 22 | headers: { 23 | authorization: token ? `Bearer ${token}` : "", 24 | } 25 | }); 26 | }, 27 | onError: ({ graphQLErrors, networkError }) => { 28 | // if (graphQLErrors) { 29 | // console.log(graphQLErrors); 30 | // sendToLoggingService(graphQLErrors); 31 | // } 32 | // if (networkError) { 33 | hashHistory.replace('/login'); 34 | Feedback.toast.error('网络异常'); 35 | // } 36 | }, 37 | }); 38 | 39 | const ICE_CONTAINER = document.getElementById('ice-container'); 40 | 41 | if (!ICE_CONTAINER) { 42 | throw new Error('当前页面不存在
节点.'); 43 | } 44 | 45 | ReactDOM.render( 46 | 47 | {router} 48 | , ICE_CONTAINER); 49 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/layouts/AsideLayout/index.js: -------------------------------------------------------------------------------- 1 | import Layout from './Layout'; 2 | 3 | export default Layout; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/layouts/AsideLayout/scss/light.scss: -------------------------------------------------------------------------------- 1 | @import '~@icedesign/skin/variables.scss'; 2 | 3 | // Basic color variables 4 | $custom-layout-bg: #f3f9fb; 5 | 6 | // Layout light styles 7 | $ice-body-bg: #fbfbfb !default; 8 | $ice-layout-bg: $custom-layout-bg !default; 9 | $ice-toggle-btn-bg: $color-brand1-5 !default; 10 | $ice-toggle-btn-text-color: $color-white !default; 11 | $ice-collapse-btn-color: $color-text1-3 !default; 12 | $ice-logo-text-color: $color-white !default; 13 | $ice-icon-down-color: $color-text1-3 !default; 14 | 15 | $ice-header-menu-bg: transparent !default; 16 | $ice-header-text-color: $color-text1-3 !default; 17 | $ice-header-text-hover-color: $color-brand1-5 !default; 18 | 19 | $ice-aside-bg: #001529 !default; 20 | $ice-aside-menu-bg: transparent !default; 21 | $ice-aside-text-color: rgba(204, 204, 204, 0.95) !default; 22 | $ice-aside-sub-text-color: #999 !default; 23 | $ice-aside-text-hover-color: $color-white !default; 24 | $ice-aside-menu-item-selected-gradient-left-bg: $color-brand1-9 !default; 25 | $ice-aside-menu-item-selected-gradient-right-bg: $color-brand1-5 !default; 26 | $ice-aside-menu-item-selected-text-color: $color-white !default; 27 | $ice-aside-menu-submenu-item-hover-bg: transparent !default; 28 | $ice-aside-menu-submenu-vertical-bg: $color-white !default; 29 | $ice-aside-menu-collapse-bg: transparent !default; 30 | $ice-aside-menu-collapse-selected-text-color: $color-brand1-5 !default; 31 | $ice-aside-collapse-menu-text-color: $color-text1-3 !default; 32 | 33 | $ice-copyright-text-color: $color-text1-3 !default; 34 | $ice-copyright-text-hover-color: $color-brand1-5 !default; 35 | 36 | $theme: 'light'; 37 | 38 | @import './base.scss'; 39 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/layouts/AsideLayoutWithHeader/index.js: -------------------------------------------------------------------------------- 1 | import Layout from './Layout'; 2 | 3 | export default Layout; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/layouts/AsideLayoutWithHeader/scss/light.scss: -------------------------------------------------------------------------------- 1 | @import '~@icedesign/skin/variables.scss'; 2 | 3 | // Basic color variables 4 | $custom-layout-bg: #f3f9fb; 5 | 6 | // Layout light styles 7 | $ice-body-bg: #fbfbfb !default; 8 | $ice-layout-bg: $custom-layout-bg !default; 9 | $ice-toggle-btn-bg: $color-brand1-5 !default; 10 | $ice-toggle-btn-text-color: $color-white !default; 11 | $ice-collapse-btn-color: $color-text1-3 !default; 12 | $ice-logo-text-color: $color-white !default; 13 | $ice-icon-down-color: $color-text1-3 !default; 14 | 15 | $ice-header-menu-bg: transparent !default; 16 | $ice-header-text-color: $color-text1-3 !default; 17 | $ice-header-text-hover-color: $color-brand1-5 !default; 18 | 19 | $ice-aside-bg: #001529 !default; 20 | $ice-aside-menu-bg: transparent !default; 21 | $ice-aside-text-color: rgba(204, 204, 204, 0.95) !default; 22 | $ice-aside-sub-text-color: #999 !default; 23 | $ice-aside-text-hover-color: $color-white !default; 24 | $ice-aside-menu-item-selected-gradient-left-bg: $color-brand1-9 !default; 25 | $ice-aside-menu-item-selected-gradient-right-bg: $color-brand1-5 !default; 26 | $ice-aside-menu-item-selected-text-color: $color-white !default; 27 | $ice-aside-menu-submenu-item-hover-bg: transparent !default; 28 | $ice-aside-menu-submenu-vertical-bg: $color-white !default; 29 | $ice-aside-menu-collapse-bg: transparent !default; 30 | $ice-aside-menu-collapse-selected-text-color: $color-brand1-5 !default; 31 | $ice-aside-collapse-menu-text-color: $color-text1-3 !default; 32 | 33 | $ice-copyright-text-color: $color-text1-3 !default; 34 | $ice-copyright-text-hover-color: $color-brand1-5 !default; 35 | 36 | $theme: 'light'; 37 | 38 | @import './base.scss'; 39 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/layouts/BlankLayout/Layout.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Layout from '@icedesign/layout'; 3 | 4 | import './Layout.scss'; 5 | 6 | export default class BlankLayout extends Component { 7 | static propTypes = {}; 8 | 9 | static defaultProps = {}; 10 | 11 | render() { 12 | return ( 13 | 14 | {this.props.children} 15 | 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/layouts/BlankLayout/Layout.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .blank-layout { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/layouts/BlankLayout/index.js: -------------------------------------------------------------------------------- 1 | import Layout from './Layout'; 2 | 3 | export default Layout; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/menuConfig.js: -------------------------------------------------------------------------------- 1 | // 菜单配置 2 | // headerMenuConfig:头部导航配置 3 | 4 | const headerMenuConfig = [ 5 | // { 6 | // name: '首页', 7 | // path: '/', 8 | // icon: 'home', 9 | // }, 10 | // { 11 | // name: '反馈', 12 | // path: 'https://github.com/alibaba/ice', 13 | // external: true, 14 | // newWindow: true, 15 | // icon: 'message', 16 | // }, 17 | // { 18 | // name: '帮助', 19 | // path: 'https://alibaba.github.io/ice', 20 | // external: true, 21 | // newWindow: true, 22 | // icon: 'bangzhu', 23 | // }, 24 | ]; 25 | 26 | // asideMenuConfig:侧边导航配置 27 | 28 | const getAsideMenuConfig = () => { 29 | var menusJson = sessionStorage.getItem('menus'); 30 | const topmenus = JSON.parse(menusJson); 31 | 32 | let asideMenuConfig = [] 33 | 34 | if (topmenus == null || topmenus == undefined) { 35 | asideMenuConfig = []; 36 | } else{ 37 | const getSubMenus = menu => { 38 | let subs = menu.subs; 39 | let submenus = []; 40 | for (var i = 0; i < subs.length; i++) { 41 | var sub = subs[i]; 42 | submenus.push({ 43 | name: sub.title, 44 | path: sub.to, 45 | icon: menu.icon, 46 | children: getSubMenus(sub), 47 | }); 48 | } 49 | return submenus; 50 | } 51 | 52 | asideMenuConfig = topmenus.map(function(menu) { 53 | let realMenu = { 54 | icon: menu.icon, 55 | name: menu.title, 56 | path: menu.to, 57 | }; 58 | realMenu.children = getSubMenus(menu); 59 | return realMenu; 60 | }); 61 | } 62 | return asideMenuConfig; 63 | }; 64 | 65 | export { headerMenuConfig, getAsideMenuConfig }; 66 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Auth/Auth.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import UserLogin from './components/UserLogin'; 3 | 4 | export default class Auth extends Component { 5 | static displayName = 'Auth'; 6 | 7 | constructor(props) { 8 | super(props); 9 | this.state = {}; 10 | } 11 | 12 | render() { 13 | return ( 14 |
15 | 16 |
17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Auth/Auth.scss: -------------------------------------------------------------------------------- 1 | .auth-page { 2 | } 3 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Auth/components/UserLogin/UserLogin.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .user-login { 4 | .next-input.next-input-single { 5 | width: 240px; 6 | border-top: 0; 7 | border-left: 0; 8 | border-right: 0; 9 | border-color: #dcdcdc; 10 | border-radius: 0; 11 | input { 12 | padding-left: 25px; 13 | font-size: 13px; 14 | } 15 | } 16 | .next-checkbox-label { 17 | color: #999; 18 | font-size: 13px; 19 | } 20 | .content-wrapper { 21 | position: absolute; 22 | top: -100px; 23 | left: 0; 24 | right: 0; 25 | bottom: 0; 26 | max-width: 1080px; 27 | margin: 0 auto; 28 | display: flex; 29 | justify-content: space-around; 30 | align-items: center; 31 | .slogan { 32 | text-align: center; 33 | color: #fff; 34 | font-size: 36px; 35 | letter-spacing: 2px; 36 | line-height: 48px; 37 | } 38 | } 39 | } 40 | 41 | @media screen and (max-width: 720px) { 42 | .user-login { 43 | .content-wrapper { 44 | margin: 20px auto; 45 | top: 40px; 46 | max-width: 300px; 47 | display: block; 48 | .slogan { 49 | color: #666; 50 | font-size: 22px; 51 | line-height: 30px; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Auth/components/UserLogin/index.js: -------------------------------------------------------------------------------- 1 | import UserLogin from './UserLogin'; 2 | 3 | export default UserLogin; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Auth/index.js: -------------------------------------------------------------------------------- 1 | import Auth from './Auth'; 2 | export default Auth; 3 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Home/Home.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import ReviewDataChart from './components/ReviewDataChart'; 4 | import ReviewDetailInfo from './components/ReviewDetailInfo'; 5 | import ReviewOverview from './components/ReviewOverview'; 6 | import ReviewRequestTable from './components/ReviewRequestTable'; 7 | 8 | export default class Home extends Component { 9 | static displayName = 'Home'; 10 | 11 | constructor(props) { 12 | super(props); 13 | this.state = {}; 14 | } 15 | 16 | render() { 17 | return ( 18 |
19 | 20 | 21 | 22 | 23 |
24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Home/components/ReviewDataChart/index.js: -------------------------------------------------------------------------------- 1 | import ReviewDataChart from './ReviewDataChart'; 2 | 3 | export default ReviewDataChart; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Home/components/ReviewDetailInfo/index.js: -------------------------------------------------------------------------------- 1 | import ReviewDetailInfo from './ReviewDetailInfo'; 2 | 3 | export default ReviewDetailInfo; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Home/components/ReviewOverview/Progress.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export default class Progress extends Component { 5 | static displayName = 'Progress'; 6 | 7 | static propTypes = { 8 | percent: PropTypes.number, 9 | maxLength: PropTypes.number, 10 | extra: PropTypes.string, 11 | color: PropTypes.string, 12 | }; 13 | 14 | static defaultProps = { 15 | percent: 100, 16 | maxLength: 100, 17 | extra: '', 18 | color: '#5485f7', 19 | }; 20 | 21 | constructor(props) { 22 | super(props); 23 | this.state = {}; 24 | } 25 | 26 | render() { 27 | const { percent, extra, color, style } = this.props; 28 | const customStyles = { 29 | width: `${percent}%`, 30 | backgroundColor: color, 31 | }; 32 | 33 | return ( 34 |
35 | 36 | {extra ? {extra} : null} 37 |
38 | ); 39 | } 40 | } 41 | 42 | const styles = { 43 | progressWrapper: { 44 | display: 'flex', 45 | alignItems: 'center', 46 | }, 47 | progress: { 48 | display: 'inline-block', 49 | height: 5, 50 | marginRight: 5, 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Home/components/ReviewOverview/index.js: -------------------------------------------------------------------------------- 1 | import ReviewOverview from './ReviewOverview'; 2 | 3 | export default ReviewOverview; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Home/components/ReviewRequestTable/index.js: -------------------------------------------------------------------------------- 1 | import ReviewRequestTable from './ReviewRequestTable'; 2 | 3 | export default ReviewRequestTable; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Home/index.js: -------------------------------------------------------------------------------- 1 | import Home from './Home'; 2 | 3 | export default Home; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Menu/Menu.scss: -------------------------------------------------------------------------------- 1 | .user-page { 2 | } 3 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Menu/components/LmmOperate/LmmOperate.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Grid, Button } from '@icedesign/base'; 3 | import IceContainer from '@icedesign/container'; 4 | 5 | const { Row, Col } = Grid; 6 | import LmmTreeSelect from '../../../../components/LmmTreeSelect'; 7 | 8 | export default class LmmOperate extends Component { 9 | 10 | render() { 11 | return ( 12 | 13 | 14 | 15 | 选择菜单:{' '} 16 | 17 | 18 | { 21 | this.props.reloadData(value); 22 | }} /> 23 | 24 | 28 | 31 | 32 | 33 | 34 | ); 35 | } 36 | } 37 | 38 | const styles = { 39 | simpleFormDialog: { width: '640px' }, 40 | dialogContent: {}, 41 | formRow: { marginTop: 20 }, 42 | input: { width: '100%' }, 43 | formLabel: { lineHeight: '26px' }, 44 | label: { lineHeight: '28px', paddingRight: '10px' }, 45 | }; 46 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Menu/components/LmmOperate/index.js: -------------------------------------------------------------------------------- 1 | import LmmOperate from './LmmOperate'; 2 | 3 | export default LmmOperate; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Menu/components/SimpleFormDialog/index.js: -------------------------------------------------------------------------------- 1 | import SimpleFormDialog from './SimpleFormDialog'; 2 | 3 | export default SimpleFormDialog; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Menu/index.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | export default Menu; 3 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/NotFound/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import BasicNotFound from '../../components/BasicNotFound'; 3 | import './NotFound.scss'; 4 | 5 | export default class NotFound extends Component { 6 | static displayName = 'NotFound'; 7 | 8 | constructor(props) { 9 | super(props); 10 | this.state = {}; 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 17 |
18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/NotFound/NotFound.scss: -------------------------------------------------------------------------------- 1 | .not-found-page { 2 | 3 | } -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/NotFound/index.js: -------------------------------------------------------------------------------- 1 | import NotFound from './NotFound'; 2 | 3 | export default NotFound; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Role/Role.scss: -------------------------------------------------------------------------------- 1 | .role-page { 2 | } 3 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Role/components/LmmOperate/LmmOperate.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Grid, Button } from '@icedesign/base'; 3 | import IceContainer from '@icedesign/container'; 4 | 5 | const { Row, Col } = Grid; 6 | import LmmTreeSelect from '../../../../components/LmmTreeSelect'; 7 | 8 | export default class LmmOperate extends Component { 9 | 10 | render() { 11 | return ( 12 | 13 | 14 | 15 | 选择角色:{' '} 16 | 17 | 18 | { 21 | this.props.reloadData(value); 22 | }} /> 23 | 24 | 28 | 31 | 32 | 33 | 34 | ); 35 | } 36 | } 37 | 38 | const styles = { 39 | simpleFormDialog: { width: '640px' }, 40 | dialogContent: {}, 41 | formRow: { marginTop: 20 }, 42 | input: { width: '100%' }, 43 | formLabel: { lineHeight: '26px' }, 44 | label: { lineHeight: '28px', paddingRight: '10px' }, 45 | }; 46 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Role/components/LmmOperate/index.js: -------------------------------------------------------------------------------- 1 | import LmmOperate from './LmmOperate'; 2 | 3 | export default LmmOperate; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Role/components/SimpleFormDialog/index.js: -------------------------------------------------------------------------------- 1 | import SimpleFormDialog from './SimpleFormDialog'; 2 | 3 | export default SimpleFormDialog; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/Role/index.js: -------------------------------------------------------------------------------- 1 | import Role from './Role'; 2 | export default Role; 3 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/User/User.scss: -------------------------------------------------------------------------------- 1 | .user-page { 2 | } 3 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/User/components/LmmOperate/LmmOperate.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Button } from '@icedesign/base'; 3 | import IceContainer from '@icedesign/container'; 4 | 5 | export default class LmmOperate extends Component { 6 | render() { 7 | return ( 8 | 9 | 12 | 13 | ); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/User/components/LmmOperate/index.js: -------------------------------------------------------------------------------- 1 | import LmmOperate from './LmmOperate'; 2 | 3 | export default LmmOperate; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/User/components/SimpleFormDialog/index.js: -------------------------------------------------------------------------------- 1 | import SimpleFormDialog from './SimpleFormDialog'; 2 | 3 | export default SimpleFormDialog; 4 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/pages/User/index.js: -------------------------------------------------------------------------------- 1 | import User from './User'; 2 | export default User; 3 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/routerConfig.js: -------------------------------------------------------------------------------- 1 | // 以下文件格式为描述路由的协议格式 2 | // 你可以调整 routerConfig 里的内容 3 | // 变量名 routerConfig 为 iceworks 检测关键字,请不要修改名称 4 | 5 | import AsideLayout from './layouts/AsideLayout'; 6 | import Home from './pages/Home'; 7 | import NotFound from './pages/NotFound'; 8 | 9 | import Auth from './pages/Auth'; 10 | 11 | import BlankLayout from './layouts/BlankLayout'; 12 | import User from './pages/User'; 13 | 14 | import Role from './pages/Role'; 15 | import Menu from './pages/Menu'; 16 | 17 | const routerConfig = [ 18 | { 19 | path: '/', 20 | layout: AsideLayout, 21 | component: Home, 22 | }, 23 | { 24 | path: '/backend/users', 25 | layout: AsideLayout, 26 | component: User, 27 | }, 28 | { 29 | path: '/backend/menu', 30 | layout: AsideLayout, 31 | component: Menu, 32 | }, 33 | { 34 | path: '/login', 35 | component: Auth, 36 | }, 37 | { 38 | path: '/backend/role', 39 | layout: AsideLayout, 40 | component: Role, 41 | }, 42 | { 43 | path: '*', 44 | layout: AsideLayout, 45 | component: NotFound, 46 | }, 47 | ]; 48 | 49 | export default routerConfig; 50 | -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/utils/graphqlUtil.js: -------------------------------------------------------------------------------- 1 | import { Feedback } from '@icedesign/base'; 2 | 3 | import { createHashHistory } from 'history'; 4 | const hashHistory = createHashHistory(); 5 | 6 | import gql from "graphql-tag"; 7 | 8 | import { ApolloClient } from 'apollo-client'; 9 | import { createHttpLink } from 'apollo-link-http'; 10 | import { setContext } from 'apollo-link-context'; 11 | import { InMemoryCache } from 'apollo-cache-inmemory'; 12 | 13 | const httpLink = createHttpLink({ 14 | uri: '/graphql', 15 | }); 16 | 17 | const authLink = setContext((_, { headers }) => { 18 | // get the authentication token from local storage if it exists 19 | const token = sessionStorage.getItem('token'); 20 | // return the headers to the context so httpLink can read them 21 | return { 22 | headers: { 23 | ...headers, 24 | authorization: token ? `Bearer ${token}` : "", 25 | } 26 | } 27 | }); 28 | 29 | const client = new ApolloClient({ 30 | link: authLink.concat(httpLink), 31 | cache: new InMemoryCache() 32 | }); 33 | 34 | const graphqlUtil = {}; 35 | 36 | graphqlUtil.query = async (queryString) => { 37 | try { 38 | let graphqlRequest = new Promise((resolve,reject) => { 39 | client 40 | .query({ 41 | query: gql` 42 | ${queryString} 43 | ` 44 | }) 45 | .then(result => { 46 | let data = result.data; 47 | resolve(data); 48 | }).catch((error) => { 49 | Feedback.toast.error('网络异常'); 50 | hashHistory.replace('/login'); 51 | }) 52 | 53 | }) 54 | return graphqlRequest; 55 | } catch (e) { 56 | throw new Error("网络请求异常"); 57 | } 58 | }; 59 | 60 | graphqlUtil.queryWithParams = async (queryString,params) => { 61 | try { 62 | let graphqlRequest = new Promise((resolve,reject) => { 63 | client 64 | .query( 65 | { 66 | query:queryString, 67 | variables:params, 68 | fetchPolicy: 'network-only' 69 | }) 70 | .then(result => { 71 | let data = result.data; 72 | resolve(data); 73 | }).catch((error) => { 74 | Feedback.toast.error('网络异常'); 75 | hashHistory.replace('/login'); 76 | }) 77 | 78 | }) 79 | return graphqlRequest; 80 | } catch (e) { 81 | throw new Error("网络请求异常"); 82 | } 83 | }; 84 | 85 | graphqlUtil.mutation = async (mutationString,params) => { 86 | try { 87 | let graphqlRequest = new Promise((resolve,reject) => { 88 | client 89 | .mutate({ 90 | mutation: gql` 91 | ${mutationString} 92 | ` 93 | },{ 94 | variables:params, 95 | }) 96 | .then(result => { 97 | let data = result.data; 98 | resolve(data); 99 | }).catch((error) => { 100 | Feedback.toast.error('网络异常'); 101 | hashHistory.replace('/login'); 102 | }) 103 | }) 104 | return graphqlRequest; 105 | } catch (e) { 106 | throw new Error("网络请求异常"); 107 | } 108 | }; 109 | 110 | 111 | 112 | export default graphqlUtil; -------------------------------------------------------------------------------- /IceBackend/LmmIceBackend/src/utils/treeConvertUtil.js: -------------------------------------------------------------------------------- 1 | 2 | const loopTreeData = (treeNode, data, pid) => { 3 | let result = [], temp; 4 | for (var i = 0; i < data.length; i++) { 5 | const treeData = data[i]; 6 | const treeDataName = treeData[treeNode.title]; 7 | const treeDataKey = ""+treeData[treeNode.key]; 8 | const treeDataPkey = ""+treeData[treeNode.pkey]; 9 | 10 | if (treeDataPkey === pid) { 11 | let obj = {label: treeDataName, value: treeDataKey, key: treeDataKey}; 12 | temp = loopTreeData(treeNode,data, treeDataKey); 13 | if (temp.length > 0) { 14 | obj.children = temp; 15 | } 16 | result.push(obj); 17 | } 18 | } 19 | 20 | return result; 21 | }; 22 | 23 | export const getTreeData = (treeNode,dataList,pid) => { 24 | return loopTreeData(treeNode,dataList,pid); 25 | } -------------------------------------------------------------------------------- /IceFrame.md: -------------------------------------------------------------------------------- 1 | # 基于Ice(飞冰)工具搭建的后台权限管理 2 | 3 | ## 介绍 4 | 5 | 系统后台使用[egg](https://eggjs.org/)框架,并利用Graphql来做接口层. 利用[apollo](https://www.apollographql.com/docs/react/)搭建server端,与前端。 6 | 7 | ## 项目截图 8 | 9 | ![登录](img/login.jpeg) 10 | 11 | ![首页](img/main.jpeg) 12 | 13 | ![其他页面1](img/page1.jpeg) 14 | 15 | ![其他页面2](img/page2.jpeg) 16 | 17 | ## 项目启动 18 | 19 | ### mysql数据库导入 20 | 21 | 使用 NewLmmFrame.sql 生成数据库相关数据 22 | 23 | ### 后端接口启动 24 | 25 | * 进入到eggserver目录下 26 | * 使用vscode打开eggserver工程 27 | 28 | * 修改mysql数据库配置,打开config.default.js 文件 29 | 30 | * 修改配置 31 | 32 | ``` 33 | config.mysql = { 34 | // 单数据库信息配置 35 | client: { 36 | // host 37 | host: '127.0.0.1', 38 | // 端口号 39 | port: '3306', 40 | // 用户名 41 | user: 'root', 42 | // 密码 43 | password: '11111', 44 | // 数据库名 45 | database: 'NewLmmFrame', 46 | }, 47 | // 是否加载到 app 上,默认开启 48 | app: true, 49 | // 是否加载到 agent 上,默认关闭 50 | agent: false, 51 | }; 52 | 53 | ``` 54 | 55 | * 安装依赖 56 | 57 | ``` 58 | npm install 59 | ``` 60 | 61 | * 开发环境启动 62 | 63 | ``` 64 | npm run dev 65 | ``` 66 | 67 | * 部署环境启动 68 | 69 | ``` 70 | npm start 71 | ``` 72 | 73 | * 部署环境停止 74 | 75 | ``` 76 | npm stop 77 | ``` 78 | 79 | ### 前端开发(前端工程目录是IceBackend) 80 | 81 | * [下载飞冰工具](https://alibaba.github.io/ice/iceworks) 82 | 83 | > 飞冰工具用于打开工程,新增页面,启动调试模式,构建工程,安装依赖等相关操作。 84 | 85 | * 进入到IceBackend目录下 86 | 87 | * 使用飞冰工具打开工程(注意:飞冰工具不是代码编辑器,主要用于工程构建调试等操作,代码编辑请使用vscode等相关工具),配置接口代理,解决跨域问题(正式环境,把前端打包成静态文件,放在后台接口同域下,或者统一使用ngnix,进行转发) 88 | 89 | ![调试页面](img/tiaoshi.jpeg) 90 | ![设置接口代理](img/configproxy.jpeg) 91 | 92 | * 启动前端工程 93 | 94 | ![启动工程](img/startproject.jpeg) 95 | 96 | * 浏览器打开 97 | 98 | [访问登录页](http://localhost:4444/#/login) 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###自己搭建的后台角色管理系统 2 | 3 | * [使用最新的飞冰工具搭建](IceFrame.md) 4 | 5 | * [基于egg以及antd搭建](koaFrame.md) 6 | 7 | ###页面展示 8 | 9 | ![登录](img/login.jpeg) 10 | 11 | ![首页](img/main.jpeg) 12 | 13 | ![其他页面1](img/page1.jpeg) 14 | 15 | ![其他页面2](img/page2.jpeg) -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CLAntDFrame", 3 | "version": "1.0.0", 4 | "description": "react-antd", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "node ./tools/gulpfile.js&&node ./tools/webpack.config.js", 8 | "pub": "node ./tools/gulpfile.js&&export NODE_ENV=__PROD__&&node ./tools/webpack.config.js" 9 | }, 10 | "win-scripts": { 11 | "dev": "node ./tools/gulpfile.js&&node ./tools/webpack.config.js", 12 | "pub": "node ./tools/gulpfile.js&&set NODE_ENV=__PROD__&&node ./tools/webpack.config.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "" 17 | }, 18 | "author": "ClTeam", 19 | "license": "", 20 | "dependencies": { 21 | "antd": "^3.1.0", 22 | "babel-core": "^6.1.4", 23 | "babel-loader": "^6.1.0", 24 | "babel-plugin-import": "^1.0.1", 25 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 26 | "babel-plugin-transform-jsx": "^2.0.0", 27 | "babel-plugin-transform-runtime": "^6.15.0", 28 | "babel-preset-es2015": "^6.9.0", 29 | "babel-preset-es2015-loose": "^7.0.0", 30 | "babel-preset-react": "^6.1.4", 31 | "babel-preset-stage-0": "^6.16.0", 32 | "bluebird": "^3.5.0", 33 | "classnames": "^2.2.3", 34 | "clean-webpack-plugin": "^0.1.4", 35 | "copy-webpack-plugin-hash": "^3.0.2", 36 | "css-loader": "^0.22.0", 37 | "echarts-for-react": "^1.2.1", 38 | "es6-promise": "^4.0.5", 39 | "exports-loader": "^0.6.2", 40 | "expose-loader": "^0.7.1", 41 | "express": "^4.13.4", 42 | "extract-text-webpack-plugin-steamer": "^1.0.1", 43 | "file-loader": "^0.8.4", 44 | "gulp": "^3.9.1", 45 | "gulp-replace": "^0.5.4", 46 | "gulp-util": "^3.0.7", 47 | "gulp.spritesmith-multi": "^3.0.0", 48 | "html-loader": "^0.3.0", 49 | "html-res-webpack-plugin": "^1.1.2", 50 | "http-proxy": "^1.12.0", 51 | "image-webpack-loader": "^1.8.0", 52 | "imports-loader": "^0.6.3", 53 | "isomorphic-fetch": "^2.2.1", 54 | "less": "^2.7.1", 55 | "less-loader": "^2.2.3", 56 | "lodash.merge": "^4.4.0", 57 | "merge-stream": "^1.0.0", 58 | "preact": "^5.6.0", 59 | "preact-redux": "^1.0.1", 60 | "proxy-middleware": "^0.15.0", 61 | "react": "^15.1.0", 62 | "react-addons-perf": "^15.1.0", 63 | "react-dom": "^15.1.0", 64 | "react-hot-loader": "^1.3.0", 65 | "react-redux": "^4.4.5", 66 | "react-router": "^2.4.1", 67 | "react-router-redux": "^4.0.6", 68 | "redux": "^3.5.2", 69 | "redux-devtools": "^3.3.1", 70 | "redux-devtools-dock-monitor": "^1.1.1", 71 | "redux-devtools-log-monitor": "^1.0.11", 72 | "redux-thunk": "^1.0.3", 73 | "reqwest": "^2.0.5", 74 | "reselect": "^2.5.2", 75 | "run-sequence": "^1.1.5", 76 | "simditor": "^2.3.6", 77 | "steamer-browserutils": "^0.5.0", 78 | "style-loader": "^0.13.0", 79 | "superagent": "^3.5.2", 80 | "superagent-bluebird-promise": "^4.2.0", 81 | "url-loader": "^0.5.6", 82 | "webpack": "^1.13.1", 83 | "webpack-dev-middleware": "^1.6.1", 84 | "webpack-dev-server": "^1.14.1", 85 | "webpack-hot-middleware": "^2.10.0", 86 | "webpack-md5-hash": "^0.0.5" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /backend/src/actions/actionType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/18. 3 | */ 4 | 5 | 6 | 7 | 8 | /** 登录 ...*/ 9 | export const LOGIN_AJAX_START = "login_ajax_start"; 10 | export const LOGIN_SUCCESS = "login_success"; 11 | export const LOGIN_FAILURE = "login_failure"; 12 | 13 | /** 用户列表 ...*/ 14 | export const USER_LIST = "user_list"; 15 | export const USER_ROLE_FORM_SHOW = "user_role_form_show"; 16 | export const USER_ROLE_FORM_HIDE = "user_role_form_hide"; 17 | export const USER_ROLE_ALL= "user_role_all"; 18 | export const USER_ROLE_FORM_LOADING= "USER_ROLE_FORM_LOADING"; 19 | 20 | /** 菜单列表 ...*/ 21 | export const MENU_LIST = "menu_list"; 22 | 23 | /** 角色列表 ...*/ 24 | export const ROLE_LIST = "role_list"; 25 | export const ROLE_MENU_FORM_SHOW = "role_menu_form_show"; 26 | export const ROLE_MENU_FORM_HIDE = "role_menu_form_hide"; 27 | export const ROLE_MENU_ALL= "role_menu_all"; 28 | export const ROLE_MENU_FORM_LOADING= "role_menu_form_loading"; 29 | 30 | /** 信息发布 ...*/ 31 | export const INFO_ADD = "INFO_ADD"; 32 | export const INFO_EDIT = "INFO_EDIT"; 33 | export const INFO_LIST = "INFO_LIST"; 34 | export const INFOC_LIST = "INFOC_LIST"; 35 | -------------------------------------------------------------------------------- /backend/src/actions/common/commonActionType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/27. 3 | */ 4 | /** table状态 ...*/ 5 | export const TABLE_LOADING = "table_loading"; 6 | export const TABLE_NORMAL = "table_normal"; 7 | 8 | /** form状态 ...*/ 9 | export const FORM_ADD = "form_add"; 10 | export const FORM_EDIT = "form_edit"; 11 | export const FORM_HIDE = "form_hide"; -------------------------------------------------------------------------------- /backend/src/actions/common/formAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/20. 3 | */ 4 | import {FORM_ADD,FORM_EDIT,FORM_HIDE} from './commonActionType' 5 | 6 | const formEditAction = (formdata)=> { 7 | return { 8 | type: FORM_EDIT, 9 | payload: { 10 | visible:true, 11 | formdata:formdata, 12 | formedit:true, 13 | } 14 | } 15 | }; 16 | 17 | const formAddAction= ()=> { 18 | return { 19 | type: FORM_ADD, 20 | payload: { 21 | visible:true, 22 | formdata:{}, 23 | formedit:false, 24 | } 25 | } 26 | }; 27 | 28 | const formHideAction = ()=> { 29 | return { 30 | type: FORM_HIDE, 31 | payload: { 32 | visible:false, 33 | formdata:{}, 34 | formedit:false, 35 | } 36 | } 37 | }; 38 | 39 | export const formAdd=() => { 40 | return dispatch => { 41 | dispatch(formAddAction()); 42 | } 43 | }; 44 | 45 | export const formEdit=(formdata) =>{ 46 | return dispatch => { 47 | dispatch(formEditAction(formdata)); 48 | } 49 | }; 50 | 51 | export const formHide=() => { 52 | return dispatch => { 53 | dispatch(formHideAction()); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /backend/src/actions/common/tableAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/27. 3 | */ 4 | import {TABLE_LOADING,TABLE_NORMAL} from './commonActionType' 5 | 6 | function dataSourceAction(type,total,rows) { 7 | return { 8 | type: type, 9 | payload: { 10 | dataSource:rows, 11 | total:total 12 | } 13 | } 14 | } 15 | 16 | const tableLoadingAction = ()=> { 17 | return { 18 | type: TABLE_LOADING, 19 | payload: { 20 | loading:true 21 | } 22 | } 23 | } 24 | 25 | const tableNormalAction = ()=> { 26 | return { 27 | type: TABLE_NORMAL, 28 | payload: { 29 | loading:false 30 | } 31 | } 32 | } 33 | 34 | export const setTableLoading=() => { 35 | return dispatch => { 36 | dispatch(tableLoadingAction()); 37 | } 38 | }; 39 | 40 | export const setTableNormal=() =>{ 41 | return dispatch => { 42 | dispatch(tableNormalAction()); 43 | } 44 | }; 45 | 46 | export const setTableDataSource=(type,total,rows) => { 47 | return dispatch => { 48 | dispatch(dataSourceAction(type,total,rows)); 49 | } 50 | }; -------------------------------------------------------------------------------- /backend/src/actions/frameMenuAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/21. 3 | */ 4 | import {MENU_LIST} from './actionType' 5 | import fetchUtil from '../libs/fetchUtil'; 6 | import { replace } from 'react-router-redux' 7 | import {setTableNormal,setTableLoading} from './common/tableAction' 8 | 9 | function menuListaction(total,rows) { 10 | return { 11 | type: MENU_LIST, 12 | payload: { 13 | menulist:rows, 14 | total:total 15 | } 16 | } 17 | } 18 | 19 | export const getMenuList = (page,pmenuId) => { 20 | return dispatch => { 21 | dispatch(setTableLoading()) 22 | let url = `/api/menu/list?page=${page}&size=10`; 23 | if (pmenuId != undefined){ 24 | url+=`&pmenuId=${pmenuId}` 25 | } 26 | fetchUtil.get(url) 27 | .then((rs)=>{ 28 | dispatch(menuListaction(rs.count,rs.rows)); 29 | dispatch(setTableNormal()) 30 | },e =>{ 31 | dispatch(replace('/user/login')); 32 | dispatch(setTableNormal()) 33 | }); 34 | } 35 | }; 36 | 37 | export const deleteMenu = (menuId,callback) => { 38 | return dispatch => { 39 | fetchUtil.get(`/api/menu/delete?id=${menuId}`) 40 | .then((rs)=>{ 41 | callback(); 42 | },e =>{ 43 | dispatch(setTableNormal()) 44 | }); 45 | } 46 | }; -------------------------------------------------------------------------------- /backend/src/actions/infoAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/27. 3 | */ 4 | import {setTableLoading,setTableNormal,setTableDataSource} from './common/tableAction' 5 | import fetchUtil from '../libs/fetchUtil'; 6 | import { replace } from 'react-router-redux'; 7 | 8 | import {INFO_ADD,INFO_EDIT,INFO_LIST} from './actionType'; 9 | 10 | const infoAddAction = ()=> { 11 | return { 12 | type:INFO_ADD, 13 | payload:{ 14 | formData:{}, 15 | formedit:false, 16 | } 17 | } 18 | }; 19 | 20 | const infoEditAction = (formdata)=> { 21 | return { 22 | type:INFO_EDIT, 23 | payload:{ 24 | formData:formdata, 25 | formedit:true, 26 | } 27 | } 28 | }; 29 | 30 | export const infoAdd = () => { 31 | return dispatch => { 32 | dispatch(infoAddAction()); 33 | } 34 | }; 35 | 36 | export const infoEdit = (formdata) => { 37 | return dispatch => { 38 | dispatch(infoEditAction(formdata)); 39 | } 40 | }; 41 | 42 | export const getInfoList = (page,categoryId) => { 43 | return dispatch => { 44 | dispatch(setTableLoading()) 45 | let url = `/api/info/list?page=${page}&size=10`; 46 | if (categoryId){ 47 | url+=`&categoryId=${categoryId}`; 48 | } 49 | fetchUtil.get(url) 50 | .then((rs)=>{ 51 | dispatch(setTableDataSource(INFO_LIST,rs.count,rs.rows)); 52 | dispatch(setTableNormal()) 53 | },e =>{ 54 | dispatch(replace('/user/login')); 55 | dispatch(setTableNormal()) 56 | }); 57 | } 58 | }; 59 | 60 | export const deleteInfo = (id) => { 61 | return dispatch => { 62 | fetchUtil.get(`/api/info/delete?id=${id}`) 63 | .then((rs)=>{ 64 | dispatch(getInfoList(1)) 65 | },e =>{ 66 | dispatch(setTableNormal()) 67 | }); 68 | } 69 | }; 70 | 71 | export const getInfoDetail = (id) => { 72 | return dispatch => { 73 | fetchUtil.get(`/api/info/detail?infoId=${id}`) 74 | .then((rs)=>{ 75 | dispatch(infoEditAction(rs.info)) 76 | },e =>{ 77 | dispatch(setTableNormal()) 78 | }); 79 | } 80 | }; -------------------------------------------------------------------------------- /backend/src/actions/infoCategoryAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/27. 3 | */ 4 | import {setTableLoading,setTableNormal,setTableDataSource} from './common/tableAction' 5 | import fetchUtil from '../libs/fetchUtil'; 6 | import { replace } from 'react-router-redux' 7 | import {INFOC_LIST} from './actionType' 8 | 9 | export const getInfoCList = (page,pCategoryId) => { 10 | return dispatch => { 11 | dispatch(setTableLoading()) 12 | let url = `/api/infoCategory/list?page=${page}&size=10`; 13 | if (pCategoryId){ 14 | url+=`&pCategoryId=${pCategoryId}`; 15 | } 16 | fetchUtil.get(url) 17 | .then((rs)=>{ 18 | dispatch(setTableDataSource(INFOC_LIST,rs.count,rs.rows)); 19 | dispatch(setTableNormal()) 20 | },e =>{ 21 | dispatch(replace('/user/login')); 22 | dispatch(setTableNormal()) 23 | }); 24 | 25 | } 26 | } 27 | 28 | export const deleteInfoC = (id) => { 29 | return dispatch => { 30 | fetchUtil.get(`/api/infoCategory/delete?id=${id}`) 31 | .then((rs)=>{ 32 | dispatch(getInfoCList(1)) 33 | },e =>{ 34 | dispatch(setTableNormal()) 35 | }); 36 | } 37 | } -------------------------------------------------------------------------------- /backend/src/actions/loginAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/20. 3 | */ 4 | import {LOGIN_AJAX_START,LOGIN_SUCCESS,LOGIN_FAILURE} from './actionType' 5 | import { replace } from 'react-router-redux' 6 | import { message } from 'antd'; 7 | import fetchUtil from '../libs/fetchUtil'; 8 | 9 | function loginAjaxStartAction() { 10 | return { 11 | type: LOGIN_AJAX_START, 12 | payload:{ 13 | loginState:"start", 14 | msg:"" 15 | }, 16 | } 17 | } 18 | 19 | function loginSuccessAction() { 20 | return { 21 | type: LOGIN_SUCCESS, 22 | payload: { 23 | loginState: "success", 24 | msg: "" 25 | } 26 | }; 27 | } 28 | 29 | function loginFailedAction(msg) { 30 | return { 31 | type: LOGIN_FAILURE, 32 | payload: { 33 | loginState: "fail", 34 | msg: msg 35 | } 36 | }; 37 | } 38 | 39 | function loopTreeData(data, pid){ 40 | var result = [], temp; 41 | for (var i = 0; i < data.length; i++) { 42 | if (data[i].pmenuId == pid) { 43 | var obj = { 44 | "id":data[i].menuId, 45 | "pid":data[i].pmenuId, 46 | "title": data[i].name, 47 | "key":data[i].menuKey, 48 | "tag":data[i].tag, 49 | "to":data[i].tourl}; 50 | temp = loopTreeData(data, data[i].menuId); 51 | if (temp!=undefined) { 52 | if (temp.length > 0) { 53 | obj.subs = temp; 54 | }else{ 55 | obj.subs = []; 56 | } 57 | }else{ 58 | obj.subs = []; 59 | } 60 | result.push(obj); 61 | } 62 | } 63 | return result; 64 | } 65 | 66 | export default (username,password) => { 67 | return dispatch => { 68 | dispatch(loginAjaxStartAction()); 69 | 70 | fetchUtil.post('/api/auth/login',{ 71 | username: username, 72 | password: password}) 73 | .then((rs)=>{ 74 | sessionStorage.setItem('login', 'true'); 75 | sessionStorage.setItem('name',rs.name); 76 | sessionStorage.setItem('token',rs.token); 77 | var menus = loopTreeData(rs.menus,-1); 78 | var str = JSON.stringify(menus); 79 | sessionStorage.setItem('menus', str); 80 | dispatch(loginSuccessAction()); 81 | dispatch(replace('/')); 82 | 83 | },e =>{ 84 | message.info("用户密码错误"); 85 | dispatch(loginFailedAction()); 86 | }); 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /backend/src/api/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/18. 3 | */ 4 | 5 | require('es6-promise').polyfill(); 6 | require('isomorphic-fetch'); 7 | 8 | import { constant } from '../libs/constant'; 9 | 10 | import {Ajax} from '../libs/common'; 11 | 12 | function status(response){ 13 | console.log(response.status); 14 | if(response.status>=200 && response.status<300){ 15 | return Promise.resolve(response); 16 | } 17 | else{ 18 | return Promise.reject(new Error(response.statusText)); 19 | } 20 | } 21 | 22 | function json(response){ 23 | return response.json(); 24 | } 25 | 26 | const userLogin = (username,password) => { 27 | // source file is iso-8859-15 but it is converted to utf-8 automatically 28 | 29 | const url = constant.urlPrex+'/auth/login'; 30 | 31 | // fetch(url,{ 32 | // method:"post", 33 | // body:`username=${username}&password=${password}` 34 | // }) 35 | // .then(status) 36 | // .then(json) 37 | // .then(function(data){ 38 | // console.log("请求成功,JSON解析后的响应数据为:",data); 39 | // }) 40 | // .catch(function(err){ 41 | // console.log("Fetch错误:"+err); 42 | // }); 43 | 44 | const data = { 45 | username:username, 46 | password:password 47 | } 48 | 49 | Ajax.post('/auth/login', data).then((d) => { 50 | 51 | sessionStorage.setItem('login', 'true'); 52 | sessionStorage.setItem('name',d.data.name); 53 | sessionStorage.setItem('token',d.data.token); 54 | 55 | if (d.success) { 56 | 57 | var menus = this.loopTreeData(d.menus,-1); 58 | var str = JSON.stringify(menus); 59 | sessionStorage.setItem('menus', str); 60 | if (location.state && location.state.nextPathname) { 61 | router.replace(location.state.nextPathname); 62 | } else { 63 | router.replace('/'); 64 | } 65 | }else{ 66 | message.info('用户密码错误'); 67 | } 68 | }); 69 | 70 | }; 71 | 72 | export default userLogin; -------------------------------------------------------------------------------- /backend/src/components/CLComplexMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Menu,Icon,Breadcrumb,Dropdown,Row,Col} from 'antd'; 3 | const SubMenu = Menu.SubMenu; 4 | 5 | class CLComplexMenu extends React.Component { 6 | render() { 7 | return ( 8 | {this.props.title}}> 9 | {this.props.menus.map(function(menu) { 10 | return {menu.title}; 11 | })} 12 | 13 | ); 14 | } 15 | } 16 | 17 | export default CLComplexMenu; 18 | -------------------------------------------------------------------------------- /backend/src/components/CLContentCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Icon} from 'antd'; 3 | import styles from './CLContentCard.less'; 4 | 5 | class CLContentCard extends React.Component { 6 | 7 | static defaultProps = { 8 | minHeight: 737, 9 | }; 10 | 11 | constructor() { 12 | super(); 13 | }; 14 | 15 | render() { 16 | const minHeight = this.props.minHeight; 17 | const mHeight = parseInt(minHeight); 18 | return ( 19 |
20 |
{this.props.title}
21 |
22 | {this.props.children} 23 |
24 | ); 25 | } 26 | } 27 | 28 | export default CLContentCard; 29 | -------------------------------------------------------------------------------- /backend/src/components/CLContentCard.less: -------------------------------------------------------------------------------- 1 | .cardtitle{ 2 | font-size: 15px; 3 | } 4 | 5 | .carddivier{ 6 | width: 100%; 7 | height: 1px; 8 | background-color: #ECECEC; 9 | margin-top: 10px; 10 | margin-bottom: 10px; 11 | } 12 | 13 | .close{ 14 | display: -webkit-flex; /* Safari */ 15 | display: flex; 16 | justify-content:flex-end; 17 | } -------------------------------------------------------------------------------- /backend/src/components/CLContentCardWithClose.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Icon, 4 | Row, 5 | Col 6 | } from 'antd'; 7 | import styles from './CLContentCard.less'; 8 | 9 | class CLContentCardWithClose extends React.Component { 10 | 11 | static defaultProps = { 12 | minHeight: 737, 13 | }; 14 | 15 | constructor() { 16 | super(); 17 | }; 18 | 19 | render() { 20 | const minHeight = this.props.minHeight; 21 | const mHeight = parseInt(minHeight); 22 | return ( 23 |
24 |
25 | 26 | 27 | {this.props.title} 28 | 29 | 30 |
31 | 32 |
33 | 34 |
35 |
36 |
37 |
38 | {this.props.children} 39 |
40 |
41 | ); 42 | } 43 | } 44 | 45 | export default CLContentCardWithClose; -------------------------------------------------------------------------------- /backend/src/components/CLForm.less: -------------------------------------------------------------------------------- 1 | .formItemContainer{ 2 | margin-top: 10px; 3 | margin-bottom: 10px; 4 | font-size: 14px; 5 | } 6 | 7 | .formItemTitle{ 8 | margin-top:5px; 9 | margin-bottom:5px; 10 | } -------------------------------------------------------------------------------- /backend/src/components/CLSimditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Simditor from 'simditor'; 4 | require('simditor/styles/simditor.css'); 5 | 6 | class CLSimditor extends React.Component{ 7 | 8 | constructor(props) { 9 | super(props); 10 | } 11 | 12 | componentWillReceiveProps(nextProps) { 13 | this.setState(nextProps); 14 | 15 | const value = nextProps.value; 16 | if (value) { 17 | 18 | if (this.getValue() !== value){ 19 | this.editor.setValue(value); 20 | } 21 | } 22 | }; 23 | 24 | 25 | 26 | componentDidMount() { 27 | this.initEditor(); 28 | } 29 | 30 | initEditor = ()=>{ 31 | var textbox = ReactDOM.findDOMNode(this.refs.textarea); 32 | this.editor = new Simditor({ 33 | textarea: textbox, 34 | toolbar:[ 35 | 'title','bold','italic','underline','strikethrough','fontScale','color', 36 | 'ol' , 'ul' , 'blockquote','code' , 'table','link','image','hr' , 'indent', 37 | 'outdent','alignment' 38 | ], 39 | upload:{ 40 | url:"/api/media/simditorUploadImage", 41 | params: null, 42 | connectionCount: 3, 43 | leaveConfirm: 'Uploading is in progress, are you sure to leave this page?' 44 | } 45 | }); 46 | this.editor.on('valuechanged', this._handleValueChange); 47 | }; 48 | 49 | getValue = () => { 50 | return this.editor ? this.editor.getValue() : ''; 51 | }; 52 | 53 | _handleValueChange = (e) => { 54 | var changeFun = this.props.onValueChange; 55 | changeFun(this.getValue()); 56 | }; 57 | 58 | render(){ 59 | return( 60 |
61 | 62 |
63 | ); 64 | } 65 | } 66 | 67 | export default CLSimditor; -------------------------------------------------------------------------------- /backend/src/components/CLTableViewCell.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { 4 | Table,Icon,Input 5 | } from 'antd'; 6 | 7 | class CLTableViewCell extends React.Component { 8 | state = { 9 | value: this.props.value, 10 | editable: false, 11 | } 12 | handleChange = (e) => { 13 | const value = e.target.value; 14 | this.setState({ value }); 15 | } 16 | check = () => { 17 | this.setState({ editable: false }); 18 | if (this.props.onChange) { 19 | this.props.onChange(this.state.value); 20 | } 21 | } 22 | edit = () => { 23 | this.setState({ editable: true }); 24 | } 25 | render() { 26 | const { value, editable } = this.state; 27 | return ( 28 |
29 | { 30 | editable ? 31 |
32 | 37 | 42 |
43 | : 44 |
45 | {value || ' '} 46 | 51 |
52 | } 53 |
54 | ); 55 | } 56 | } 57 | 58 | export default CLTableViewCell; -------------------------------------------------------------------------------- /backend/src/components/CLTopMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './CLTopMenu.less'; 3 | 4 | const CLTopMenu = ({isOnFoucs,icon,title,onClickMenu}) => { 5 | return
onClickMenu()} className={isOnFoucs? styles.menuContainerFoucs: styles.menuContainerNormal}> 6 | {title} 7 |
8 | } 9 | 10 | export default CLTopMenu; 11 | -------------------------------------------------------------------------------- /backend/src/components/CLTopMenu.less: -------------------------------------------------------------------------------- 1 | .menuContainerNormal { 2 | background-color: #fff; 3 | height: 64px; 4 | display: -webkit-flex; /* Safari */ 5 | display: flex; 6 | align-items:center; 7 | padding:15px; 8 | border-top: 3px solid #fff; 9 | cursor:pointer; 10 | } 11 | 12 | .menuContainerFoucs { 13 | // background-color: #e2edf5; 14 | height: 64px; 15 | display: -webkit-flex; /* Safari */ 16 | display: flex; 17 | align-items:center; 18 | border-top: 3px solid #0083CD; 19 | padding:15px; 20 | cursor:pointer; 21 | } 22 | 23 | .menuTitleNormal{ 24 | font-size: 15px; 25 | color:#969aa7; 26 | } 27 | 28 | .menuTitleFoucs{ 29 | font-size: 15px; 30 | color:#969aa7; 31 | } 32 | 33 | .topMenuCotainer{ 34 | display: -webkit-flex; /* Safari */ 35 | display: flex; 36 | flex:1; 37 | align-items:center; 38 | flex-direction: row ; 39 | } -------------------------------------------------------------------------------- /backend/src/components/CLTopMenus.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './CLTopMenu.less'; 3 | import CLTopMenu from 'components/CLTopMenu' 4 | 5 | class CLTopMenus extends React.Component{ 6 | 7 | constructor(props){ 8 | super(props) 9 | this.state = { 10 | selectMenu:-1, 11 | } 12 | } 13 | 14 | onClickMenu = (id,i) => { 15 | this.setState({selectMenu:i}); 16 | this.props.onClickMenu(id); 17 | } 18 | 19 | render() { 20 | return
21 | {this.props.menus.map((item,i) => { 22 | return {this.onClickMenu(item.id,i)}} 26 | title={item.title} /> 27 | })} 28 |
29 | } 30 | 31 | } 32 | 33 | export default CLTopMenus; 34 | 35 | 36 | -------------------------------------------------------------------------------- /backend/src/components/CLTree.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Tree 4 | } from 'antd'; 5 | const TreeNode = Tree.TreeNode; 6 | import fetchUtil from '../libs/fetchUtil'; 7 | 8 | function setLeaf(treeData, curKey, level) { 9 | const loopLeaf = (data, lev) => { 10 | const l = lev - 1; 11 | data.forEach((item) => { 12 | if ((item.key.length > curKey.length) ? item.key.indexOf(curKey) !== 0 : 13 | curKey.indexOf(item.key) !== 0) { 14 | return; 15 | } 16 | if (item.children) { 17 | loopLeaf(item.children, l); 18 | } else if (l < 1) { 19 | item.isLeaf = true; 20 | } 21 | }); 22 | }; 23 | loopLeaf(treeData, level + 1); 24 | } 25 | 26 | function getNewTreeData(treeData, curKey, child, level) { 27 | const loop = (data) => { 28 | data.forEach((item) => { 29 | if (item.children) { 30 | loop(item.children); 31 | } else { 32 | if (curKey.indexOf(item.key) === 0) { 33 | item.children = child; 34 | } 35 | } 36 | }); 37 | }; 38 | loop(treeData); 39 | setLeaf(treeData, curKey, level); 40 | } 41 | 42 | class CLTree extends React.Component { 43 | 44 | static defaultProps = { 45 | rootKey: '-1', 46 | }; 47 | 48 | constructor() { 49 | super(); 50 | this.state = { 51 | treeData: [] 52 | }; 53 | }; 54 | 55 | componentDidMount() { 56 | var self = this; 57 | const rootKey = self.props.rootKey; 58 | this.setState({ 59 | treeData: [{ 60 | name: '根目录', 61 | key: rootKey 62 | }], 63 | }); 64 | }; 65 | 66 | generateTreeNodes = (treeLoadData) => { 67 | var titleKey = this.props.treeNode.title; 68 | var keyKey = this.props.treeNode.key; 69 | const arr = []; 70 | var count = treeLoadData.length; 71 | for (let i = 0; i < count; i++) { 72 | var item = treeLoadData[i]; 73 | var name = item[titleKey]; 74 | var key = item[keyKey]; 75 | arr.push({ 76 | name: name, 77 | key: key 78 | }); 79 | } 80 | return arr; 81 | }; 82 | 83 | onLoadData = (treeNode) => { 84 | return new Promise((resolve) => { 85 | const treeUrl = `${this.props.treeUrl}?pid=${treeNode.props.eventKey}`; 86 | fetchUtil.get(treeUrl) 87 | .then((d) => { 88 | const treeData = [...this.state.treeData]; 89 | getNewTreeData(treeData, treeNode.props.eventKey, this.generateTreeNodes(d), 1); 90 | this.setState({ 91 | treeData 92 | }); 93 | resolve(); 94 | }, e => { 95 | 96 | }); 97 | }); 98 | }; 99 | 100 | render() { 101 | const loop = data => data.map((item) => { 102 | if (item.children) { 103 | return {loop(item.children)}; 104 | } 105 | return ; 106 | }); 107 | const treeNodes = loop(this.state.treeData); 108 | return ( 109 | 110 | {treeNodes} 111 | 112 | ); 113 | } 114 | } 115 | 116 | export default CLTree; -------------------------------------------------------------------------------- /backend/src/components/CLTreeSelect.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {TreeSelect} from 'antd'; 3 | import fetchUtil from '../libs/fetchUtil'; 4 | 5 | class CLTreeSelect extends React.Component { 6 | 7 | static defaultProps = { 8 | rootKey: '-1', 9 | }; 10 | 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = { 15 | treeData: [], 16 | }; 17 | 18 | this.treeNode = props.treeNode; 19 | this.treeUrl = props.treeUrl; 20 | }; 21 | 22 | componentWillReceiveProps(nextProps) { 23 | this.setState(nextProps); 24 | }; 25 | 26 | componentDidMount() { 27 | const self = this; 28 | const rootKey = "" + self.props.rootKey; 29 | fetchUtil.get(this.treeUrl) 30 | .then((d)=>{ 31 | const treeData = self.loopTreeData(d,rootKey); 32 | self.setState({ 33 | treeData: [{ 34 | label:"根", 35 | value:rootKey, 36 | key:rootKey, 37 | children:treeData 38 | }], 39 | }); 40 | },e =>{ 41 | 42 | }); 43 | 44 | }; 45 | 46 | loopTreeData = (data, pid) => { 47 | let result = [], temp; 48 | for (var i = 0; i < data.length; i++) { 49 | const treeData = data[i]; 50 | const treeDataName = treeData[this.treeNode.title]; 51 | const treeDataKey = ""+treeData[this.treeNode.key]; 52 | const treeDataPkey = ""+treeData[this.treeNode.pkey]; 53 | 54 | if (treeDataPkey === pid) { 55 | let obj = {label: treeDataName, value: treeDataKey, key: treeDataKey}; 56 | temp = this.loopTreeData(data, treeDataKey); 57 | if (temp.length > 0) { 58 | obj.children = temp; 59 | } 60 | result.push(obj); 61 | } 62 | } 63 | 64 | return result; 65 | }; 66 | 67 | treeSelect = (info, node) => { 68 | var treeNodeSelect = this.props.onTreeNodeSelect; 69 | var treeSelectFormTitle = this.props.treeSelectFormTitle; 70 | var treeSelectFormValue = this.props.treeSelectFormValue; 71 | treeNodeSelect(info, node, treeSelectFormTitle, treeSelectFormValue); 72 | }; 73 | 74 | 75 | render() { 76 | var treeData = this.state.treeData; 77 | let value; 78 | if (this.props.value){ 79 | value = ""+this.props.value; 80 | } 81 | 82 | const tProps = { 83 | treeData, 84 | value: value, 85 | placeholder: '请选择', 86 | onSelect: this.treeSelect, 87 | }; 88 | 89 | return ( 90 | 91 | ); 92 | } 93 | } 94 | 95 | export default CLTreeSelect; 96 | -------------------------------------------------------------------------------- /backend/src/components/CLUploadFiles.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Button,Upload,Icon} from 'antd'; 3 | import {StringUtil} from '../libs/common'; 4 | import {constant} from '../libs/constant'; 5 | 6 | class CLUploadFiles extends React.Component{ 7 | 8 | constructor(props){ 9 | super(props); 10 | this.state = { 11 | fileList: [] 12 | }; 13 | 14 | var defaultFileList = props.value; 15 | 16 | if (defaultFileList){ 17 | const files = this.convertValueToFileList(defaultFileList); 18 | this.state = {fileList:files}; 19 | } 20 | } 21 | 22 | componentWillReceiveProps(nextProps) { 23 | this.setState(nextProps); 24 | var defaultFileList = nextProps.value; 25 | 26 | if (defaultFileList){ 27 | const files = this.convertValueToFileList(defaultFileList); 28 | this.setState({fileList:files}); 29 | } 30 | }; 31 | 32 | convertValueToFileList = (defaultFileList) =>{ 33 | //获取图片列表 start 34 | var files = []; 35 | 36 | if (defaultFileList != undefined) { 37 | defaultFileList.split(";").map(function (val, index) { 38 | var fileurl = val; 39 | files.push( 40 | { 41 | uid: index, 42 | name: val, 43 | status: 'done', 44 | url: fileurl, 45 | } 46 | ) 47 | }); 48 | } 49 | //获取图片列表 end 50 | 51 | return files; 52 | 53 | } 54 | 55 | handleChange = (info)=> { 56 | 57 | var onFilesChangeSuccess = this.props.onFilesChangeSuccess; 58 | var formItem = this.props.formItem; 59 | 60 | var self = this; 61 | let fileList = info.fileList; 62 | 63 | var fileurls = ""; 64 | 65 | // 2. 读取远程路径并显示链接 66 | fileList = fileList.map((file) => { 67 | if (file.response) { 68 | // 组件会将 file.url 作为链接进行展示 69 | file.url = file.response.url; 70 | } 71 | 72 | if (fileurls=="") { 73 | fileurls += file.url; 74 | }else{ 75 | fileurls += ";" + file.url; 76 | } 77 | return file; 78 | }); 79 | 80 | 81 | fileurls = StringUtil.replaceAll(fileurls,constant.urlPrex,""); 82 | 83 | onFilesChangeSuccess(formItem,fileurls); 84 | 85 | // 3. 按照服务器返回信息筛选成功上传的文件 86 | fileList = fileList.filter((file) => { 87 | if (file.response) { 88 | return file.response.status === 'success'; 89 | } 90 | return true; 91 | }); 92 | this.setState({ fileList }); 93 | }; 94 | 95 | render() { 96 | const uploadProps = { 97 | action: '/api/media/uploadFiles', 98 | fileList:this.state.fileList, 99 | onChange: this.handleChange, 100 | multiple: true 101 | }; 102 | 103 | return ( 104 | 105 | 108 | 109 | ); 110 | } 111 | }; 112 | 113 | export default CLUploadFiles; 114 | -------------------------------------------------------------------------------- /backend/src/components/CLUploadImage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Upload,Icon,message} from 'antd'; 3 | import {constant} from '../libs/constant'; 4 | 5 | class CLUploadImage extends React.Component{ 6 | 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | fileList: [] 11 | }; 12 | 13 | var defaultFileList = props.value; 14 | 15 | if (defaultFileList){ 16 | const files = this.convertValueToFileList(defaultFileList); 17 | this.state = {fileList:files}; 18 | } 19 | }; 20 | 21 | componentWillReceiveProps(nextProps) { 22 | this.setState(nextProps); 23 | 24 | var defaultFileList = nextProps.value; 25 | 26 | if (defaultFileList){ 27 | const files = this.convertValueToFileList(defaultFileList); 28 | this.setState({fileList:files}); 29 | } 30 | }; 31 | 32 | convertValueToFileList = (defaultFileList) =>{ 33 | //获取图片列表 start 34 | var files = []; 35 | 36 | if (defaultFileList != undefined) { 37 | defaultFileList.split(";").map(function (val, index) { 38 | var fileurl = val; 39 | console.log("图片地址"+fileurl); 40 | files.push( 41 | { 42 | uid: index, 43 | name: val, 44 | status: 'done', 45 | url: fileurl, 46 | } 47 | ) 48 | }); 49 | } 50 | //获取图片列表 end 51 | 52 | return files; 53 | 54 | } 55 | 56 | handleChange = (info) => { 57 | 58 | var onUploadSuccess = this.props.onUploadSuccess; 59 | var formItem = this.props.formItem; 60 | 61 | var self = this; 62 | let fileList = info.fileList; 63 | fileList = fileList.slice(-1); 64 | 65 | 66 | if (info.file.status === 'uploading') { 67 | } 68 | if (info.file.status === 'done') { 69 | 70 | fileList = fileList.map((file) => { 71 | if (file.response) { 72 | // 组件会将 file.url 作为链接进行展示 73 | file.url = file.response.url; 74 | onUploadSuccess(formItem,file.url); 75 | } 76 | return file; 77 | }); 78 | } else if (info.file.status === 'error') { 79 | 80 | } else if (info.file.status === 'removed'){ 81 | onUploadSuccess(formItem,''); 82 | } 83 | 84 | self.setState({ fileList }); 85 | }; 86 | 87 | render() { 88 | const uploadProps = { 89 | action: '/api/media/upload', 90 | listType: 'picture-card', 91 | fileList:this.state.fileList, 92 | onChange: this.handleChange, 93 | name:'pic' 94 | }; 95 | 96 | return ( 97 | 98 | 99 |
上传照片
100 |
101 | ); 102 | } 103 | }; 104 | 105 | export default CLUploadImage; 106 | -------------------------------------------------------------------------------- /backend/src/components/CLUploadImageToQiniu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Upload,Icon,message} from 'antd'; 3 | import {constant} from 'utils/constant'; 4 | 5 | import fetchUtil from 'utils/fetchUtil'; 6 | 7 | import {Qiniu} from 'utils/qiniuUtil' 8 | 9 | import request from 'superagent-bluebird-promise' 10 | import moment from 'moment'; 11 | 12 | class CLUploadImageToQiniu extends React.Component{ 13 | 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | fileList: [] 18 | }; 19 | 20 | this.uploadUrl = 'http://upload.qiniu.com' 21 | if (window.location.protocol === 'https:') { 22 | this.uploadUrl = 'https://up.qbox.me/' 23 | } 24 | 25 | var defaultFileList = props.value; 26 | 27 | if (defaultFileList){ 28 | const files = this.convertValueToFileList(defaultFileList); 29 | this.state = {fileList:files}; 30 | } 31 | }; 32 | 33 | componentWillReceiveProps(nextProps) { 34 | this.setState(nextProps); 35 | 36 | var defaultFileList = nextProps.value; 37 | 38 | if (defaultFileList){ 39 | const files = this.convertValueToFileList(defaultFileList); 40 | this.setState({fileList:files}); 41 | } 42 | }; 43 | 44 | convertValueToFileList = (defaultFileList) =>{ 45 | //获取图片列表 start 46 | var files = []; 47 | 48 | if (defaultFileList != undefined) { 49 | defaultFileList.split(";").map(function (val, index) { 50 | var fileurl = val; 51 | files.push( 52 | { 53 | uid: index, 54 | name: val, 55 | status: 'done', 56 | url: fileurl, 57 | } 58 | ) 59 | }); 60 | } 61 | //获取图片列表 end 62 | 63 | return files; 64 | 65 | } 66 | 67 | _customRequest = (options) => { 68 | console.log(options.file); 69 | fetchUtil.get(`/api/zhende/qiniu/token`) 70 | .then((rs) => { 71 | this.uploadFile(options.file,rs.data.token); 72 | }, e => { 73 | }); 74 | } 75 | 76 | uploadFile = (file,token) => { 77 | var self = this; 78 | 79 | var onUploadSuccess = this.props.onUploadSuccess; 80 | var formItem = this.props.formItem; 81 | 82 | if (!file || file.size === 0) return null; 83 | 84 | const time = moment() 85 | const timeString = time.format("YYYYMMDDHHmmss"); 86 | 87 | var key = "shop/"+timeString+".jpg"; 88 | 89 | var r = request 90 | .post(this.uploadUrl) 91 | .field('key', key) 92 | .field('token', token) 93 | .field('x:filename', file.name) 94 | .field('x:size', file.size) 95 | .attach('file', file, file.name) 96 | .set('Accept', 'application/json') 97 | .then(function(res) { 98 | onUploadSuccess(formItem,"https://appimage.1688zdw.com/"+key); 99 | file.url = "https://appimage.1688zdw.com/"+key; 100 | let fileList = [file]; 101 | self.setState({ fileList }); 102 | }, function(error) { 103 | console.log(error); 104 | }); 105 | } 106 | 107 | render() { 108 | const uploadProps = { 109 | action: '/api/media/uploadToQiniu', 110 | listType: 'picture-card', 111 | fileList:this.state.fileList, 112 | customRequest:this._customRequest, 113 | name:'pic' 114 | }; 115 | 116 | return ( 117 | 118 | 119 |
上传照片
120 |
121 | ); 122 | } 123 | }; 124 | 125 | export default CLUploadImageToQiniu; 126 | -------------------------------------------------------------------------------- /backend/src/components/LmmTableViewSearch.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Form, Row, Col, Input, Button, Icon } from 'antd'; 3 | const FormItem = Form.Item; 4 | 5 | import styles from './LmmTableViewSearch.less' 6 | 7 | class LmmTableViewSearchForm extends React.Component { 8 | 9 | constructor(props){ 10 | super(props); 11 | this.searchValues = {} ; 12 | this.state = { 13 | expand: false, 14 | } 15 | } 16 | 17 | handleSearch = (e) => { 18 | e.preventDefault(); 19 | this.props.form.validateFields((err, values) => { 20 | console.log('Received values of form: ', values); 21 | console.log(values); 22 | this.props.searchAction(values); 23 | }); 24 | } 25 | 26 | handleReset = () => { 27 | this.props.form.resetFields(); 28 | } 29 | 30 | toggle = () => { 31 | const { expand } = this.state; 32 | this.setState({ expand: !expand }); 33 | } 34 | 35 | // To generate mock Form.Item 36 | getFields() { 37 | let fieldCount = this.props.searchFields.length; 38 | const count = this.state.expand ? fieldCount : 3; 39 | const { getFieldDecorator } = this.props.form; 40 | const children = []; 41 | 42 | const formItemLayout = { 43 | labelCol: { span: 4 }, 44 | wrapperCol: { span: 20 }, 45 | } ; 46 | 47 | let i = 0; 48 | for (let searchField of this.props.searchFields){ 49 | children.push( 50 | 51 | 55 | {getFieldDecorator(`${searchField.attr}`)( 56 | 57 | )} 58 | 59 | 60 | ); 61 | i++; 62 | } 63 | return children; 64 | } 65 | 66 | render() { 67 | return ( 68 |
72 | {this.getFields()} 73 | 74 | 75 | 78 | {this.props.searchFields.length > 3 ? 79 | 80 | 展开 81 | :null 82 | } 83 | 84 | 85 |
86 | ); 87 | } 88 | } 89 | 90 | const LmmTableViewSearch = Form.create()(LmmTableViewSearchForm); 91 | 92 | export default LmmTableViewSearch; 93 | 94 | 95 | -------------------------------------------------------------------------------- /backend/src/components/LmmTableViewSearch.less: -------------------------------------------------------------------------------- 1 | .ant-advanced-search-form { 2 | padding: 24px; 3 | background: #fbfbfb; 4 | border: 1px solid #d9d9d9; 5 | border-radius: 6px; 6 | } 7 | 8 | .ant-advanced-search-form .ant-form-item { 9 | display: flex; 10 | } 11 | 12 | .ant-advanced-search-form .ant-form-item-control-wrapper { 13 | flex: 1; 14 | } -------------------------------------------------------------------------------- /backend/src/favicon.ico: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /backend/src/img/LOGO.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/LOGO.jpg -------------------------------------------------------------------------------- /backend/src/img/LOGO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/LOGO.png -------------------------------------------------------------------------------- /backend/src/img/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/account.png -------------------------------------------------------------------------------- /backend/src/img/activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/activity.png -------------------------------------------------------------------------------- /backend/src/img/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/bg1.jpg -------------------------------------------------------------------------------- /backend/src/img/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/config.png -------------------------------------------------------------------------------- /backend/src/img/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/head.png -------------------------------------------------------------------------------- /backend/src/img/imgtag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/imgtag.png -------------------------------------------------------------------------------- /backend/src/img/order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/order.png -------------------------------------------------------------------------------- /backend/src/img/rightTo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/rightTo.png -------------------------------------------------------------------------------- /backend/src/img/sep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/sep.png -------------------------------------------------------------------------------- /backend/src/img/shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/shop.png -------------------------------------------------------------------------------- /backend/src/img/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/text.png -------------------------------------------------------------------------------- /backend/src/img/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/backend/src/img/user.png -------------------------------------------------------------------------------- /backend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /backend/src/layouts/BaseLayout/ListWithTreeLayout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/27. 3 | */ 4 | import React from 'react'; 5 | import { 6 | Row, 7 | Col, 8 | } from 'antd'; 9 | import styles from "./ListWithTreeLayout.less"; 10 | import CLTree from "../../components/CLTree"; 11 | 12 | class ListWithTreeLayout extends React.Component { 13 | render() { 14 | return ( 15 |
16 | 17 | 18 |
19 |
20 | {this.props.treeTitle} 21 |
22 | {this.props.treeTitleDes} 23 |
24 |
25 | 27 |
28 | 29 | 30 | {this.props.children} 31 | 32 |
33 |
34 | ) 35 | } 36 | } 37 | 38 | export default ListWithTreeLayout; -------------------------------------------------------------------------------- /backend/src/layouts/BaseLayout/ListWithTreeLayout.less: -------------------------------------------------------------------------------- 1 | .operateDiv{ 2 | margin-top: 15px; 3 | margin-bottom: 15px; 4 | } 5 | 6 | .treeLayout { 7 | width: 100%; 8 | background-color: white; 9 | overflow: hidden; 10 | margin-bottom: -10000px; 11 | padding-bottom: 10000px; 12 | border-right: 2px solid #eeeeee; 13 | } 14 | 15 | .treeLayout .treeTitle{ 16 | border-bottom: 1px solid #e9e9e9; 17 | font-size:15px; 18 | padding: 24px; 19 | } 20 | 21 | .treeLayout .treeDes{ 22 | font-size:12px; 23 | margin-top: 5px; 24 | color: gray; 25 | } -------------------------------------------------------------------------------- /backend/src/layouts/MainLayout/MainLayout.less: -------------------------------------------------------------------------------- 1 | body{ 2 | height:100%; 3 | } 4 | 5 | .aside { 6 | position: relative; 7 | min-height: 100%; 8 | } 9 | 10 | .logo { 11 | width: 150px; 12 | height: 64px; 13 | display: -webkit-flex; /* Safari */ 14 | display: flex; 15 | flex-direction: row ; 16 | align-items: center; 17 | //border-radius: 6px; 18 | //margin: 16px 24px 16px 28px; 19 | } 20 | 21 | .logo .img{ 22 | width:30px; 23 | height: 30px; 24 | margin-left: 15px; 25 | margin-right: 10px; 26 | } 27 | 28 | .aside .logo .logoItem{ 29 | display: -webkit-flex; /* Safari */ 30 | display: flex; 31 | align-items:center; 32 | font-weight: bold; 33 | font-size: 20px; 34 | color: white; 35 | cursor: pointer; 36 | } 37 | 38 | .logoText{ 39 | font-size: 15px; 40 | color: #000; 41 | } 42 | 43 | .logoText2{ 44 | font-size: 12px; 45 | color: #eee; 46 | } 47 | 48 | .sider { 49 | width: 100%; 50 | background: #fff; 51 | border-right:1px solid #eee; 52 | overflow: hidden; 53 | margin-bottom: -10000px; 54 | padding-bottom: 10000px; 55 | } 56 | 57 | .header { 58 | display: -webkit-flex; /* Safari */ 59 | display: flex; 60 | background-color: #fff; 61 | flex-direction: row; 62 | justify-content: space-between; 63 | box-shadow: 0 0 10px #deeaf4; 64 | border-bottom: 1px solid #deeaf4; 65 | } 66 | 67 | .headerleft { 68 | display: -webkit-flex; /* Safari */ 69 | display: flex; 70 | flex-direction: row; 71 | } 72 | 73 | .aside .headerRight { 74 | height: 64px; 75 | display: -webkit-flex; /* Safari */ 76 | display: flex; 77 | justify-content:flex-end; 78 | } 79 | 80 | .aside .headerRight .headItem{ 81 | display: -webkit-flex; /* Safari */ 82 | display: flex; 83 | align-items:center; 84 | margin-right: 10px; 85 | margin-left: 10px; 86 | border-radius:50%; overflow:hidden; 87 | color: black; 88 | } 89 | 90 | .logoContainer { 91 | height: 64px; 92 | } 93 | 94 | 95 | .aside .headerRight .headItemImg{ 96 | width: 30px; 97 | height: 30px; 98 | border-radius:50%; overflow:hidden; 99 | } 100 | 101 | .aside .footer { 102 | height: 64px; 103 | line-height: 64px; 104 | text-align: center; 105 | font-size: 12px; 106 | color: #999; 107 | background: #fff; 108 | border-top: 1px solid #e9e9e9; 109 | width: 100%; 110 | } 111 | 112 | .rootMenuTitle{ 113 | font-family:"Times New Roman",Georgia,Serif; 114 | font-size: 14px; 115 | } -------------------------------------------------------------------------------- /backend/src/libs/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jf on 16/1/14. 3 | */ 4 | class StringUtil { 5 | static replaceAll(s,s1,s2){ 6 |    return s.replace(new RegExp(s1,"gm"),s2); 7 |   } 8 | } 9 | 10 | export {StringUtil}; 11 | -------------------------------------------------------------------------------- /backend/src/libs/constant.js: -------------------------------------------------------------------------------- 1 | var constant = {}; 2 | 3 | constant.urlPrex = "http://localhost"; 4 | 5 | export {constant}; -------------------------------------------------------------------------------- /backend/src/libs/fetchUtil.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/20. 3 | */ 4 | import fetch from 'isomorphic-fetch'; 5 | 6 | const fetchUtil = {}; 7 | 8 | fetchUtil.post = async (url,params) => { 9 | try { 10 | 11 | const token = sessionStorage.getItem('token'); 12 | let headers = {}; 13 | if (token != undefined){ 14 | headers = { 15 | "Content-Type": "application/json", 16 | "Accept": "application/json", 17 | "Authorization":"Bearer "+token, 18 | } 19 | }else{ 20 | headers = { 21 | "Content-Type": "application/json", 22 | "Accept": "application/json" 23 | } 24 | } 25 | 26 | let response = await fetch(url, { 27 | method: 'POST', 28 | headers: headers, 29 | body: JSON.stringify(params) 30 | }); 31 | let data = await response.json(); 32 | if (response.status === 200) { 33 | if (data.success){ 34 | return data.data; 35 | }else{ 36 | throw new Error(data.msg); 37 | } 38 | } else { 39 | throw new Error(response.status); 40 | } 41 | } catch (e) { 42 | throw new Error("网络请求异常"); 43 | } 44 | }; 45 | 46 | fetchUtil.get = async (url) => { 47 | try { 48 | 49 | const token = sessionStorage.getItem('token'); 50 | let headers = {}; 51 | if (token != undefined){ 52 | headers = { 53 | "Content-Type": "application/json", 54 | "Accept": "application/json", 55 | "Authorization":"Bearer "+token, 56 | } 57 | }else{ 58 | headers = { 59 | "Content-Type": "application/json", 60 | "Accept": "application/json" 61 | } 62 | } 63 | 64 | let response = await fetch(url, { 65 | method: 'GET', 66 | headers: headers, 67 | }); 68 | let data = await response.json(); 69 | if (response.status === 200) { 70 | if (data.success){ 71 | return data.data; 72 | }else{ 73 | throw new Error(data.msg); 74 | } 75 | } else { 76 | throw new Error(response.status); 77 | } 78 | } catch (e) { 79 | throw new Error("网络请求异常"); 80 | } 81 | }; 82 | 83 | fetchUtil.requestZhenDe = (apiUrl,params,method) => { 84 | let data = { 85 | url:apiUrl, 86 | params:params 87 | } 88 | 89 | let requestUrl = ""; 90 | if (method == "get"){ 91 | requestUrl = "/api/zhende/common/get"; 92 | }else if (method == "post"){ 93 | requestUrl = "/api/zhende/common/post"; 94 | } 95 | 96 | return fetchUtil.post(requestUrl,data) 97 | } 98 | 99 | export default fetchUtil; 100 | -------------------------------------------------------------------------------- /backend/src/libs/react-dom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ReactDOM v15.1.0 3 | * 4 | * Copyright 2013-present, Facebook, Inc. 5 | * All rights reserved. 6 | * 7 | * This source code is licensed under the BSD-style license found in the 8 | * LICENSE file in the root directory of this source tree. An additional grant 9 | * of patent rights can be found in the PATENTS file in the same directory. 10 | * 11 | */ 12 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e(require("react"));else if("function"==typeof define&&define.amd)define(["react"],e);else{var f;f="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,f.ReactDOM=e(f.React)}}(function(e){return e.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED}); -------------------------------------------------------------------------------- /backend/src/page/frame/FrameRoleMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Button, Input, Form, Modal,Tree,Table} from 'antd'; 3 | const createForm = Form.create; 4 | const FormItem = Form.Item; 5 | const TreeNode = Tree.TreeNode; 6 | 7 | import {getAllRoleMenu,configRoleMenu} from '../../actions/frameRoleAction' 8 | 9 | class FrameRoleMenu extends React.Component{ 10 | 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = { 15 | defaultKeys:props.defaultKeys, 16 | }; 17 | this.chooseRoleId = props.chooseRoleId; 18 | this.selectKeys=`${props.defaultKeys}`; 19 | } 20 | 21 | componentWillReceiveProps(nextProps) { 22 | this.setState(nextProps); 23 | this.chooseRoleId = nextProps.chooseRoleId; 24 | this.selectKeys=`${nextProps.defaultKeys}`; 25 | } 26 | 27 | componentDidMount() { 28 | this.props.dispatch(getAllRoleMenu()); 29 | } 30 | 31 | handleOk = ()=> { 32 | this.props.dispatch(configRoleMenu(this.chooseRoleId,this.selectKeys)); 33 | }; 34 | 35 | handleCancel = ()=> { 36 | this.props.onClose(); 37 | }; 38 | 39 | rowKey = (record)=> { 40 | return record.menuId; 41 | }; 42 | 43 | rowOnChange = (selectedRowKeys, selectedRows)=> { 44 | this.selectKeys = `${selectedRowKeys}`; 45 | this.setState({defaultKeys:selectedRowKeys}) 46 | }; 47 | 48 | 49 | render() { 50 | const columns = [{ 51 | title: '菜单名称', 52 | dataIndex: 'name', 53 | key: 'name', 54 | }]; 55 | 56 | var rowSelection = { 57 | selectedRowKeys:this.state.defaultKeys, 58 | onChange:this.rowOnChange, 59 | }; 60 | 61 | return ( 62 |
63 | 返 回 , ]}> 67 |
68 | , 69 | 70 | 71 | 72 | ); 73 | } 74 | } 75 | 76 | export default FrameRoleMenu; 77 | -------------------------------------------------------------------------------- /backend/src/page/frame/FrameUserRole.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Button, Input, Form, Modal,Tree,Table} from 'antd'; 3 | const createForm = Form.create; 4 | const FormItem = Form.Item; 5 | const TreeNode = Tree.TreeNode; 6 | import {getAllUserRole,configUserRole} from '../../actions/userAction'; 7 | 8 | class FrameUserRole extends React.Component{ 9 | 10 | constructor(props) { 11 | super(props); 12 | this.state = { 13 | defaultKeys:props.defaultKeys, 14 | }; 15 | this.chooseUserId = props.chooseUserId; 16 | this.selectKeys=`${props.defaultKeys}`; 17 | } 18 | 19 | componentWillReceiveProps(nextProps) { 20 | this.setState(nextProps); 21 | this.chooseUserId = nextProps.chooseUserId; 22 | this.selectKeys=`${nextProps.defaultKeys}`; 23 | } 24 | 25 | componentDidMount() { 26 | this.props.dispatch(getAllUserRole()); 27 | } 28 | 29 | handleOk = ()=> { 30 | this.props.dispatch(configUserRole(this.chooseUserId,this.selectKeys)); 31 | }; 32 | 33 | handleCancel = ()=> { 34 | this.props.onClose(); 35 | }; 36 | 37 | rowKey = (record)=> { 38 | return record.roleId; 39 | }; 40 | 41 | rowOnChange = (selectedRowKeys, selectedRows)=> { 42 | this.selectKeys = `${selectedRowKeys}`; 43 | this.setState({defaultKeys:selectedRowKeys}) 44 | }; 45 | 46 | render() { 47 | const columns = [{ 48 | title: '角色名称', 49 | dataIndex: 'name', 50 | key: 'name', 51 | }]; 52 | 53 | var rowSelection = { 54 | selectedRowKeys:this.state.defaultKeys, 55 | onChange:this.rowOnChange, 56 | }; 57 | 58 | return ( 59 |
60 | 返 回 , ]}> 64 |
65 |
, 66 | 67 | 68 | 69 | ); 70 | } 71 | } 72 | 73 | export default FrameUserRole; 74 | -------------------------------------------------------------------------------- /backend/src/page/index/index.less: -------------------------------------------------------------------------------- 1 | 2 | :global { 3 | html, body, #root { 4 | height: 100%; 5 | } 6 | body { 7 | background: #fafafa; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /backend/src/page/index/main.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | import ReactDOM from 'react-dom'; 3 | import React from 'react'; 4 | import Routes from '../../routes/index'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /backend/src/page/info/InfoAdd.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CLForm from '../../components/CLForm'; 3 | import CLContentCard from '../../components/CLContentCard'; 4 | import { message } from 'antd'; 5 | import { connect } from 'react-redux' 6 | import {replace} from 'react-router-redux' 7 | 8 | import {infoAdd} from '../../actions/infoAction' 9 | 10 | @connect(state => ({ 11 | infoData:state.info.formData, 12 | formedit:state.info.formedit 13 | })) 14 | class InfoAdd extends React.Component{ 15 | constructor(props) { 16 | super(props); 17 | this.state = { 18 | edit: false, 19 | formData:{} 20 | } 21 | } 22 | 23 | onSubmitSuccess = (e)=> { 24 | message.info('保存成功'); 25 | const {dispatch} = this.props; 26 | dispatch(replace('/info/infoList')); 27 | dispatch(infoAdd()); 28 | }; 29 | 30 | render(){ 31 | const formItems = [ 32 | { 33 | type:"TreeSelect", 34 | title:"类别", 35 | arrname:"categoryId", 36 | treeUrl:"/api/infoCategory/listByPid", 37 | treeSelectFormValue:"categoryId", 38 | treeSelectFormTitle:"categoryName", 39 | treeNode:{ 40 | title: 'categoryName', 41 | key: 'categoryId', 42 | pkey:'pCategoryId' 43 | }, 44 | require:true 45 | }, 46 | { 47 | type:"Input", 48 | title:"标题", 49 | arrname:"topic", 50 | require:true 51 | }, 52 | { 53 | type:"Input", 54 | title:"城市", 55 | arrname:"city", 56 | require:true 57 | }, 58 | { 59 | type:"Input", 60 | title:"跳转地址", 61 | arrname:"url", 62 | require:true 63 | }, 64 | { 65 | type:"Input", 66 | title:"描述", 67 | arrname:"infoDes" 68 | }, 69 | { 70 | type:"UploadImg", 71 | title:"缩略图", 72 | arrname:"headImage", 73 | require:true 74 | }, 75 | { 76 | type:"Editor", 77 | title:"内容", 78 | arrname:"content", 79 | require:true 80 | } 81 | ]; 82 | 83 | const formProps = { 84 | edit:this.props.formedit, 85 | formData:this.props.infoData, 86 | formItems:formItems, 87 | onSubmitSuccess:this.onSubmitSuccess, 88 | updateUrl:'/api/info/update', 89 | addUrl:'/api/info/add' 90 | }; 91 | 92 | return( 93 | 94 | 95 | 96 | ); 97 | } 98 | } 99 | 100 | export default InfoAdd; -------------------------------------------------------------------------------- /backend/src/page/signIn.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './signIn.less'; 3 | import { message ,Spin} from 'antd'; 4 | import userLoginAction from '../actions/loginAction'; 5 | import { connect } from 'react-redux' 6 | 7 | @connect( 8 | state => ({ 9 | loginState: state.login.loginState, 10 | }) 11 | ) 12 | class SignIn extends React.Component{ 13 | 14 | static contextTypes = { 15 | router: React.PropTypes.object.isRequired 16 | }; 17 | 18 | handleSubmit = (e) => { 19 | e.preventDefault(); 20 | 21 | var username = this.refs.name.value 22 | var password = this.refs.pass.value 23 | 24 | const { dispatch } = this.props 25 | dispatch(userLoginAction(username,password)); 26 | }; 27 | 28 | 29 | render() { 30 | 31 | let loading = false; 32 | if (this.props.loginState==="no"){ 33 | loading=false; 34 | }else if(this.props.loginState==="start"){ 35 | loading=true; 36 | }else if(this.props.loginState==="success"){ 37 | loading=false; 38 | }else if(this.props.loginState==="fail"){ 39 | loading=false; 40 | } 41 | 42 | return
43 |
44 | 45 |

后台管理登录

46 |
47 | 48 | 49 | 50 | 51 |
52 |
53 |
54 | } 55 | } 56 | 57 | export default SignIn; 58 | 59 | -------------------------------------------------------------------------------- /backend/src/page/signIn.less: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | top: 25%; 4 | z-index: 1001; 5 | width: 450px; 6 | margin: auto; 7 | padding: 30px 40px 40px; 8 | border-radius: 10px; 9 | background-color: #fff; 10 | } 11 | 12 | .container h2{ 13 | margin: 0 0 25px; 14 | font-size: 25px; 15 | font-weight: 400; 16 | text-align: center; 17 | color: #323a45; 18 | } 19 | 20 | .container button{ 21 | width: 100%; 22 | height: 45px; 23 | border: none; 24 | background-color: #323a45; 25 | color: #fff; 26 | font-size: 24px; 27 | border-radius: 24px; 28 | cursor: pointer; 29 | margin-left: auto; 30 | margin-right: auto; 31 | transition: opacity .2s ease; 32 | } 33 | 34 | .LoginContainer{ 35 | position: absolute; 36 | width: 100%; 37 | height: 100%; 38 | background-image: url('../img/bg1.jpg'); 39 | background-repeat: no-repeat; 40 | background-size:100% 100%; 41 | } 42 | 43 | :-moz-placeholder { 44 | color: #c9c9c9 !important; 45 | font-size: 13px; 46 | } 47 | 48 | ::-webkit-input-placeholder { 49 | color: #ccc; 50 | font-size: 13px; 51 | } 52 | 53 | .input[type=text], input[type=password] { 54 | margin-bottom: 20px; 55 | padding: 0 10px; 56 | width: 100%; 57 | height: 50px; 58 | font-family: 'Lucida Grande', Tahoma, Verdana, sans-serif; 59 | font-size: 14px; 60 | color: #404040; 61 | background: white; 62 | border: 1px solid; 63 | border-color: #c4c4c4 #d1d1d1 #d4d4d4; 64 | border-radius: 2px; 65 | -moz-outline-radius: 3px; 66 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.12); 67 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.12); 68 | } 69 | .input[type=text]:focus, input[type=password]:focus { 70 | border-color: #7dc9e2; 71 | outline-color: #dceefc; 72 | outline-offset: 0; 73 | } 74 | 75 | .input[type=submit] { 76 | padding: 0 18px; 77 | height: 29px; 78 | font-size: 12px; 79 | font-weight: bold; 80 | color: #527881; 81 | text-shadow: 0 1px #e3f1f1; 82 | background: #cde5ef; 83 | border: 1px solid; 84 | border-color: #b4ccce #b3c0c8 #9eb9c2; 85 | border-radius: 16px; 86 | outline: 0; 87 | -webkit-box-sizing: content-box; 88 | -moz-box-sizing: content-box; 89 | box-sizing: content-box; 90 | background-image: -webkit-linear-gradient(top, #edf5f8, #cde5ef); 91 | background-image: -moz-linear-gradient(top, #edf5f8, #cde5ef); 92 | background-image: -o-linear-gradient(top, #edf5f8, #cde5ef); 93 | background-image: linear-gradient(to bottom, #edf5f8, #cde5ef); 94 | -webkit-box-shadow: inset 0 1px white, 0 1px 2px rgba(0, 0, 0, 0.15); 95 | box-shadow: inset 0 1px white, 0 1px 2px rgba(0, 0, 0, 0.15); 96 | } 97 | .input[type=submit]:active { 98 | background: #cde5ef; 99 | border-color: #9eb9c2 #b3c0c8 #b4ccce; 100 | -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.2); 101 | box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.2); 102 | } 103 | 104 | .lt-ie9 .input[type=text], .lt-ie9 input[type=password] { 105 | line-height: 34px; 106 | } 107 | -------------------------------------------------------------------------------- /backend/src/page/style/pageStyle.less: -------------------------------------------------------------------------------- 1 | .operateDiv{ 2 | margin-top: 15px; 3 | margin-bottom: 15px; 4 | } 5 | 6 | .treeLayout { 7 | width: 100%; 8 | background-color: white; 9 | overflow: hidden; 10 | margin-bottom: -10000px; 11 | padding-bottom: 10000px; 12 | border-right: 2px solid #eeeeee; 13 | } 14 | 15 | .treeLayout .treeTitle{ 16 | border-bottom: 1px solid #e9e9e9; 17 | font-size:15px; 18 | padding: 24px; 19 | } 20 | 21 | .treeLayout .treeDes{ 22 | font-size:12px; 23 | margin-top: 5px; 24 | color: gray; 25 | } -------------------------------------------------------------------------------- /backend/src/reducers/common/formReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/20. 3 | */ 4 | import { 5 | FORM_ADD,FORM_HIDE,FORM_EDIT 6 | } from '../../actions/common/commonActionType' 7 | 8 | const initState = { 9 | visible:false, 10 | formdata:{}, 11 | formedit:false, 12 | } 13 | 14 | export default (state = initState, action) => { 15 | switch (action.type) { 16 | case FORM_ADD: 17 | return action.payload; 18 | case FORM_EDIT: 19 | return action.payload; 20 | case FORM_HIDE: 21 | return action.payload; 22 | default: 23 | return state 24 | } 25 | }; -------------------------------------------------------------------------------- /backend/src/reducers/common/tableReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/20. 3 | */ 4 | import { 5 | TABLE_LOADING,TABLE_NORMAL,TABLE_DATASOURCE 6 | } from '../../actions/common/commonActionType' 7 | 8 | const initState = { 9 | payload:{ 10 | loading:false, 11 | dataSource:[], 12 | total:0, 13 | } 14 | } 15 | 16 | export default (state = initState, action) => { 17 | switch (action.type) { 18 | case TABLE_LOADING: 19 | return Object.assign({},state,action.payload); 20 | case TABLE_NORMAL: 21 | return Object.assign({},state,action.payload); 22 | case TABLE_DATASOURCE: 23 | return Object.assign({},state,action.payload); 24 | default: 25 | return state 26 | } 27 | }; -------------------------------------------------------------------------------- /backend/src/reducers/frameMenuReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/21. 3 | */ 4 | import { 5 | MENU_LIST 6 | } from '../actions/actionType' 7 | 8 | const initState = { 9 | menulist:[], 10 | total:0 11 | } 12 | 13 | export default (state=initState , action) =>{ 14 | switch (action.type) { 15 | case MENU_LIST: 16 | return Object.assign({},state,action.payload); 17 | default: 18 | return state; 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /backend/src/reducers/frameRoleReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/21. 3 | */ 4 | import { 5 | ROLE_LIST,ROLE_MENU_FORM_SHOW,ROLE_MENU_FORM_HIDE,ROLE_MENU_FORM_LOADING,ROLE_MENU_ALL 6 | } from '../actions/actionType' 7 | 8 | const initState = { 9 | rolelist:[], 10 | total:0 11 | } 12 | 13 | export default (state=initState , action) =>{ 14 | switch (action.type) { 15 | case ROLE_LIST: 16 | return Object.assign({},state,{ 17 | rolelist:action.payload.dataSource, 18 | total:action.payload.total, 19 | }); 20 | case ROLE_MENU_FORM_SHOW: 21 | return Object.assign({},state,action.payload); 22 | case ROLE_MENU_FORM_HIDE: 23 | return Object.assign({},state,action.payload); 24 | case ROLE_MENU_FORM_LOADING: 25 | return Object.assign({},state,action.payload); 26 | case ROLE_MENU_ALL: 27 | return Object.assign({},state,action.payload); 28 | default: 29 | return state; 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /backend/src/reducers/infoCReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/27. 3 | */ 4 | import { 5 | INFOC_LIST 6 | } from '../actions/actionType' 7 | 8 | const initState = { 9 | formData:{}, 10 | infoCList:[], 11 | total:0, 12 | } 13 | 14 | export default (state=initState, action) => { 15 | switch (action.type) { 16 | case INFOC_LIST: 17 | return Object.assign({},state,{ 18 | infoCList:action.payload.dataSource, 19 | total:action.payload.total, 20 | }); 21 | 22 | default: 23 | return state 24 | } 25 | }; -------------------------------------------------------------------------------- /backend/src/reducers/infoReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/27. 3 | */ 4 | import { 5 | INFO_ADD,INFO_EDIT,INFO_LIST 6 | } from '../actions/actionType' 7 | 8 | const initState = { 9 | formData:{}, 10 | infoList:[], 11 | total:0, 12 | } 13 | 14 | export default (state=initState, action) => { 15 | switch (action.type) { 16 | case INFO_ADD: 17 | return Object.assign({},state,action.payload); 18 | case INFO_EDIT: 19 | return Object.assign({},state,action.payload); 20 | case INFO_LIST: 21 | return Object.assign({},state,{ 22 | infoList:action.payload.dataSource, 23 | total:action.payload.total, 24 | }); 25 | 26 | default: 27 | return state 28 | } 29 | }; -------------------------------------------------------------------------------- /backend/src/reducers/loginReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/19. 3 | */ 4 | import { 5 | LOGIN_AJAX_START,LOGIN_SUCCESS,LOGIN_FAILURE 6 | } from '../actions/actionType' 7 | 8 | export default (state = {loginState:"no", msg:""}, action) => { 9 | switch (action.type) { 10 | case LOGIN_AJAX_START: 11 | return action.payload; 12 | case LOGIN_SUCCESS: 13 | return action.payload; 14 | case LOGIN_FAILURE: 15 | return action.payload; 16 | default: 17 | return state 18 | } 19 | }; -------------------------------------------------------------------------------- /backend/src/reducers/userReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/20. 3 | */ 4 | import { 5 | USER_LIST,USER_ROLE_FORM_HIDE,USER_ROLE_FORM_SHOW,USER_ROLE_ALL,USER_ROLE_FORM_LOADING 6 | } from '../actions/actionType' 7 | 8 | const initState = { 9 | userlist:[], 10 | total:0 11 | } 12 | 13 | export default (state=initState, action) => { 14 | switch (action.type) { 15 | case USER_LIST: 16 | return Object.assign({},state,action.payload); 17 | case USER_ROLE_FORM_SHOW: 18 | return Object.assign({},state,action.payload); 19 | case USER_ROLE_FORM_HIDE: 20 | return Object.assign({},state,action.payload); 21 | case USER_ROLE_ALL: 22 | return Object.assign({},state,action.payload); 23 | case USER_ROLE_FORM_LOADING: 24 | return Object.assign({},state,action.payload); 25 | default: 26 | return state 27 | } 28 | }; -------------------------------------------------------------------------------- /backend/src/routes/index.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { Router, Route,hashHistory} from 'react-router'; 3 | import { syncHistoryWithStore } from 'react-router-redux' 4 | import { Provider } from 'react-redux'; 5 | 6 | import store from './store'; 7 | 8 | import App from '../layouts/MainLayout/MainLayout'; 9 | import SignIn from '../page/signIn'; 10 | 11 | 12 | import Users from '../page/frame/Users'; 13 | import CLFrameMenu from '../page/frame/FrameMenu'; 14 | import CLFrameRole from '../page/frame/FrameRole'; 15 | import InfoList from '../page/info/InfoList'; 16 | import InfoAdd from '../page/info/InfoAdd'; 17 | import InfoCategory from '../page/info/InfoCategory'; 18 | 19 | function requireAuth(nextState, replace) { 20 | var islogin = sessionStorage.getItem('login'); 21 | if(islogin == 'true'){ 22 | 23 | }else{ 24 | replace('/user/login') 25 | } 26 | } 27 | 28 | 29 | store.subscribe(() => 30 | console.log(store.getState()) 31 | ); 32 | 33 | const history = syncHistoryWithStore(hashHistory, store); 34 | 35 | const Routes = () => 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | ; 55 | 56 | Routes.propTypes = { 57 | history: PropTypes.any, 58 | }; 59 | 60 | export default Routes; 61 | -------------------------------------------------------------------------------- /backend/src/routes/store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/19. 3 | */ 4 | 5 | import {combineReducers ,createStore,applyMiddleware} from 'redux' 6 | import {routerReducer,routerMiddleware } from 'react-router-redux' 7 | 8 | //通用 9 | import table from "../reducers/common/tableReducer"; 10 | import form from "../reducers/common/formReducer"; 11 | 12 | //框架相关 13 | import login from "../reducers/loginReducer"; 14 | import user from "../reducers/userReducer"; 15 | import menu from "../reducers/frameMenuReducer"; 16 | import role from "../reducers/frameRoleReducer"; 17 | 18 | //业务相关 19 | import info from "../reducers/infoReducer"; 20 | import infoc from "../reducers/infoCReducer"; 21 | 22 | import {hashHistory} from 'react-router'; 23 | import thunk from 'redux-thunk' 24 | 25 | const rootReducer = combineReducers({ 26 | form, 27 | table, 28 | login, 29 | user, 30 | menu, 31 | role, 32 | info, 33 | infoc, 34 | routing: routerReducer 35 | }); 36 | 37 | const routermiddleware = routerMiddleware(hashHistory) 38 | 39 | const middleware = [ thunk,routermiddleware ] 40 | 41 | const store = createStore( 42 | rootReducer, 43 | applyMiddleware(...middleware) 44 | ); 45 | 46 | export default store; -------------------------------------------------------------------------------- /backend/tools/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'), 4 | utils = require('./utils'), 5 | __basename = path.dirname(__dirname), 6 | __env = process.env.NODE_ENV; 7 | 8 | let defaultPath = "//localhost:9000/"; 9 | let cdn = "//localhost:9000/"; 10 | 11 | if (__env == "__PROD__") { 12 | defaultPath = "//localhost:9000/page/"; 13 | cdn = "//localhost:9000/page/"; 14 | } 15 | 16 | /** 17 | * [config basic configuration] 18 | * @type {Object} 19 | */ 20 | 21 | var config = { 22 | env: __env, 23 | webpack: { 24 | path: { 25 | src: path.resolve(__basename, "src"), 26 | dev: path.resolve(__basename, "dev"), 27 | pub: path.resolve(__basename, "pub"), 28 | }, 29 | defaultPath: defaultPath, 30 | cdn: cdn, 31 | hash: "[hash:6]", 32 | chunkhash: "[chunkhash:6]", 33 | imghash: "", 34 | contenthash: "[contenthash:6]", 35 | }, 36 | gulp: { 37 | path: { 38 | src: path.resolve(__basename, "src"), 39 | dev: path.resolve(__basename, "dev"), 40 | pub: path.resolve(__basename, "pub"), 41 | offline: path.resolve(__basename, "offline"), 42 | }, 43 | }, 44 | server: { // webpack开发环境服务器配置 45 | port: 9000, // port for local server 46 | hostDirectory: "/page/" // http://host/hostDirectory/ 47 | }, 48 | }; 49 | 50 | // 自动扫描html 51 | config.webpack.html = utils.getHtmlFile(config.webpack.path.src); 52 | // 根据约定,自动扫描js entry,约定是src/page/xxx/main.js 或 src/page/xxx/main.jsx 53 | /** 54 | 当前获取结果 55 | { 56 | 'js/index': [path.join(configWebpack.path.src, "/page/index/main.js")], 57 | 'js/spa': [path.join(configWebpack.path.src, "/page/spa/main.js")], 58 | 'js/pindex': [path.join(configWebpack.path.src, "/page/pindex/main.jsx")], 59 | } 60 | */ 61 | config.webpack.entry = utils.getJsFile(config.webpack.path.src, 'page', 'main', ['js', 'jsx']); 62 | 63 | // 合图配置 64 | config.gulp.sprites = { 65 | tplpath: path.resolve(__basename, "tools/sprite-template/less.template.handlebars"), 66 | imgPath: '../../css/sprites/', 67 | imgName: 'sprites.png', 68 | cssName: 'sprites.scss', 69 | imgDest: config.gulp.path.src + '/css/sprites/', 70 | cssDest: config.gulp.path.src + '/css/sprites/', 71 | }; 72 | 73 | module.exports = config; 74 | -------------------------------------------------------------------------------- /backend/tools/gulpfile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var gulp = require("gulp"); 3 | var run = require('run-sequence'); 4 | var merge = require('merge-stream'); 5 | var replace = require('gulp-replace'); 6 | var gutil = require('gulp-util'); 7 | 8 | // 合图 9 | var spritesmith = require('gulp.spritesmith-multi'); 10 | var config = require('./config.js'); 11 | 12 | gulp.task('sprites', function (cb) { 13 | var spriteData = gulp.src(config.gulp.path.src + '/img/sprites/**/*.png') 14 | // .on('data', function(files) { 15 | // gutil.log(gutil.colors.green("sprites start: " + files.path)); 16 | // }) 17 | .pipe(spritesmith({ 18 | spritesmith: function(options) { 19 | options.imgPath = config.gulp.sprites.imgPath + options.imgName; 20 | 21 | options.cssName = options.cssName.replace('.css', '.less'); 22 | // customized generated css template 23 | options.cssTemplate = config.gulp.sprites.tplpath; //'./sprite-template/less.template.handlebars'; 24 | 25 | } 26 | })) 27 | // .on('finish', function() { 28 | // gutil.log(gutil.colors.green("sprites done")); 29 | // }); 30 | 31 | // Pipe image stream through image optimizer and onto disk 32 | var imgStream = spriteData.img 33 | // DEV: We must buffer our stream into a Buffer for `imagemin` 34 | .pipe(gulp.dest(config.gulp.sprites.imgDest)); 35 | 36 | // Pipe CSS stream through CSS optimizer and onto disk 37 | var cssStream = spriteData.css 38 | .pipe(gulp.dest(config.gulp.sprites.cssDest)); 39 | 40 | // Return a merged stream to handle both `end` events 41 | return merge(imgStream, cssStream); 42 | }); 43 | 44 | gulp.task('dist', ['sprites'], function(cb) { 45 | cb(); 46 | }); 47 | 48 | gulp.task('default', function() { 49 | run('dist'); 50 | }); 51 | 52 | run('default'); -------------------------------------------------------------------------------- /backend/tools/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'), 4 | path = require('path'); 5 | 6 | module.exports = { 7 | /** 8 | * get html files automatically 9 | * @param {String} srcPath [directory contains html files] 10 | * @return {Array} [array of html files path] 11 | */ 12 | getHtmlFile: function(srcPath) { 13 | // read html filename from 14 | let srcFiles = fs.readdirSync(srcPath); 15 | 16 | srcFiles = srcFiles.filter((item, index) => { 17 | return !!~item.indexOf('.html'); 18 | }); 19 | 20 | srcFiles = srcFiles.map((item, index) => { 21 | return item.replace('.html', ''); 22 | }); 23 | 24 | return srcFiles; 25 | }, 26 | /** 27 | * get js files automatically 28 | * @param {String} srcPath [directory contains js files] 29 | * @param {String} jsDirectory [js directory] 30 | * @param {String} fileName [js filename] 31 | * @param {Array} extensions [possiable js extension] 32 | * @return {Object} [Object of js files path] 33 | */ 34 | getJsFile: function(srcPath, jsDirectory, fileName, extensions) { 35 | let jsFileArray = {}; 36 | //read js filename 37 | let srcFiles = fs.readdirSync(path.join(srcPath, jsDirectory)); 38 | 39 | srcFiles = srcFiles.filter((item, index) => { 40 | return item !== 'common'; 41 | }); 42 | 43 | srcFiles.map((item, index) => { 44 | extensions.map((ext, index) => { 45 | let jsPath = path.join(srcPath, jsDirectory, item, 'main.' + ext); 46 | if (fs.existsSync(jsPath)) { 47 | jsFileArray['js/' + item] = [jsPath]; 48 | } 49 | }); 50 | }); 51 | 52 | // console.log(jsFileArray); 53 | return jsFileArray; 54 | } 55 | }; -------------------------------------------------------------------------------- /backend/tools/webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'), 4 | config = require('./config'), 5 | isProduction = (config.env === '__PROD__'), 6 | gutil = require('gulp-util'); 7 | 8 | 9 | if (isProduction) { 10 | var compiler = webpack(require('./webpack.pub')); 11 | compiler.run(function(err, stats) { 12 | if (!err) { 13 | gutil.log('[webpack:pub]', stats.toString({ 14 | chunks: false, // Makes the build much quieter 15 | colors: true 16 | })); 17 | } 18 | else { 19 | console.log(err); 20 | } 21 | }); 22 | } 23 | else { 24 | require('./webpack.server'); 25 | } -------------------------------------------------------------------------------- /backend/tools/webpack.server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var webpack = require('webpack'); 4 | var webpackDevMiddleware = require("webpack-dev-middleware"); 5 | var webpackHotMiddleware = require("webpack-hot-middleware"); 6 | var proxy = require('proxy-middleware'); 7 | 8 | var webpackConfig = require("./webpack.dev.js"), 9 | config = require("./config.js"); 10 | var port = config.server.port; 11 | 12 | for (var key in webpackConfig.entry) { 13 | webpackConfig.entry[key].unshift('webpack-hot-middleware/client'); 14 | } 15 | 16 | var compiler = webpack(webpackConfig); 17 | app.use(webpackDevMiddleware(compiler, { 18 | hot: true, 19 | // historyApiFallback: false, 20 | noInfo: true, 21 | stats: { 22 | colors: true 23 | }, 24 | })); 25 | app.use(webpackHotMiddleware(compiler)); 26 | // 前端转发 27 | app.use(config.server.hostDirectory, proxy('http://localhost:' + port)); 28 | //后台转发 29 | // app.use('/api/', proxy('http://localhost:8080/api')); 30 | app.use('/api/', proxy('http://localhost:7001/api')); 31 | 32 | app.listen(port, function(err) { 33 | if (err) { 34 | console.error(err); 35 | } 36 | else { 37 | console.info("Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port); 38 | } 39 | }); -------------------------------------------------------------------------------- /doc/form表单.md: -------------------------------------------------------------------------------- 1 | ### form表单熟悉 2 | 3 | ``` 4 | const formProps = { 5 | visible: this.props.formVisible, 6 | edit: this.props.formedit, 7 | formData: this.props.formdata, 8 | formItems: formItems, 9 | formValueName:"title",//title:使用lable的值作为form表单的值,使用value的值作为form表单的值 10 | onClose: this.formClose, 11 | dateFormatList: ['start', 'end'],//需要格式化的字段名 12 | deleteFormItemList:['createAt'],//需要删除的字段名 13 | updateUrl: '/api/zhende/activity/update', 14 | addUrl: '/api/zhende/activity/add' 15 | }; 16 | 17 | ``` 18 | 19 | ### formItems 属性 20 | 21 | 树结构 22 | 23 | ``` 24 | 25 | { 26 | type: "TreeSelect", 27 | title: "选择区域", 28 | arrname: "area", 29 | treeUrl: "/api/zhende/area/listByPid", 30 | treeSelectFormTitle: "name",//title复值给表单哪个key 31 | treeSelectFormValue:"",//value复值给表单哪个key 32 | formValueName:"title",//希望使用title作为表单的值 33 | rootKey:"101", 34 | treeNode: { 35 | title: 'name', 36 | key: 'name', 37 | pkey: 'pid' 38 | }, 39 | require: true 40 | }, 41 | 42 | ``` 43 | 44 | 文本 45 | 46 | ``` 47 | { 48 | type: "Input", 49 | title: "菜单名称", 50 | arrname: "name", 51 | require: true 52 | }, 53 | 54 | ``` 55 | 56 | 下拉选择 57 | 58 | ``` 59 | 60 | { 61 | type: "Select", 62 | title: "活动类别", 63 | arrname: "activityType", 64 | require: true, 65 | selectOptions:[ 66 | {name:"新人大礼包", 67 | value:"0",}, 68 | {name:"周期性活动礼包", 69 | value:"1",} 70 | ], 71 | }, 72 | 73 | ``` 74 | 75 | 下拉选择(多选) 76 | 77 | ``` 78 | 79 | { 80 | type: "MutiSelect", 81 | title: "活动类别", 82 | arrname: "activityType", 83 | require: true, 84 | selectOptions:[ 85 | {name:"新人大礼包", 86 | value:"0",}, 87 | {name:"周期性活动礼包", 88 | value:"1",} 89 | ], 90 | }, 91 | 92 | ``` 93 | 94 | 时间 95 | 96 | ``` 97 | { 98 | type: "DatePicker", 99 | title: "活动开始时间", 100 | arrname: "start", 101 | require: true 102 | } 103 | 104 | ``` 105 | 106 | 107 | ## 操作 108 | 109 | ``` 110 | showForm = () => { 111 | const { 112 | dispatch 113 | } = this.props; 114 | dispatch(formAdd()); 115 | }; 116 | 117 | deleteHandle = (record) => { 118 | const currentCityName = this.props.currentCityName; 119 | const self = this; 120 | const { 121 | dispatch 122 | } = this.props; 123 | return function() { 124 | confirm({ 125 | title: '提示', 126 | content: '确认删除?', 127 | onOk() { 128 | dispatch(deleteActivity(record.id,currentCityName)); 129 | }, 130 | onCancel() {} 131 | }); 132 | }; 133 | }; 134 | 135 | editHandle = (record, index) => { 136 | const { 137 | dispatch 138 | } = this.props; 139 | return function() { 140 | dispatch(formEdit(record)); 141 | }; 142 | }; 143 | 144 | formClose = (d) => { 145 | const { 146 | dispatch 147 | } = this.props; 148 | dispatch(formHide()); 149 | if (d) { 150 | dispatch(getList(1)); 151 | } 152 | }; 153 | 154 | ``` -------------------------------------------------------------------------------- /doc/table控件.md: -------------------------------------------------------------------------------- 1 | ### 使用curd控件 2 | 3 | ``` 4 | import React from 'react'; 5 | import LmmTableView from 'components/LmmTableView' 6 | import moment from 'moment' 7 | 8 | class TradeList extends React.Component { 9 | 10 | constructor() { 11 | super(); 12 | } 13 | 14 | render() { 15 | 16 | const self = this; 17 | 18 | let columns = [ 19 | { 20 | title: '姓名', 21 | dataIndex: 'name', 22 | key: 'name', 23 | minWidth: '100' 24 | }, 25 | { 26 | title: '交易号', 27 | dataIndex: 'tradeNo', 28 | key: 'tradeNo', 29 | minWidth: '100' 30 | }, 31 | { 32 | title: '交易时间', 33 | key: 'tradedAt', 34 | minWidth: '100', 35 | render: function(text, record, index) { 36 | let timeString = moment(record.tradedAt).format('YYYY-MM-DD HH:mm:ss'); 37 | return
38 | {timeString} 39 |
; 40 | } 41 | }, 42 | { 43 | title: '状态', 44 | key: 'state', 45 | minWidth: '100', 46 | render: function(text, record, index) { 47 | let divtext = '待支付'; 48 | if (record.state == 1){ 49 | divtext = '待支付' 50 | }else if (record.state == 2){ 51 | divtext = '已支付' 52 | } 53 | return
54 | {divtext} 55 |
; 56 | } 57 | }, 58 | 59 | ]; 60 | 61 | let searchFields = [{ 62 | title:'用户名', 63 | attr:'name', 64 | type:'input' 65 | },{ 66 | title:'交易号', 67 | attr:'tradeNo', 68 | type:'input' 69 | }] 70 | 71 | let tableProps = { 72 | title:'交易', 73 | columns:columns, 74 | searchFields:searchFields, 75 | apiList:'/api/b/trade/alltrade',//必须返回rows,count两个参数,get方法 76 | } 77 | 78 | return ( 79 |
80 | 81 |
82 | ); 83 | } 84 | } 85 | 86 | export default TradeList; 87 | 88 | 89 | ``` -------------------------------------------------------------------------------- /eggserver/README.md: -------------------------------------------------------------------------------- 1 | # LmmEggFrame 2 | 3 | Lmm后台管理 4 | 5 | ## QuickStart 6 | 7 | 8 | 9 | see [egg docs][egg] for more detail. 10 | 11 | ### Development 12 | 13 | ```bash 14 | $ npm i 15 | $ npm run dev 16 | $ open http://localhost:7001/ 17 | ``` 18 | 19 | ### Deploy 20 | 21 | ```bash 22 | $ npm start 23 | $ npm stop 24 | ``` 25 | 26 | ### npm scripts 27 | 28 | - Use `npm run lint` to check code style. 29 | - Use `npm test` to run unit test. 30 | - Use `npm run autod` to auto detect dependencies upgrade, see [autod](https://www.npmjs.com/package/autod) for more detail. 31 | 32 | 33 | [egg]: https://eggjs.org -------------------------------------------------------------------------------- /eggserver/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # LmmEggFrame 2 | 3 | Lmm后台管理 4 | 5 | ## 快速入门 6 | 7 | 8 | 9 | 如需进一步了解,参见 [egg 文档][egg]。 10 | 11 | ### 本地开发 12 | 13 | ```bash 14 | $ npm i 15 | $ npm run dev 16 | $ open http://localhost:7001/ 17 | ``` 18 | 19 | ### 部署 20 | 21 | ```bash 22 | $ npm start 23 | $ npm stop 24 | ``` 25 | 26 | ### 单元测试 27 | 28 | - [egg-bin] 内置了 [mocha], [thunk-mocha], [power-assert], [istanbul] 等框架,让你可以专注于写单元测试,无需理会配套工具。 29 | - 断言库非常推荐使用 [power-assert]。 30 | - 具体参见 [egg 文档 - 单元测试](https://eggjs.org/zh-cn/core/unittest)。 31 | 32 | ### 内置指令 33 | 34 | - 使用 `npm run lint` 来做代码风格检查。 35 | - 使用 `npm test` 来执行单元测试。 36 | - 使用 `npm run autod` 来自动检测依赖更新,详细参见 [autod](https://www.npmjs.com/package/autod) 。 37 | 38 | 39 | [egg]: https://eggjs.org 40 | -------------------------------------------------------------------------------- /eggserver/app/controller/menu.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('../core/base_controller'); 4 | 5 | class MenuController extends Controller { 6 | async list() { 7 | const {ctx,app} = this; 8 | var q = ctx.query; 9 | var rows = await ctx.service.menu.getMenusPage(q.pmenuId,q.page,q.size); 10 | var count = await ctx.service.menu.getMenusCount(q.pmenuId); 11 | 12 | this.success({"rows":rows, 13 | "count":count}) 14 | } 15 | 16 | async listByPid() { 17 | const {ctx,app} = this; 18 | var q = ctx.query; 19 | var rows = await ctx.service.menu.getMenuByPid(q.pid); 20 | this.success(rows); 21 | } 22 | 23 | async allList() { 24 | const {ctx,app} = this; 25 | var q = ctx.query; 26 | var rows = await ctx.service.menu.getAllMenu(); 27 | this.success(rows); 28 | } 29 | 30 | async add() { 31 | const {ctx,app} = this; 32 | var menu = ctx.request.body; 33 | var success = await ctx.service.menu.add('fmenu',menu); 34 | this.success({ success : true,}); 35 | } 36 | 37 | async delete() { 38 | const {ctx,app} = this; 39 | var q = ctx.query; 40 | var id = q.id; 41 | var success = await ctx.service.menu.delete('fmenu',{menuId:id}); 42 | this.success({ success : true,}); 43 | } 44 | 45 | async update() { 46 | const {ctx,app} = this; 47 | var menu = ctx.request.body; 48 | var success = await ctx.service.menu.update('fmenu',menu,{menuId:menu.menuId}); 49 | this.success({ success : true,}); 50 | } 51 | 52 | } 53 | 54 | module.exports = MenuController; -------------------------------------------------------------------------------- /eggserver/app/controller/role.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | const Controller = require('../core/base_controller'); 5 | 6 | class RoleController extends Controller { 7 | async list() { 8 | const {ctx,app} = this; 9 | var q = ctx.query; 10 | var rows = await ctx.service.role.getRolesPage(q.proleId,q.page,q.size); 11 | var count = await ctx.service.role.getRolesCount(q.proleId); 12 | 13 | this.success({"rows":rows, 14 | "count":count}) 15 | } 16 | 17 | async listByPid() { 18 | const {ctx,app} = this; 19 | var q = ctx.query; 20 | var rows = await ctx.service.role.getRoleByPid(q.pid); 21 | this.success(rows); 22 | } 23 | 24 | async allList() { 25 | const {ctx,app} = this; 26 | var q = ctx.query; 27 | var rows = await ctx.service.role.getAllRoles(); 28 | 29 | this.success(rows); 30 | } 31 | 32 | async add() { 33 | const {ctx,app} = this; 34 | var role = ctx.request.body; 35 | var success = await ctx.service.role.add('frole',role); 36 | 37 | ctx.body = { 38 | success : true, 39 | }; 40 | 41 | this.success({ success : true,}); 42 | } 43 | 44 | async delete() { 45 | const {ctx,app} = this; 46 | var q = ctx.query; 47 | var id = q.id; 48 | var success = await ctx.service.role.delete('frole',{roleId:id}); 49 | this.success({ success : true,}); 50 | } 51 | 52 | async update() { 53 | const {ctx,app} = this; 54 | var role = ctx.request.body; 55 | var success = await ctx.service.role.update('frole',role,{roleId:role.roleId}); 56 | this.success({ success : true,}); 57 | } 58 | 59 | async configRoleMenu() { 60 | const {ctx,app} = this; 61 | var roleMenuFormData = ctx.request.body; 62 | await ctx.service.role.configRoleMenu(roleMenuFormData); 63 | ctx.body = { 64 | success : true, 65 | }; 66 | } 67 | 68 | async getRoleMenus() { 69 | const {ctx,app} = this; 70 | var q = ctx.query; 71 | var rows = await ctx.service.role.getRoleMenus(q.roleId); 72 | this.success(rows) 73 | } 74 | 75 | } 76 | 77 | module.exports = RoleController; 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /eggserver/app/controller/upload.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const sendToWormhole = require('stream-wormhole'); 3 | const Controller = require('egg').Controller; 4 | const fs = require('fs'); 5 | const mkdirp = require('mkdirp'); 6 | 7 | class UploaderController extends Controller { 8 | async upload() { 9 | const ctx = this.ctx; 10 | const stream = await ctx.getFileStream(); 11 | const name = path.basename(stream.filename); 12 | 13 | let now = new Date(); 14 | let today = path.join(now.getFullYear().toString(), (now.getMonth() + 1).toString(), now.getDay().toString(),"/"); 15 | let folder = path.join(this.app.config.upload.localFilePrex, today); 16 | // let filename = now.getTime() + '__' + name; 17 | let filename = now.getTime() + '.jpg'; 18 | 19 | try { 20 | fs.accessSync(folder, fs.constants.W_OK); 21 | } catch (err) { 22 | mkdirp.sync(folder); 23 | } finally { 24 | 25 | } 26 | 27 | const fileAbsoluteName = folder + filename; 28 | 29 | var fileWriteStream = fs.createWriteStream(fileAbsoluteName); 30 | stream.pipe(fileWriteStream); 31 | fileWriteStream.on('close',function(){ 32 | console.log('copy over'); 33 | }); 34 | 35 | let url = `${this.app.config.upload.remoteFilePrex}` + today + filename; 36 | 37 | ctx.body = { 38 | "status": "success", 39 | "url": url 40 | }; 41 | 42 | } 43 | } 44 | 45 | module.exports = UploaderController; -------------------------------------------------------------------------------- /eggserver/app/controller/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('../core/base_controller'); 4 | 5 | class UserController extends Controller { 6 | 7 | async login() { 8 | const {ctx,app} = this; 9 | const username = ctx.request.body.username; 10 | const password = ctx.request.body.password; 11 | 12 | var loginsuccess = false; 13 | var user = await ctx.service.user.getUserByUserName(username); 14 | var menus = []; 15 | if (user!=undefined) { 16 | if(user.loginPasw == password){ 17 | loginsuccess = true; 18 | if (username=='admin') { 19 | menus = await ctx.service.menu.getAllMenu(user.userId); 20 | }else{ 21 | menus = await ctx.service.user.getUserMenus(user.userId); 22 | } 23 | } else { 24 | loginsuccess = false; 25 | } 26 | } else { 27 | loginsuccess = false; 28 | } 29 | 30 | let token = undefined; 31 | if (user != undefined){ 32 | token = app.jwt.sign({userId:user.userId}, app.config.jwt.secret,{expiresIn:app.config.jwt.expiresIn}); 33 | } 34 | 35 | if (loginsuccess){ 36 | this.success({ 37 | token:token, 38 | name:user.name, 39 | menus:menus 40 | }); 41 | }else{ 42 | this.error("登录密码错误"); 43 | } 44 | 45 | 46 | } 47 | 48 | async list(){ 49 | const ctx = this.ctx; 50 | var q = ctx.query; 51 | var rows = await ctx.service.user.getUsersPage(q.page,q.size); 52 | var count = await ctx.service.user.getUsersCount(); 53 | 54 | this.success({"rows":rows, 55 | "count":count}) 56 | } 57 | 58 | async add(){ 59 | const ctx = this.ctx; 60 | var user = ctx.request.body; 61 | await ctx.service.user.add('fuser',user); 62 | this.success({}) 63 | } 64 | 65 | async delete(){ 66 | const ctx = this.ctx; 67 | var q = ctx.query; 68 | var userId = q.userId; 69 | 70 | await ctx.service.user.delete('fuser',{userId:userId}) 71 | this.success({}) 72 | } 73 | 74 | async update(){ 75 | const ctx = this.ctx; 76 | var user = ctx.request.body; 77 | await ctx.service.user.update('fuser',user,{userId:user.userId}); 78 | this.success({}) 79 | } 80 | 81 | async configUserRole(){ 82 | const ctx = this.ctx; 83 | var userRoleFormData = ctx.request.body; 84 | await ctx.service.user.configUserRole(userRoleFormData); 85 | this.success({}) 86 | } 87 | 88 | async getUserRoles(){ 89 | const ctx = this.ctx; 90 | var q = ctx.query; 91 | var rows = await ctx.service.user.getUserRoles(q.userId); 92 | this.success(rows); 93 | } 94 | 95 | async getUserMenus(){ 96 | const ctx = this.ctx; 97 | var q = ctx.query; 98 | var rows = await ctx.service.user.getUserMenus(q.userId); 99 | this.success(rows); 100 | } 101 | 102 | } 103 | 104 | module.exports = UserController; -------------------------------------------------------------------------------- /eggserver/app/core/base_controller.js: -------------------------------------------------------------------------------- 1 | const { Controller } = require('egg'); 2 | class BaseController extends Controller { 3 | get user() { 4 | return this.ctx.session.user; 5 | } 6 | 7 | success(data) { 8 | this.ctx.body = { 9 | success: true, 10 | data, 11 | msg:"", 12 | }; 13 | } 14 | 15 | error(msg) { 16 | this.ctx.body = { 17 | success: false, 18 | data:'', 19 | msg 20 | }; 21 | } 22 | 23 | notFound(msg) { 24 | msg = msg || 'not found'; 25 | this.ctx.throw(404, msg); 26 | } 27 | } 28 | module.exports = BaseController; -------------------------------------------------------------------------------- /eggserver/app/core/base_service.js: -------------------------------------------------------------------------------- 1 | const Service = require('egg').Service; 2 | class BaseService extends Service { 3 | 4 | async query(sqlString){ 5 | return await this.app.mysql.query(sqlString); 6 | } 7 | 8 | async delete(tableName,params) { 9 | const result = await this.app.mysql.delete(tableName, params); 10 | return result; 11 | } 12 | 13 | async add(tableName,data){ 14 | const result = await this.app.mysql.insert(tableName, data); 15 | const insertSuccess = result.affectedRows === 1; 16 | return insertSuccess; 17 | } 18 | 19 | async update(tableName,data,whereParams){ 20 | const result = await this.app.mysql.update(tableName,data, { 21 | where:whereParams, 22 | }); 23 | const updateSuccess = result.affectedRows === 1; 24 | return updateSuccess; 25 | } 26 | 27 | async updateNormal(tableName,data){ 28 | const result = await this.app.mysql.update(tableName,data); 29 | const updateSuccess = result.affectedRows === 1; 30 | return updateSuccess; 31 | } 32 | } 33 | 34 | module.exports = BaseService; -------------------------------------------------------------------------------- /eggserver/app/graphql/common/resolver.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | Date: require('./scalars/date'), // eslint-disable-line 4 | }; 5 | -------------------------------------------------------------------------------- /eggserver/app/graphql/common/scalars/date.js: -------------------------------------------------------------------------------- 1 | const { GraphQLScalarType } = require('graphql'); 2 | const { Kind } = require('graphql/language'); 3 | 4 | module.exports = new GraphQLScalarType({ 5 | name: 'Date', 6 | description: 'Date custom scalar type', 7 | parseValue(value) { 8 | return new Date(value); 9 | }, 10 | serialize(value) { 11 | return value.getTime(); 12 | }, 13 | parseLiteral(ast) { 14 | if (ast.kind === Kind.INT) { 15 | return parseInt(ast.value, 10); 16 | } 17 | return null; 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /eggserver/app/graphql/common/schema.graphql: -------------------------------------------------------------------------------- 1 | scalar Date 2 | 3 | -------------------------------------------------------------------------------- /eggserver/app/graphql/menu/connector.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | class MenuConnector { 3 | constructor(ctx) { 4 | this.ctx = ctx; 5 | } 6 | async getMenuList(pmenuId,page,size) { 7 | return await this.ctx.service.menu.getMenusPage(pmenuId,page,size); 8 | } 9 | 10 | async getMenuCount(pmenuId) { 11 | let count = await this.ctx.service.menu.getMenusCount(pmenuId); 12 | return count; 13 | } 14 | 15 | async getAllMenu() { 16 | return await this.ctx.service.menu.getAllMenu(); 17 | } 18 | 19 | } 20 | 21 | module.exports = MenuConnector; -------------------------------------------------------------------------------- /eggserver/app/graphql/menu/resolver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | Query: { 4 | menu(root,{pmenuId,page,size} ,ctx) { 5 | let menuList = ctx.connector.menu.getMenuList(pmenuId,page,size); 6 | let menuCount = ctx.connector.menu.getMenuCount(pmenuId); 7 | 8 | return { 9 | count:menuCount, 10 | menulist:menuList, 11 | }; 12 | }, 13 | 14 | allmenu(root,{},ctx){ 15 | let menuList = ctx.connector.menu.getAllMenu(); 16 | return menuList; 17 | } 18 | }, 19 | 20 | Mutation: { 21 | createMenu(root, { 22 | menuInput 23 | }, ctx) { 24 | ctx.service.menu.add('fmenu',{ 25 | name:menuInput.name, 26 | icon:menuInput.icon, 27 | pmenuId:menuInput.pmenuId, 28 | orderNum:menuInput.orderNum, 29 | tourl:menuInput.tourl 30 | }); 31 | return true; 32 | }, 33 | 34 | deleteMenu(root, { 35 | menuId 36 | }, ctx) { 37 | ctx.service.menu.delete('fmenu',{menuId:menuId}) 38 | return true; 39 | }, 40 | 41 | updateMenu(root, { 42 | menuId,menuInput 43 | }, ctx) { 44 | ctx.service.menu.update('fmenu',menuInput,{menuId:menuId}); 45 | return true; 46 | }, 47 | }, 48 | }; -------------------------------------------------------------------------------- /eggserver/app/graphql/menu/schema.graphql: -------------------------------------------------------------------------------- 1 | 2 | type Menu { 3 | menuId: Int! 4 | name: String! 5 | menuKey: String! 6 | pmenuId: Int! 7 | orderNum:Int! 8 | tourl:String! 9 | tag:String! 10 | icon:String 11 | } 12 | 13 | type MenuList { 14 | count: Int! 15 | menulist: [Menu]! 16 | } 17 | 18 | input MenuInput { 19 | name: String! 20 | menuKey: String 21 | pmenuId: Int! 22 | orderNum:Int! 23 | tourl:String! 24 | tag:String 25 | icon:String 26 | } 27 | -------------------------------------------------------------------------------- /eggserver/app/graphql/mutation/schema.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | type Mutation { 4 | 5 | # User 6 | createUser (userInput: UserInput!): User 7 | updateUser (userId: Int!, userInput: UserInput!): User 8 | deleteUser (userId: Int!): User 9 | 10 | configUserRole (userId: Int!, userRoles: String!): Boolean 11 | 12 | # Menu 13 | createMenu (menuInput: MenuInput!): Boolean 14 | updateMenu (menuId: Int!, menuInput: MenuInput!): Boolean 15 | deleteMenu (menuId: Int!): Boolean 16 | 17 | # Role 18 | createRole (roleInput: RoleInput!): Boolean 19 | updateRole (roleId: Int!, roleInput: RoleInput!): Boolean 20 | deleteRole (roleId: Int!): Boolean 21 | configRoleMenu (roleId: Int!, roleMenus: String!): Boolean 22 | } -------------------------------------------------------------------------------- /eggserver/app/graphql/query/schema.graphql: -------------------------------------------------------------------------------- 1 | 2 | type Query { 3 | user(page: Int!,size: Int!): UserList! 4 | userRoles(userId : Int!) : [UserRole]! 5 | 6 | menu(pmenuId:Int,page: Int!,size: Int!): MenuList! 7 | allmenu : [Menu]! 8 | 9 | role(proleId:Int,page: Int!,size: Int!): RoleList! 10 | allrole : [Role]! 11 | roleMenus(roleId: Int!) : [RoleMenu]! 12 | } -------------------------------------------------------------------------------- /eggserver/app/graphql/role/connector.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | class RoleConnector { 3 | constructor(ctx) { 4 | this.ctx = ctx; 5 | } 6 | async getRoleList(proleId,page,size) { 7 | return await this.ctx.service.role.getRolesPage(proleId,page,size); 8 | } 9 | 10 | async getRoleCount(proleId) { 11 | let count = await this.ctx.service.role.getRolesCount(proleId); 12 | return count; 13 | } 14 | 15 | async getAllRoles() { 16 | return await this.ctx.service.role.getAllRoles(); 17 | } 18 | 19 | async getRoleMenus(roleId) { 20 | return await this.ctx.service.role.getRoleMenus(roleId); 21 | } 22 | 23 | } 24 | 25 | module.exports = RoleConnector; -------------------------------------------------------------------------------- /eggserver/app/graphql/role/resolver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | Query: { 4 | role(root,{proleId,page,size} ,ctx) { 5 | let roleList = ctx.connector.role.getRoleList(proleId,page,size); 6 | let roleCount = ctx.connector.role.getRoleCount(proleId); 7 | 8 | return { 9 | count:roleCount, 10 | rolelist:roleList, 11 | }; 12 | }, 13 | 14 | allrole(root,{},ctx){ 15 | let roleList = ctx.connector.role.getAllRoles(); 16 | return roleList; 17 | }, 18 | 19 | roleMenus(root,{roleId} ,ctx) { 20 | let roleMenus = ctx.connector.role.getRoleMenus(roleId); 21 | return roleMenus; 22 | }, 23 | }, 24 | 25 | Mutation: { 26 | createRole(root, { 27 | roleInput 28 | }, ctx) { 29 | ctx.service.role.add('frole',{ 30 | name:roleInput.name, 31 | proleId:roleInput.proleId, 32 | }); 33 | return true; 34 | }, 35 | 36 | deleteRole(root, { 37 | roleId 38 | }, ctx) { 39 | ctx.service.role.delete('frole',{roleId:roleId}) 40 | return true; 41 | }, 42 | 43 | updateRole(root, { 44 | roleId,roleInput 45 | }, ctx) { 46 | ctx.service.role.update('frole',roleInput,{roleId:roleId}); 47 | return true; 48 | }, 49 | 50 | configRoleMenu(root, { 51 | roleId, 52 | roleMenus 53 | }, ctx) { 54 | let data = { 55 | roleId:roleId, 56 | menus:roleMenus 57 | } 58 | ctx.service.role.configRoleMenu(data); 59 | return true; 60 | }, 61 | }, 62 | }; -------------------------------------------------------------------------------- /eggserver/app/graphql/role/schema.graphql: -------------------------------------------------------------------------------- 1 | 2 | type Role { 3 | roleId: Int! 4 | name: String! 5 | proleId: Int! 6 | } 7 | 8 | type RoleList { 9 | count: Int! 10 | rolelist: [Role]! 11 | } 12 | 13 | type RoleMenu { 14 | menuId:Int! 15 | } 16 | 17 | input RoleInput { 18 | name: String! 19 | proleId: Int! 20 | } 21 | -------------------------------------------------------------------------------- /eggserver/app/graphql/user/connector.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | class UserConnector { 3 | constructor(ctx) { 4 | this.ctx = ctx; 5 | } 6 | async getUserList(page,size) { 7 | return await this.ctx.service.user.getUsersPage(page,size); 8 | } 9 | 10 | async getUserCount() { 11 | let count = await this.ctx.service.user.getUsersCount(); 12 | return count; 13 | } 14 | 15 | async getUserRoles(userId) { 16 | let list = await this.ctx.service.user.getUserRoles(userId); 17 | return list; 18 | } 19 | 20 | } 21 | 22 | module.exports = UserConnector; -------------------------------------------------------------------------------- /eggserver/app/graphql/user/resolver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | Query: { 4 | user(root,{page,size} ,ctx) { 5 | let userlist = ctx.connector.user.getUserList(page,size); 6 | let userCount = ctx.connector.user.getUserCount(); 7 | 8 | return { 9 | count:userCount, 10 | userlist:userlist, 11 | }; 12 | }, 13 | 14 | userRoles(root,{userId} ,ctx) { 15 | let userRole = ctx.connector.user.getUserRoles(userId); 16 | return userRole; 17 | }, 18 | 19 | }, 20 | 21 | Mutation: { 22 | createUser(root, { 23 | userInput 24 | }, ctx) { 25 | ctx.service.user.add('fuser',{ 26 | loginName:userInput.loginName, 27 | loginPasw:userInput.loginPasw, 28 | name:userInput.name 29 | }); 30 | return {loginName:""}; 31 | }, 32 | 33 | deleteUser(root, { 34 | userId 35 | }, ctx) { 36 | ctx.service.user.delete('fuser',{userId:userId}) 37 | return {loginName:""}; 38 | }, 39 | 40 | updateUser(root, { 41 | userId,userInput 42 | }, ctx) { 43 | ctx.service.user.update('fuser',userInput,{userId:userId}); 44 | return {loginName:""}; 45 | }, 46 | 47 | configUserRole(root, { 48 | userId, 49 | userRoles 50 | }, ctx) { 51 | let data = { 52 | userId:userId, 53 | roles:userRoles 54 | } 55 | ctx.service.user.configUserRole(data); 56 | return true; 57 | }, 58 | 59 | }, 60 | }; -------------------------------------------------------------------------------- /eggserver/app/graphql/user/schema.graphql: -------------------------------------------------------------------------------- 1 | 2 | type User { 3 | userId: Int! 4 | loginName: String! 5 | loginPasw: String! 6 | name: String! 7 | } 8 | 9 | type UserList { 10 | count: Int! 11 | userlist: [User]! 12 | } 13 | 14 | type UserRole { 15 | roleId:Int! 16 | } 17 | 18 | input UserInput { 19 | loginName: String! 20 | loginPasw: String! 21 | name: String! 22 | } 23 | -------------------------------------------------------------------------------- /eggserver/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {Egg.Application} app - egg application 5 | */ 6 | module.exports = app => { 7 | require('./router/qiniu')(app); 8 | require('./router/user')(app); 9 | require('./router/menu')(app); 10 | require('./router/role')(app); 11 | require('./router/upload')(app); 12 | }; 13 | -------------------------------------------------------------------------------- /eggserver/app/router/menu.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | // app.router.get('/api/menu/list', app.jwt,app.controller.menu.list); 3 | // app.router.get('/api/menu/listByPid', app.jwt,app.controller.menu.listByPid); 4 | // app.router.get('/api/menu/allList', app.jwt,app.controller.menu.allList); 5 | // app.router.post('/api/menu/add', app.jwt,app.controller.menu.add); 6 | // app.router.get('/api/menu/delete', app.jwt,app.controller.menu.delete); 7 | // app.router.post('/api/menu/update',app.jwt, app.controller.menu.update); 8 | 9 | app.router.get('/api/menu/list',app.controller.menu.list); 10 | app.router.get('/api/menu/listByPid',app.controller.menu.listByPid); 11 | app.router.get('/api/menu/allList',app.controller.menu.allList); 12 | app.router.post('/api/menu/add',app.controller.menu.add); 13 | app.router.get('/api/menu/delete',app.controller.menu.delete); 14 | app.router.post('/api/menu/update', app.controller.menu.update); 15 | }; -------------------------------------------------------------------------------- /eggserver/app/router/qiniu.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = app => { 4 | app.router.post('/api/qiniu', app.controller.qiniu.qiniuUploadStream); 5 | }; -------------------------------------------------------------------------------- /eggserver/app/router/role.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.router.get('/api/role/list', app.jwt,app.controller.role.list); 3 | app.router.get('/api/role/listByPid', app.jwt,app.controller.role.listByPid); 4 | app.router.get('/api/role/allList', app.jwt,app.controller.role.allList); 5 | app.router.post('/api/role/add', app.jwt,app.controller.role.add); 6 | app.router.get('/api/role/delete', app.jwt,app.controller.role.delete); 7 | app.router.post('/api/role/update',app.jwt, app.controller.role.update); 8 | app.router.post('/api/role/configRoleMenu',app.jwt, app.controller.role.configRoleMenu); 9 | app.router.get('/api/role/getRoleMenus',app.jwt, app.controller.role.getRoleMenus); 10 | }; -------------------------------------------------------------------------------- /eggserver/app/router/upload.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.router.post('/api/upload',app.controller.upload.upload); 3 | }; -------------------------------------------------------------------------------- /eggserver/app/router/user.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.router.post('/api/auth/login', app.controller.user.login); 3 | app.router.get('/api/user/list', app.jwt,app.controller.user.list); 4 | app.router.post('/api/user/add', app.jwt,app.controller.user.add); 5 | app.router.get('/api/user/delete', app.jwt,app.controller.user.delete); 6 | app.router.post('/api/user/update', app.jwt,app.controller.user.update); 7 | app.router.post('/api/user/configUserRole', app.jwt,app.controller.user.configUserRole); 8 | app.router.get('/api/user/getUserRoles', app.jwt,app.controller.user.getUserRoles); 9 | app.router.get('/api/user/getUserMenus', app.jwt,app.controller.user.getUserMenus); 10 | }; -------------------------------------------------------------------------------- /eggserver/app/service/menu.js: -------------------------------------------------------------------------------- 1 | const Service = require('../core/base_service'); 2 | 3 | class MenuService extends Service { 4 | async getMenusPage(pmenuId,page,size){ 5 | 6 | let params = {}; 7 | 8 | var page = parseInt(page); 9 | var size = parseInt(size); 10 | 11 | var start = (page-1)*size; 12 | 13 | if (pmenuId != undefined && pmenuId != '') { 14 | params.pmenuId = pmenuId; 15 | }; 16 | 17 | var rows = await this.app.mysql.select('fmenu', { 18 | where: params, 19 | orders: [['orderNum','desc']], 20 | limit: size, 21 | offset: start, 22 | }); 23 | 24 | return rows; 25 | } 26 | 27 | async getMenusCount(pmenuId) { 28 | 29 | let sqlString = `select count(1) as cnt from fmenu where 1=1 `; 30 | if (pmenuId != undefined && pmenuId != '') { 31 | sqlString += ` and pmenuId = ${pmenuId} `; 32 | }; 33 | 34 | let rows = await this.app.mysql.query(sqlString); 35 | 36 | let count = 0 ; 37 | if (rows.length > 0){ 38 | count = rows[0].cnt; 39 | } 40 | 41 | return count; 42 | } 43 | 44 | async getMenuByPid(pmenuId){ 45 | var sqlString = `select * from fmenu where 1=1 `; 46 | if (pmenuId != undefined && pmenuId != '') { 47 | sqlString += ` and pmenuId=${pmenuId} `; 48 | }; 49 | let rows = await this.app.mysql.query(sqlString); 50 | return rows; 51 | } 52 | 53 | async getAllMenu(){ 54 | var rows = await this.app.mysql.select('fmenu', { 55 | orders: [['orderNum','desc']], 56 | }); 57 | return rows; 58 | } 59 | 60 | } 61 | 62 | module.exports = MenuService; -------------------------------------------------------------------------------- /eggserver/app/service/role.js: -------------------------------------------------------------------------------- 1 | const Service = require('../core/base_service'); 2 | 3 | class RoleService extends Service { 4 | 5 | async getRolesPage(proleId,page,size){ 6 | 7 | let params = {}; 8 | 9 | if (proleId != undefined && proleId != '') { 10 | params.proleId = proleId; 11 | }; 12 | 13 | var pageInt = parseInt(page); 14 | var sizeInt = parseInt(size); 15 | 16 | var start = (page-1)*size; 17 | 18 | var rows = await this.app.mysql.select('frole', { 19 | where: params, 20 | limit: size, 21 | offset: start, 22 | }); 23 | 24 | return rows; 25 | } 26 | 27 | async getAllRoles() { 28 | var rows = await this.app.mysql.select('frole'); 29 | return rows; 30 | } 31 | 32 | async getRolesCount(proleId) { 33 | let sqlString = `select count(1) as cnt from frole where 1=1 `; 34 | if (proleId != undefined && proleId != '') { 35 | sqlString += ` and proleId = ${proleId} `; 36 | }; 37 | 38 | let rows = await this.app.mysql.query(sqlString); 39 | 40 | let count = 0 ; 41 | if (rows.length > 0){ 42 | count = rows[0].cnt; 43 | } 44 | 45 | return count; 46 | } 47 | 48 | async getRoleByPid (proleId) { 49 | let params = {}; 50 | if (proleId != undefined && proleId != '') { 51 | params.proleId = proleId; 52 | } 53 | 54 | let rows = await this.app.mysql.select('frole', 55 | {where:params}); 56 | return rows; 57 | } 58 | 59 | 60 | async configRoleMenu(data){ 61 | var menus = data.menus ; 62 | await this.query(`delete from froleMenu where roleId = ${data.roleId} `); 63 | if (menus!=undefined && menus != ""){ 64 | var menuArray = menus.split(","); 65 | for(var i = 0 ; i< menuArray.length ; i ++){ 66 | var menuId = menuArray[i]; 67 | if (parseInt(menuId) != -1 ) { 68 | var insertsql = `INSERT INTO froleMenu(menuId, roleId) SELECT ${menuId}, ${data.roleId} FROM DUAL WHERE NOT EXISTS(SELECT * FROM froleMenu WHERE menuId =${menuId} and roleId =${data.roleId} )`; 69 | await this.query(insertsql); 70 | } 71 | } 72 | } 73 | } 74 | 75 | async getRoleMenus (roleId) { 76 | let params = {}; 77 | if (roleId != undefined && roleId != '') { 78 | params.roleId = roleId; 79 | }; 80 | 81 | const rows = await this.app.mysql.select('froleMenu',{ 82 | where:params, 83 | columns: ['menuId'], 84 | }) 85 | return rows; 86 | } 87 | 88 | } 89 | 90 | module.exports = RoleService; -------------------------------------------------------------------------------- /eggserver/app/service/user.js: -------------------------------------------------------------------------------- 1 | const Service = require('../core/base_service'); 2 | 3 | class UserService extends Service { 4 | 5 | async checkUser(username,password){ 6 | 7 | var loginsuccess = false; 8 | 9 | var user = await this.app.mysql.select('fuser', { // 搜索 post 表 10 | loginName: username, // 返回数据量 11 | }); 12 | 13 | // var menus = []; 14 | // if (user!=undefined) { 15 | // if(user.loginPasw == password){ 16 | // loginsuccess = true; 17 | // if (username=='admin') { 18 | // menus = await menuService.getAllMenu(user.userId); 19 | // }else{ 20 | // menus = await userService.getUserMenus(user.userId); 21 | // } 22 | // } else { 23 | // loginsuccess = false; 24 | // } 25 | // } else { 26 | // loginsuccess = false; 27 | // } 28 | 29 | // var token = jwt.sign(user, tokenConfig.JWT_SECRET,{expiresIn:tokenConfig.JWT_expiresIn}); 30 | 31 | return user; 32 | } 33 | 34 | async getUsersPage(page,size) { 35 | 36 | var page = parseInt(page); 37 | var size = parseInt(size); 38 | 39 | var start = (page-1)*size; 40 | var end = page*size; 41 | 42 | const users = await this.app.mysql.select('fuser', { // 搜索 post 表 43 | limit: size, // 返回数据量 44 | offset: start, // 数据偏移量 45 | }); 46 | 47 | return users; 48 | } 49 | 50 | async getUserByUserName (username) { 51 | const row = await this.app.mysql.get('fuser',{loginName:username}); 52 | return row; 53 | } 54 | 55 | async getUsersCount(){ 56 | var sqlString = `select count(1) as cnt from fuser `; 57 | 58 | let countRows = await this.query(sqlString); 59 | let count = 0; 60 | if (countRows != undefined){ 61 | count = countRows[0].cnt; 62 | } 63 | 64 | return count; 65 | } 66 | 67 | async configUserRole(data){ 68 | var roles = data.roles ; 69 | await this.query(`delete from fuserrole where userId = ${data.userId} `); 70 | if (roles!=undefined && roles != ""){ 71 | var roleArray = roles.split(","); 72 | for(var i = 0 ; i< roleArray.length ; i ++){ 73 | var roleId = roleArray[i]; 74 | if (parseInt(roleId) != -1 ) { 75 | var insertsql = `INSERT INTO fuserrole(userId, roleId) SELECT ${data.userId}, ${roleId} FROM DUAL WHERE NOT EXISTS(SELECT * FROM fuserrole WHERE userId =${data.userId} and roleId =${roleId} )`; 76 | await this.query(insertsql); 77 | } 78 | } 79 | } 80 | } 81 | 82 | async getUserRoles(userId) { 83 | 84 | let params = {}; 85 | if (userId != undefined && userId != '') { 86 | params.userId = userId; 87 | } 88 | 89 | const rows = await this.app.mysql.select('fuserrole',{ 90 | where:params, 91 | columns:['roleId'] 92 | }) 93 | 94 | return rows; 95 | } 96 | 97 | 98 | async getUserMenus(userId) { 99 | var sqlString = `select distinct m.* from fmenu m inner join frolemenu rm on m.menuId = rm.menuId where rm.roleId in 100 | (select r.roleId from frole r inner join fuserrole ur on r.roleId = ur.roleId where ur.userId = ${userId}) order by m.orderNum `; 101 | 102 | console.log(sqlString); 103 | var rows = await this.query(sqlString); 104 | return rows; 105 | } 106 | 107 | } 108 | 109 | module.exports = UserService; 110 | -------------------------------------------------------------------------------- /eggserver/appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '8' 4 | 5 | install: 6 | - ps: Install-Product node $env:nodejs_version 7 | - npm i npminstall && node_modules\.bin\npminstall 8 | 9 | test_script: 10 | - node --version 11 | - npm --version 12 | - npm run test 13 | 14 | build: off 15 | -------------------------------------------------------------------------------- /eggserver/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = appInfo => { 4 | const config = exports = {}; 5 | 6 | // use for cookie sign key, should change to your own and keep security 7 | config.keys = appInfo.name + '_1520384613004_1138'; 8 | 9 | // add your config here 10 | config.middleware = [ 'graphql' ]; 11 | 12 | config.upload = { 13 | localFilePrex: '/Users/chenliang/website/', 14 | remoteFilePrex: 'http://localhost:7002/', 15 | qiniuCdn:'http://ybimage.atogether.com/', 16 | } 17 | 18 | config.security = { 19 | csrf: { 20 | enable: false, 21 | }, 22 | } 23 | 24 | config.jwt = { 25 | secret: 'frameAntDesignToken', 26 | expiresIn: "30m", 27 | enable: true, // default is false 28 | match: ['/api/menu/*','/graphql'], // �optional 29 | // match: ['/api/menu/*'], // �optional 30 | }; 31 | 32 | config.mysql = { 33 | // 单数据库信息配置 34 | client: { 35 | // host 36 | host: '114.80.200.31', 37 | // 端口号 38 | port: '3306', 39 | // 用户名 40 | user: 'root', 41 | // 密码 42 | password: '11111', 43 | // 数据库名 44 | database: 'tyh_qx', 45 | }, 46 | // 是否加载到 app 上,默认开启 47 | app: true, 48 | // 是否加载到 agent 上,默认关闭 49 | agent: false, 50 | }; 51 | 52 | config.graphql = { 53 | router: '/graphql', 54 | // 是否加载到 app 上,默认开启 55 | app: true, 56 | // 是否加载到 agent 上,默认关闭 57 | agent: false, 58 | // 是否加载开发者工具 graphiql, 默认开启。路由同 router 字段。使用浏览器打开该可见。 59 | graphiql: true, 60 | // graphQL 路由前的拦截器 61 | onPreGraphQL: function* (ctx) {}, 62 | // 开发工具 graphiQL 路由前的拦截器,建议用于做权限操作(如只提供开发者使用) 63 | onPreGraphiQL: function* (ctx) {}, 64 | }; 65 | 66 | return config; 67 | }; -------------------------------------------------------------------------------- /eggserver/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // had enabled by egg 4 | // exports.static = true; 5 | 6 | exports.mysql = { 7 | enable: true, 8 | package: 'egg-mysql', 9 | }; 10 | 11 | exports.jwt = { 12 | enable: true, 13 | package: 'egg-jwt', 14 | }; 15 | 16 | exports.graphql = { 17 | enable: true, 18 | package: 'egg-graphql', 19 | }; -------------------------------------------------------------------------------- /eggserver/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LmmEggFrame", 3 | "version": "1.0.0", 4 | "description": "Lmm后台管理", 5 | "private": true, 6 | "dependencies": { 7 | "egg": "^2.2.1", 8 | "egg-graphql": "^2.1.0", 9 | "egg-jwt": "^2.2.1", 10 | "egg-mysql": "^3.0.0", 11 | "egg-scripts": "^2.5.0", 12 | "graphql": "^0.13.2", 13 | "mkdirp": "^0.5.1", 14 | "qiniu": "^7.2.1", 15 | "stream-wormhole": "^1.0.3" 16 | }, 17 | "devDependencies": { 18 | "autod": "^3.0.1", 19 | "autod-egg": "^1.0.0", 20 | "egg-bin": "^4.3.5", 21 | "egg-ci": "^1.8.0", 22 | "egg-mock": "^3.14.0", 23 | "eslint": "^4.11.0", 24 | "eslint-config-egg": "^6.0.0", 25 | "webstorm-disable-index": "^1.2.0" 26 | }, 27 | "engines": { 28 | "node": ">=8.9.0" 29 | }, 30 | "scripts": { 31 | "start": "egg-scripts start --daemon --title=egg-server-LmmEggFrame", 32 | "stop": "egg-scripts stop --title=egg-server-LmmEggFrame", 33 | "dev": "egg-bin dev", 34 | "debug": "egg-bin debug", 35 | "test": "npm run lint -- --fix && npm run test-local", 36 | "test-local": "egg-bin test", 37 | "cov": "egg-bin cov", 38 | "lint": "eslint .", 39 | "ci": "npm run lint && npm run cov", 40 | "autod": "autod" 41 | }, 42 | "ci": { 43 | "version": "8" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "" 48 | }, 49 | "author": "陈靓", 50 | "license": "MIT" 51 | } 52 | -------------------------------------------------------------------------------- /eggserver/test/app/controller/home.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { app, assert } = require('egg-mock/bootstrap'); 4 | 5 | describe('test/app/controller/home.test.js', () => { 6 | 7 | it('should assert', function* () { 8 | const pkg = require('../../../package.json'); 9 | assert(app.config.keys.startsWith(pkg.name)); 10 | 11 | // const ctx = app.mockContext({}); 12 | // yield ctx.service.xx(); 13 | }); 14 | 15 | it('should GET /', () => { 16 | return app.httpRequest() 17 | .get('/') 18 | .expect('hi, egg') 19 | .expect(200); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /img/b1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/b1.png -------------------------------------------------------------------------------- /img/configproxy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/configproxy.jpeg -------------------------------------------------------------------------------- /img/database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/database.png -------------------------------------------------------------------------------- /img/login.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/login.jpeg -------------------------------------------------------------------------------- /img/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/login.png -------------------------------------------------------------------------------- /img/main.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/main.jpeg -------------------------------------------------------------------------------- /img/page1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/page1.jpeg -------------------------------------------------------------------------------- /img/page2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/page2.jpeg -------------------------------------------------------------------------------- /img/startproject.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/startproject.jpeg -------------------------------------------------------------------------------- /img/tiaoshi.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/tiaoshi.jpeg -------------------------------------------------------------------------------- /img/webpackserverconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenliang2016/CLReactAntDesign/445318d8ce9e38d4c150351628ca2dedc677384c/img/webpackserverconfig.png -------------------------------------------------------------------------------- /koaFrame.md: -------------------------------------------------------------------------------- 1 | # CLReactAntDesign介绍 2 | * CLReactAntDesign是基于[AntDesign2.0+](https://ant.design/)开发的后端页面例子, 3 | * 整合react,react-router,react-redux,react-router-redux。进行页面状态控制 4 | * 实用fetch进行页面请求,最终生成静态页面,实现前后端完全分离 5 | * 附带使用koa实现的后端接口 //住server下面是koa写的接口,目前框架不使用此接口,仅供想学习koa的同学参考参考接口 6 | * 新版本使用egg实现后端接口 7 | 8 | # 开发环境 9 | * 基于node8.0+进行开发 10 | 11 | # 目录介绍 12 | * backend:基于AntDesign使用的后端SPA。 13 | * server:后端koa搭建的服务接口,主要包括,菜单管理,角色管理,用户管理,信息发布。 //目前废弃不再使用 14 | * eggserver:后端使用egg搭建服务接口 15 | 16 | 使用前,调整mysql数据库。使用 clantd.sql修改数据库。 17 | 18 | # 工程运行 19 | 进入到backend目录,编译运行前端框架 20 | 21 | ## 安装 22 | 23 | 安装node依赖 24 | 25 | ``` 26 | npm install 27 | 28 | ``` 29 | 30 | ## 开发环境 31 | 32 | 开发使用webpack进行编译,并使用webpack.server启动一个服务器,进行页面的访问, 33 | 34 | 使用前,配置webpack.server,修改接口的转发。主要是调整端口,如果接口端口不冲突,不需要调整。 35 | 36 | ![](img/webpackserverconfig.png) 37 | 38 | 如上图, 39 | 40 | 前端转发,即今天服务器地址,默认端口port是9000,所以请求页面的默认地址是localhost:9000/index.html或localhost:9000 41 | 42 | 后端转发,为接口转发的时候,映射的地址。根据server工程设置的端口定,默认有两个,一个是请求常用接口,一个是登录验证。可视情况做相应调整 43 | 44 | 启动 45 | 46 | ``` 47 | npm run dev 48 | 49 | ``` 50 | 51 | ## 生产环境 52 | 53 | ``` 54 | npm run pub 55 | 56 | ``` 57 | 58 | 执行上述命令,会生成相应的文件到根目录下的pub文件夹,直接放服务器中使用即可。 59 | 60 | 生产环境,建议使用ngnix部署,直接配置静态文件目录,放目录下,使用ngnix进行接口转发。解决跨域问题。 61 | 62 | # 后端工程启动 63 | 64 | 后端工程主要提供了一些基础的接口。使用egg来开发。利用JWT整合token机制。 65 | 66 | 67 | ## 安装 68 | 69 | 使用clantd.sql进行数据库的安装, 70 | 71 | 进入到eggserver文件夹下, 72 | 73 | ### 修改数据库配置文件 74 | 75 | 修改eggserver/config/config.default.js下的数据库配置文件 76 | 77 | ![](img/database.png) 78 | 79 | 安装依赖 80 | 81 | ``` 82 | npm install 83 | 84 | ``` 85 | 86 | ## 启动开发 87 | 88 | ``` 89 | npm run dev 90 | 91 | ``` 92 | 93 | ## 后台部署 94 | 95 | ``` 96 | npm start //开始 97 | npm stop //stop 98 | 99 | ``` 100 | 101 | 102 | # 展示 103 | 访问地址 localhost:9000 登录用户名密码,admin/123 104 | 105 | ![](img/login.png) 106 | 107 | ![](img/b1.png) -------------------------------------------------------------------------------- /server/config/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by chenliang on 2017/1/12. 3 | */ 4 | const path = require('path'); 5 | const _ = require('lodash'); 6 | const env = process.env.NODE_ENV || 'development'; 7 | 8 | const defaults = { 9 | upload:{ 10 | localFilePrex:'/Users/chenliang/website/upload/',//上传的图片位置 11 | downloadFilePrex:'http://localhost/upload/', 12 | }, 13 | database:{ 14 | host: '127.0.0.1', 15 | user: 'root', 16 | password: '11111', 17 | port: '3306', 18 | database: 'lmmFrame', 19 | }, 20 | tokenConfig:{ 21 | JWT_SECRET:"frameAntDesignToken", 22 | JWT_expiresIn:"30m" 23 | } 24 | }; 25 | 26 | //正式环境参数不同的情况下修改对应参数 27 | const specific = { 28 | test: {}, 29 | production: { 30 | upload:{ 31 | localFilePrex:'/Users/chenliang/website/upload/',//上传的图片位置 32 | downloadFilePrex:'http://localhost/upload/', 33 | }, 34 | database:{ 35 | host: 'rm-uf69e0aq27f1zj341o.mysql.rds.aliyuncs.com', 36 | user: 'zhendedev', 37 | password: 'Zhendedev123456', 38 | port: '3306', 39 | database: 'framework', 40 | }, 41 | loggers: { 42 | console: { 43 | level: 'info' 44 | } 45 | } 46 | } 47 | }; 48 | 49 | const finallyOpts = _.defaultsDeep(specific[env], defaults); 50 | 51 | module.exports = finallyOpts; -------------------------------------------------------------------------------- /server/database/mysqlUtil.js: -------------------------------------------------------------------------------- 1 | var wrapper = require('co-mysql'), 2 | mysql = require('mysql'); 3 | 4 | var config = require('../config/config'); 5 | 6 | var options = { 7 | host: config.database.host, 8 | user: config.database.user, 9 | password: config.database.password, 10 | port: config.database.port, 11 | database: config.database.database, 12 | }; 13 | 14 | var pool = mysql.createPool(options), 15 | p = wrapper(pool); 16 | 17 | module.exports = p; 18 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "entry": {}, 4 | "dependencies": { 5 | "co": "^4.6.0", 6 | "co-busboy": "^1.3.1", 7 | "co-mysql": "^1.0.0", 8 | "es3ify-loader": "^0.2.0", 9 | "history": "^2.0.1", 10 | "isomorphic-fetch": "^2.2.1", 11 | "js-cookie": "^2.1.1", 12 | "jsonwebtoken": "^7.4.1", 13 | "koa": "^2.0.0", 14 | "koa-body": "^2.0.0", 15 | "koa-bodyparser": "^4.2.0", 16 | "koa-convert": "^1.2.0", 17 | "koa-json": "^2.0.2", 18 | "koa-jwt": "^2.2.4", 19 | "koa-router": "^7.0.1", 20 | "koa-static": "^3.0.0", 21 | "lodash": "^4.15.0", 22 | "memory-cache": "^0.1.6", 23 | "mysql": "^2.10.0", 24 | "reqwest": "^2.0.5", 25 | "mkdirp": "^0.5.1", 26 | "stream-wormhole": "^1.0.3" 27 | }, 28 | "scripts": { 29 | "start": "nodemon --harmony server.js" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/process.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [ 3 | { 4 | "name": "zhende-admin", 5 | "script": "server.js", 6 | "interpreter_args": "--harmony", 7 | "instances": "0", 8 | "exec_mode": "cluster", 9 | "merge_logs": true, 10 | "log_date_format": "YYYY-MM-DD HH:mm Z", 11 | "env": { 12 | "PORT": 8082 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /server/routers/api/frame/auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by chenliang on 16/1/4. 3 | */ 4 | 5 | const Router = require('koa-router'); 6 | const router = new Router(); 7 | 8 | var config = require('../../../config/config'); 9 | var tokenConfig = config.tokenConfig 10 | 11 | var jwt = require('jsonwebtoken'); 12 | 13 | var userService = require('../../../service/frame/userService'); 14 | var menuService = require('../../../service/frame/menuService'); 15 | 16 | router.post('/login', async (ctx) => { 17 | const username = ctx.request.body.username; 18 | const password = ctx.request.body.password; 19 | var loginsuccess = false; 20 | var user = await userService.getUserByUserName(username); 21 | var menus = []; 22 | if (user!=undefined) { 23 | if(user.loginPasw == password){ 24 | loginsuccess = true; 25 | if (username=='admin') { 26 | menus = await menuService.getAllMenu(user.userId); 27 | }else{ 28 | menus = await userService.getUserMenus(user.userId); 29 | } 30 | } else { 31 | loginsuccess = false; 32 | } 33 | } else { 34 | loginsuccess = false; 35 | } 36 | 37 | var token = jwt.sign(user, tokenConfig.JWT_SECRET,{expiresIn:tokenConfig.JWT_expiresIn}); 38 | 39 | ctx.body = { 40 | success : loginsuccess, 41 | data:{ 42 | token:token, 43 | name:user.name 44 | }, 45 | menus:menus, 46 | }; 47 | }) 48 | 49 | 50 | 51 | module.exports = router; 52 | -------------------------------------------------------------------------------- /server/routers/api/frame/menu.js: -------------------------------------------------------------------------------- 1 | var Router = require('koa-router'); 2 | var router = new Router(); 3 | 4 | var menuService = require('../../../service/frame/menuService'); 5 | 6 | router.get('/list', async (ctx) => { 7 | var q = ctx.query; 8 | var rows = await menuService.getMenusPage(q.pmenuId,q.page,q.size); 9 | var count = await menuService.getMenusCount(q.pmenuId); 10 | 11 | ctx.body = {"rows":rows, 12 | "count":count}; 13 | }); 14 | 15 | router.get('/listByPid',async (ctx) => { 16 | var q = ctx.query; 17 | var rows = await menuService.getMenuByPid(q.pid); 18 | ctx.body = rows; 19 | }); 20 | 21 | router.get('/allList', async (ctx) => { 22 | var q = ctx.query; 23 | var rows = await menuService.getAllMenu(); 24 | ctx.body = rows; 25 | }); 26 | 27 | router.post('/add', async (ctx) => { 28 | var menu = ctx.request.body; 29 | await menuService.addNew(menu); 30 | ctx.body = { 31 | success : true, 32 | }; 33 | }); 34 | 35 | router.get('/delete', async (ctx) => { 36 | var q = ctx.query; 37 | var id = q.id; 38 | await menuService.delete(id); 39 | ctx.body = { 40 | success : true, 41 | }; 42 | }); 43 | 44 | router.post('/update', async (ctx) => { 45 | var menu = ctx.request.body; 46 | await menuService.update(menu); 47 | ctx.body = { 48 | success : true, 49 | }; 50 | }); 51 | 52 | 53 | module.exports = router; 54 | -------------------------------------------------------------------------------- /server/routers/api/frame/role.js: -------------------------------------------------------------------------------- 1 | var Router = require('koa-router'); 2 | var router = new Router(); 3 | 4 | var roleService = require('../../../service/frame/roleService'); 5 | 6 | router.get('/list', async (ctx) => { 7 | var q = ctx.query; 8 | var rows = await roleService.getRolesPage(q.proleId,q.page,q.size); 9 | var count = await roleService.getRolesCount(q.proleId); 10 | 11 | ctx.body = {"rows":rows, 12 | "count":count}; 13 | }); 14 | 15 | router.get('/listByPid',async (ctx) => { 16 | var q = ctx.query; 17 | var rows = await roleService.getRoleByPid(q.pid); 18 | ctx.body = rows; 19 | }); 20 | 21 | router.get('/allList', async (ctx) => { 22 | var q = ctx.query; 23 | var rows = await roleService.getAllRoles(); 24 | 25 | ctx.body = rows; 26 | }); 27 | 28 | router.post('/add', async (ctx) =>{ 29 | var role = ctx.request.body; 30 | await roleService.addNew(role); 31 | ctx.body = { 32 | success : true, 33 | }; 34 | }); 35 | 36 | router.get('/delete', async (ctx) => { 37 | var q = ctx.query; 38 | var id = q.id; 39 | await roleService.delete(id); 40 | ctx.body = { 41 | success : true, 42 | }; 43 | }); 44 | 45 | router.post('/update', async (ctx) => { 46 | var role = ctx.request.body; 47 | await roleService.update(role); 48 | ctx.body = { 49 | success : true, 50 | }; 51 | }); 52 | 53 | router.post('/configRoleMenu', async (ctx) => { 54 | var roleMenuFormData = ctx.request.body; 55 | await roleService.configRoleMenu(roleMenuFormData); 56 | ctx.body = { 57 | success : true, 58 | }; 59 | }); 60 | 61 | router.get('/getRoleMenus', async (ctx) => { 62 | var q = ctx.query; 63 | var rows = await roleService.getRoleMenus(q.roleId); 64 | ctx.body = rows; 65 | }); 66 | 67 | module.exports = router; 68 | -------------------------------------------------------------------------------- /server/routers/api/frame/user.js: -------------------------------------------------------------------------------- 1 | var Router = require('koa-router'); 2 | var router = new Router(); 3 | 4 | var p = require('../../../database/mysqlUtil'); 5 | var userService = require('../../../service/frame/userService'); 6 | 7 | router.get('/list', async (ctx) =>{ 8 | var q = ctx.query; 9 | var rows = await userService.getUsersPage(q.page,q.size); 10 | var count = await userService.getUsersCount(); 11 | 12 | ctx.body = {"rows":rows, 13 | "count":count}; 14 | }); 15 | 16 | router.post('/add', async (ctx) =>{ 17 | var user = ctx.request.body; 18 | await userService.addNew(user); 19 | ctx.body = { 20 | success : true, 21 | }; 22 | }); 23 | 24 | router.get('/delete', async (ctx) =>{ 25 | 26 | var q = ctx.query; 27 | var userId = q.userId; 28 | 29 | await userService.delete(userId); 30 | ctx.body = { 31 | success : true, 32 | }; 33 | }); 34 | 35 | router.post('/update', async (ctx) =>{ 36 | var user = ctx.request.body; 37 | await userService.update(user); 38 | ctx.body = { 39 | success : true, 40 | }; 41 | }); 42 | 43 | router.post('/configUserRole', async (ctx) =>{ 44 | var userRoleFormData = ctx.request.body; 45 | await userService.configUserRole(userRoleFormData); 46 | ctx.body = { 47 | success : true, 48 | }; 49 | }); 50 | 51 | router.get('/getUserRoles', async (ctx) =>{ 52 | var q = ctx.query; 53 | var rows = await userService.getUserRoles(q.userId); 54 | ctx.body = rows; 55 | }); 56 | 57 | router.get('/getUserMenus', async (ctx) =>{ 58 | var q = ctx.query; 59 | var rows = await userService.getUserMenus(q.userId); 60 | ctx.body = rows; 61 | }) 62 | 63 | module.exports = router; 64 | -------------------------------------------------------------------------------- /server/routers/api/index.js: -------------------------------------------------------------------------------- 1 | var config = require('../../config/config'); 2 | var tokenConfig = config.tokenConfig 3 | var jwt = require('koa-jwt'); 4 | 5 | const Router = require('koa-router'); 6 | 7 | const api_user = require("./frame/user"); 8 | const api_menu = require("./frame/menu");//菜单 9 | const api_role = require("./frame/role");//角色管理 10 | const api_auth = require("./frame/auth"); 11 | 12 | const api_media = require("./media"); 13 | 14 | const api_infoCategory = require('./info/infoCategory'); 15 | const api_info = require('./info/info'); 16 | 17 | const noAuthRouter = new Router(); 18 | 19 | const needAuthRouter = new Router(); 20 | 21 | module.exports = (app) => { 22 | noAuthRouter.use('/api/auth', api_auth.routes()); 23 | noAuthRouter.use('/api/media', api_media.routes()); 24 | 25 | needAuthRouter.use('/api/menu', api_menu.routes()); 26 | needAuthRouter.use('/api/role', api_role.routes()); 27 | needAuthRouter.use('/api/user', api_user.routes()); 28 | 29 | needAuthRouter.use('/api/infoCategory', api_infoCategory.routes()); 30 | needAuthRouter.use('/api/info', api_info.routes()); 31 | 32 | app.use(noAuthRouter.routes()); 33 | 34 | app.use(jwt({ secret: tokenConfig.JWT_SECRET })); 35 | 36 | app.use(needAuthRouter.routes()); 37 | }; -------------------------------------------------------------------------------- /server/routers/api/info/info.js: -------------------------------------------------------------------------------- 1 | var Router = require('koa-router'); 2 | var router = new Router(); 3 | 4 | var infoService = require('../../../service/info/infoService'); 5 | 6 | router.get('/list', async (ctx) =>{ 7 | var q = ctx.query; 8 | var rows = await infoService.getInfoPage(q.categoryId,q.page,q.size); 9 | var count = await infoService.getInfoCount(q.categoryId); 10 | 11 | ctx.body = {"rows":rows, 12 | "count":count}; 13 | }); 14 | 15 | router.get('/detail' ,async (ctx) => { 16 | var q = ctx.query; 17 | var info = await infoService.getInfoById(q.infoId); 18 | ctx.body = {"info":info}; 19 | }); 20 | 21 | router.post('/add', async (ctx) =>{ 22 | var info = ctx.request.body; 23 | await infoService.addNew(info); 24 | ctx.body = { 25 | success : true, 26 | }; 27 | }); 28 | 29 | router.get('/delete', async (ctx) =>{ 30 | var q = ctx.query; 31 | var id = q.id; 32 | await infoService.delete(id); 33 | ctx.body = { 34 | success : true, 35 | }; 36 | }); 37 | 38 | router.post('/update', async (ctx) =>{ 39 | var info = ctx.request.body; 40 | await infoService.update(info); 41 | ctx.body = { 42 | success : true, 43 | }; 44 | }); 45 | 46 | 47 | module.exports = router; 48 | -------------------------------------------------------------------------------- /server/routers/api/info/infoCategory.js: -------------------------------------------------------------------------------- 1 | var Router = require('koa-router'); 2 | var router = new Router(); 3 | 4 | var infoCategoryService = require('../../../service/info/infoCategoryService'); 5 | 6 | router.get('/list', async (ctx) => { 7 | var q = ctx.query; 8 | var rows = await infoCategoryService.getInfoCategoryPage(q.pCategoryId,q.page,q.size); 9 | var count = await infoCategoryService.getInfoCategorysCount(q.pCategoryId); 10 | 11 | ctx.body = {"rows":rows, 12 | "count":count}; 13 | }); 14 | 15 | router.get('/listByPid',async (ctx) =>{ 16 | var q = ctx.query; 17 | var rows = await infoCategoryService.getInfoCategoryByPid(q.pid); 18 | ctx.body = rows; 19 | }); 20 | 21 | router.get('/allList', async (ctx) => { 22 | var q = ctx.query; 23 | var rows = await infoCategoryService.getAllCategory(); 24 | ctx.body = rows; 25 | }); 26 | 27 | router.post('/add', async (ctx) =>{ 28 | var infoC = ctx.request.body; 29 | await infoCategoryService.addNew(infoC); 30 | ctx.body = { 31 | success : true, 32 | }; 33 | }); 34 | 35 | router.get('/delete', async (ctx) =>{ 36 | var q = ctx.query; 37 | var id = q.id; 38 | await infoCategoryService.delete(id); 39 | ctx.body = { 40 | success : true, 41 | }; 42 | }); 43 | 44 | router.post('/update', async (ctx) =>{ 45 | var infoc = ctx.request.body; 46 | await infoCategoryService.update(infoc); 47 | ctx.body = { 48 | success : true, 49 | }; 50 | }); 51 | 52 | 53 | module.exports = router; 54 | -------------------------------------------------------------------------------- /server/routers/api/media.js: -------------------------------------------------------------------------------- 1 | var Router = require('koa-router'); 2 | var router = new Router(); 3 | 4 | var parse = require('co-busboy'); 5 | var os = require('os'); 6 | var path = require('path'); 7 | var fs = require('fs'); 8 | 9 | var config = require('../../config/config') 10 | 11 | router.post('/upload', async (ctx) => { 12 | let files = ctx.request.body.files; 13 | let returnpath = ""; 14 | 15 | if (files) { 16 | Object.keys(files).forEach(key => { 17 | let file = files[key]; 18 | let path = file.path; 19 | returnpath = config.upload.downloadFilePrex + path.replace(config.upload.localFilePrex, '').replace(/\\/g, '/'); 20 | }); 21 | } 22 | ctx.body = { 23 | "status": "success", 24 | "url": returnpath 25 | }; 26 | }); 27 | 28 | router.post('/simditorUploadImage', async (ctx) => { 29 | let files = ctx.request.body.files; 30 | let returnpath = ""; 31 | 32 | if (files) { 33 | Object.keys(files).forEach(key => { 34 | let file = files[key]; 35 | let path = file.path; 36 | returnpath = config.upload.downloadFilePrex + path.replace(config.upload.localFilePrex, '').replace(/\\/g, '/'); 37 | }); 38 | } 39 | 40 | ctx.body = { 41 | "success": true, 42 | "msg": "", 43 | "file_path": returnpath 44 | }; 45 | }); 46 | 47 | router.post('/uploadFiles', async (ctx) => { 48 | 49 | let files = ctx.request.body.files; 50 | let returnpath = ""; 51 | 52 | if (files) { 53 | Object.keys(files).forEach(key => { 54 | let file = files[key]; 55 | let path = file.path; 56 | 57 | let fileUrl = config.upload.downloadFilePrex + path.replace(config.upload.localFilePrex, '').replace(/\\/g, '/'); 58 | 59 | if (returnpath == "") { 60 | returnpath += fileUrl; 61 | } else { 62 | returnpath += ";" + fileUrl; 63 | } 64 | }); 65 | } 66 | 67 | ctx.body = { 68 | status: "success", 69 | url: returnpath 70 | }; 71 | }); 72 | 73 | 74 | module.exports = router; -------------------------------------------------------------------------------- /server/routers/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (app) => { 2 | require('./api')(app); 3 | }; 4 | -------------------------------------------------------------------------------- /server/routers/mapi/info/minfo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by cl on 2016/10/9. 3 | */ 4 | var Router = require('koa-router'); 5 | var router = new Router({prefix: '/api/m/info'}); 6 | 7 | var infoService = require('../../../service/info/infoService'); 8 | 9 | router.get('/list', function* (next){ 10 | var q = this.request.query; 11 | var rows = yield infoService.getInfoPageByCategoryName(q.categoryName,q.page,q.size); 12 | 13 | yield this.body = {ret:200, 14 | data:rows,msg:""}; 15 | }); 16 | 17 | router.get('/detail' ,function * (next) { 18 | var q = this.request.query; 19 | var info = yield infoService.getInfoById(q.infoId); 20 | yield this.body = {ret:200, 21 | data:info,msg:""}; 22 | }); 23 | 24 | module.exports = router; 25 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | // require("babel-core/register"); 2 | // require("babel-polyfill"); 3 | // set babel in entry file 4 | 5 | 'use strict'; 6 | 7 | const http = require('http'); 8 | const json = require('koa-json'); 9 | const bodyParser = require('koa-bodyparser'); 10 | const staticDir = require('koa-static'); 11 | const body = require('koa-body'); 12 | const convert = require('koa-convert') 13 | const path = require('path'); 14 | const fs = require('fs'); 15 | const mkdirp = require('mkdirp'); 16 | 17 | const port = process.env.PORT || '8080' 18 | const Koa = require('koa'); 19 | const app = new Koa(); 20 | 21 | const server = http.createServer(app.callback()); 22 | 23 | var router = require("./routers"); 24 | 25 | const config = require('./config/config') 26 | 27 | app.use(json({pretty: false, param: 'pretty'})); 28 | 29 | app.use(staticDir(__dirname + '/../dist')); 30 | app.use(staticDir(__dirname + '/public')); 31 | 32 | app.use(body({ 33 | multipart: true, 34 | formidable: { 35 | multiples: true, 36 | uploadDir: config.upload.localFilePrex, 37 | maxFieldsSize: 1024 * 10, // max file size: 10m 38 | onFileBegin: (name, file) => { 39 | let now = new Date(); 40 | let today = path.join(now.getFullYear().toString(), (now.getMonth() + 1).toString(), now.getDay().toString()); 41 | let folder = path.join(config.upload.localFilePrex, today); 42 | let filename = now.getTime() + '__' + file.name; 43 | 44 | try { 45 | fs.accessSync(folder, fs.constants.W_OK); 46 | } catch (err) { 47 | mkdirp.sync(folder); 48 | } finally { 49 | file.path = path.join(folder, filename); 50 | } 51 | } 52 | } 53 | })); 54 | 55 | app.use(bodyParser()); 56 | 57 | router(app); 58 | 59 | server.listen(port, () => { 60 | console.log(`app started in ${port}`); 61 | }); 62 | -------------------------------------------------------------------------------- /server/service/frame/menuService.js: -------------------------------------------------------------------------------- 1 | var p = require('../../database/mysqlUtil'); 2 | 3 | var menuService = {}; 4 | 5 | menuService.getMenusPage = async (pmenuId,page,size) => { 6 | var sqlString = `select * from fmenu where 1=1 `; 7 | if (pmenuId != undefined && pmenuId != '') { 8 | sqlString += ` and pmenuId = ${pmenuId} `; 9 | }; 10 | 11 | var pageInt = parseInt(page); 12 | var sizeInt = parseInt(size); 13 | 14 | var start = (page-1)*size; 15 | 16 | sqlString += ' order by orderNum '; 17 | 18 | sqlString += ' limit ' +start +','+size; 19 | 20 | var rows = await p.query(sqlString); 21 | return rows; 22 | } 23 | 24 | menuService.getMenusCount = async (pmenuId) => { 25 | var sqlString = `select count(1) as cnt from fmenu where 1=1 `; 26 | if (pmenuId != undefined && pmenuId != '') { 27 | sqlString += ` and pmenuId = ${pmenuId} `; 28 | }; 29 | 30 | var count = await p.query(sqlString); 31 | return count[0].cnt; 32 | } 33 | 34 | menuService.getMenuByPid = async (pmenuId) => { 35 | var sqlString = `select * from fmenu where 1=1 `; 36 | if (pmenuId != undefined && pmenuId != '') { 37 | sqlString += ` and pmenuId=${pmenuId} `; 38 | }; 39 | var rows = await p.query(sqlString); 40 | return rows; 41 | } 42 | 43 | menuService.getAllMenu = async () => { 44 | var sqlString = `select * from fmenu where 1=1 order by orderNum`; 45 | var rows = await p.query(sqlString); 46 | return rows; 47 | } 48 | 49 | menuService.delete = async (menuId) => { 50 | await p.query(`delete from fmenu where menuId = ${menuId} `); 51 | } 52 | 53 | menuService.addNew = async (data) => { 54 | var sqlString = `insert into fmenu (name,menuKey,tourl,orderNum,pmenuId,tag) 55 | value("${data.name}","${data.menuKey}","${data.tourl}",${data.orderNum},${data.pmenuId},"${data.tag}")`; 56 | await p.query(sqlString); 57 | } 58 | 59 | menuService.update = async (data) => { 60 | await p.query(`update fmenu set name = "${data.name}", 61 | menuKey = "${data.menuKey}", orderNum = ${data.orderNum} , 62 | pmenuId = ${data.pmenuId} , 63 | tourl = "${data.tourl}", 64 | tag = "${data.tag}" where menuId = ${data.menuId}`); 65 | } 66 | 67 | module.exports = menuService; -------------------------------------------------------------------------------- /server/service/frame/roleService.js: -------------------------------------------------------------------------------- 1 | var p = require('../../database/mysqlUtil'); 2 | 3 | var roleService = {}; 4 | 5 | roleService.getRolesPage = async (proleId,page,size) => { 6 | var sqlString = `select * from frole where 1=1 `; 7 | if (proleId != undefined && proleId != '') { 8 | sqlString += ` and proleId = ${proleId} `; 9 | }; 10 | 11 | var pageInt = parseInt(page); 12 | var sizeInt = parseInt(size); 13 | 14 | var start = (page-1)*size; 15 | 16 | sqlString += ' limit ' +start +','+size; 17 | 18 | var rows = await p.query(sqlString); 19 | return rows; 20 | } 21 | 22 | roleService.getAllRoles = async () => { 23 | var sqlString = `select * from frole where 1=1 `; 24 | var rows = await p.query(sqlString); 25 | return rows; 26 | } 27 | 28 | roleService.getRolesCount = async (proleId) => { 29 | var sqlString = `select count(1) as cnt from frole where 1=1 `; 30 | if (proleId != undefined && proleId != '') { 31 | sqlString += ` and proleId = ${proleId} `; 32 | }; 33 | 34 | var count = await p.query(sqlString); 35 | return count[0].cnt; 36 | } 37 | 38 | roleService.getRoleByPid = async (proleId) => { 39 | var sqlString = `select * from frole where 1=1 `; 40 | if (proleId != undefined && proleId != '') { 41 | sqlString += ` and proleId=${proleId} `; 42 | }; 43 | var rows = await p.query(sqlString); 44 | return rows; 45 | } 46 | 47 | roleService.delete = async (roleId) => { 48 | await p.query(`delete from frole where roleId = ${roleId} `); 49 | } 50 | 51 | roleService.addNew = async (data) => { 52 | var sqlString = `insert into frole (name,proleId) 53 | value("${data.name}",${data.proleId})`; 54 | await p.query(sqlString); 55 | } 56 | 57 | roleService.update = async (data) => { 58 | await p.query(`update frole set name = "${data.name}",proleId = ${data.proleId} where roleId = ${data.roleId}`); 59 | } 60 | 61 | roleService.configRoleMenu = async (data) => { 62 | var menus = data.menus ; 63 | await p.query(`delete from froleMenu where roleId = ${data.roleId} `); 64 | if (menus!=undefined && menus != ""){ 65 | var menuArray = menus.split(","); 66 | for(var i = 0 ; i< menuArray.length ; i ++){ 67 | var menuId = menuArray[i]; 68 | if (parseInt(menuId) != -1 ) { 69 | var insertsql = `INSERT INTO froleMenu(menuId, roleId) SELECT ${menuId}, ${data.roleId} FROM DUAL WHERE NOT EXISTS(SELECT * FROM froleMenu WHERE menuId =${menuId} and roleId =${data.roleId} )`; 70 | await p.query(insertsql); 71 | } 72 | } 73 | } 74 | } 75 | 76 | roleService.getRoleMenus = async (roleId) => { 77 | var sqlString = `select menuId from froleMenu where 1=1 `; 78 | if (roleId != undefined && roleId != '') { 79 | sqlString += ` and roleId=${roleId} `; 80 | }; 81 | var rows = await p.query(sqlString); 82 | return rows; 83 | } 84 | 85 | module.exports = roleService; 86 | -------------------------------------------------------------------------------- /server/service/frame/userService.js: -------------------------------------------------------------------------------- 1 | var p = require('../../database/mysqlUtil'); 2 | 3 | var userService = {}; 4 | 5 | userService.getUsersPage = async (page,size) => { 6 | var page = parseInt(page); 7 | var size = parseInt(size); 8 | 9 | var start = (page-1)*size; 10 | var end = page*size; 11 | 12 | var rows = await p.query('select * from fuser limit ' +start +','+end); 13 | 14 | return rows; 15 | } 16 | 17 | userService.getUserByUserName = async (username) => { 18 | var rows = await p.query('select * from fuser where loginName ="'+username+'"'); 19 | if (rows.length>0){ 20 | return rows[0]; 21 | } 22 | return undefined; 23 | } 24 | 25 | userService.delete = async (userId) => { 26 | await p.query(`delete from fuser where userId = ${userId} `); 27 | } 28 | 29 | userService.addNew = async (data) => { 30 | var sqlString = `insert into fuser (loginName,loginPasw,name) 31 | value("${data.loginName}","${data.loginPasw}","${data.name}")`; 32 | await p.query(sqlString); 33 | } 34 | 35 | userService.update = async (data) => { 36 | await p.query(`update fuser set loginName = "${data.loginName}", 37 | loginPasw = "${data.loginPasw}", 38 | name = "${data.name}" where userId = ${data.userId}`); 39 | } 40 | 41 | userService.getUsersCount = async () => { 42 | var sqlString = `select count(1) as cnt from fuser `; 43 | var count = await p.query(sqlString); 44 | return count[0].cnt; 45 | } 46 | 47 | userService.configUserRole = async (data) => { 48 | var roles = data.roles ; 49 | await p.query(`delete from fuserrole where userId = ${data.userId} `); 50 | if (roles!=undefined && roles != ""){ 51 | var roleArray = roles.split(","); 52 | for(var i = 0 ; i< roleArray.length ; i ++){ 53 | var roleId = roleArray[i]; 54 | if (parseInt(roleId) != -1 ) { 55 | var insertsql = `INSERT INTO fuserrole(userId, roleId) SELECT ${data.userId}, ${roleId} FROM DUAL WHERE NOT EXISTS(SELECT * FROM fuserrole WHERE userId =${data.userId} and roleId =${roleId} )`; 56 | await p.query(insertsql); 57 | } 58 | } 59 | } 60 | } 61 | 62 | userService.getUserRoles = async (userId) => { 63 | var sqlString = `select roleId from fuserrole where 1=1 `; 64 | if (userId != undefined && userId != '') { 65 | sqlString += ` and userId=${userId} `; 66 | }; 67 | var rows = await p.query(sqlString); 68 | return rows; 69 | } 70 | 71 | userService.getUserMenus = async (userId) => { 72 | var sqlString = `select distinct m.* from fmenu m inner join frolemenu rm on m.menuId = rm.menuId where rm.roleId in 73 | (select r.roleId from frole r inner join fuserrole ur on r.roleId = ur.roleId where ur.userId = ${userId}) order by m.orderNum `; 74 | var rows = await p.query(sqlString); 75 | return rows; 76 | } 77 | 78 | module.exports = userService; 79 | -------------------------------------------------------------------------------- /server/service/info/infoCategoryService.js: -------------------------------------------------------------------------------- 1 | var p = require('../../database/mysqlUtil'); 2 | 3 | var infoCategoryService = {}; 4 | 5 | infoCategoryService.getInfoCategoryPage = async (pCategoryId,page,size) => { 6 | var sqlString = `select * from finfocategory where 1=1 `; 7 | if (pCategoryId != undefined && pCategoryId != '') { 8 | sqlString += ` and pCategoryId = ${pCategoryId} `; 9 | }; 10 | 11 | var pageInt = parseInt(page); 12 | var sizeInt = parseInt(size); 13 | 14 | var start = (page-1)*size; 15 | 16 | sqlString += ' order by orderNum '; 17 | 18 | sqlString += ' limit ' +start +','+size; 19 | 20 | var rows = await p.query(sqlString); 21 | return rows; 22 | } 23 | 24 | infoCategoryService.getInfoCategorysCount = async (pCategoryId) => { 25 | var sqlString = `select count(1) as cnt from finfocategory where 1=1 `; 26 | if (pCategoryId != undefined && pCategoryId != '') { 27 | sqlString += ` and pCategoryId = ${pCategoryId} `; 28 | }; 29 | 30 | var count = await p.query(sqlString); 31 | return count[0].cnt; 32 | } 33 | 34 | infoCategoryService.getInfoCategoryByPid = async (pCategoryId) => { 35 | var sqlString = `select * from finfocategory where 1=1 `; 36 | if (pCategoryId != undefined && pCategoryId != '') { 37 | sqlString += ` and pCategoryId=${pCategoryId} `; 38 | }; 39 | var rows = await p.query(sqlString); 40 | return rows; 41 | } 42 | 43 | infoCategoryService.getAllCategory = async () => { 44 | var sqlString = `select * from finfocategory where 1=1 order by orderNum`; 45 | var rows = await p.query(sqlString); 46 | return rows; 47 | } 48 | 49 | infoCategoryService.delete = async (categoryId) => { 50 | await p.query(`delete from finfocategory where categoryId = ${categoryId} `); 51 | } 52 | 53 | infoCategoryService.addNew = async (data) => { 54 | var sqlString = `insert into finfocategory (categoryName,orderNum,pCategoryId) 55 | value("${data.categoryName}",${data.orderNum},${data.pCategoryId})`; 56 | await p.query(sqlString); 57 | } 58 | 59 | infoCategoryService.update = async (data) => { 60 | await p.query(`update finfocategory set categoryName = "${data.categoryName}", 61 | orderNum = ${data.orderNum}, pCategoryId = ${data.pCategoryId} where categoryId = ${data.categoryId} `); 62 | } 63 | 64 | module.exports = infoCategoryService; -------------------------------------------------------------------------------- /server/service/info/infoService.js: -------------------------------------------------------------------------------- 1 | var p = require('../../database/mysqlUtil'); 2 | 3 | var infoService = {}; 4 | 5 | infoService.getInfoPage = async (categoryId,page,size) => { 6 | var sqlString = `select infoId,createDate,infoDes,categoryName,topic,url,city,categoryId from finfo where 1=1 `; 7 | if (categoryId != undefined && categoryId != '') { 8 | sqlString += ` and categoryId = ${categoryId} `; 9 | }; 10 | 11 | var pageInt = parseInt(page); 12 | var sizeInt = parseInt(size); 13 | 14 | var start = (page-1)*size; 15 | 16 | sqlString += ' order by createDate desc'; 17 | 18 | sqlString += ' limit ' +start +','+size; 19 | 20 | var rows = await p.query(sqlString); 21 | return rows; 22 | } 23 | 24 | infoService.getInfoPageByCategoryName = async (categoryName,page,size) => { 25 | var sqlString = `select infoId,createDate,updateDate,categoryName,topic,url,city,categoryId,headImage from finfo where 1=1 `; 26 | if (categoryName != undefined && categoryName != '') { 27 | sqlString += ` and categoryName = '${categoryName}' `; 28 | }; 29 | 30 | var pageInt = parseInt(page); 31 | var sizeInt = parseInt(size); 32 | 33 | var start = (page-1)*size; 34 | 35 | sqlString += ' order by createDate desc'; 36 | 37 | sqlString += ' limit ' +start +','+size; 38 | 39 | var rows = await p.query(sqlString); 40 | return rows; 41 | } 42 | 43 | infoService.getInfoCount = async (categoryId) => { 44 | var sqlString = `select count(1) as cnt from finfo where 1=1 `; 45 | if (categoryId != undefined && categoryId != '') { 46 | sqlString += ` and categoryId = ${categoryId} `; 47 | }; 48 | 49 | var count = await p.query(sqlString); 50 | return count[0].cnt; 51 | } 52 | 53 | infoService.getInfoByCid = async (categoryId) => { 54 | var sqlString = `select * from finfo where 1=1 `; 55 | if (categoryId != undefined && categoryId != '') { 56 | sqlString += ` and categoryId=${categoryId} `; 57 | }; 58 | var rows = await p.query(sqlString); 59 | return rows; 60 | } 61 | 62 | infoService.getInfoById = async (infoId) => { 63 | var sqlString = `select * from finfo where 1=1 `; 64 | if (infoId != undefined && infoId != '') { 65 | sqlString += ` and infoId=${infoId} `; 66 | }; 67 | var rows = await p.query(sqlString); 68 | if (rows.length>0){ 69 | return rows[0]; 70 | } 71 | return undefined; 72 | } 73 | 74 | infoService.delete = async (infoId) => { 75 | await p.query(`delete from finfo where infoId = ${infoId} `); 76 | } 77 | 78 | infoService.addNew = async (data) => { 79 | var sqlString = `insert into finfo (createDate,updateDate,categoryName,topic,content,url,headImage,infoDes,city,categoryId) 80 | value(now(),now(),'${data.categoryName}','${data.topic}','${data.content}','${data.url}','${data.headImage}','${data.infoDes}','${data.city}',${data.categoryId})`; 81 | await p.query(sqlString); 82 | } 83 | 84 | infoService.update = async (data) => { 85 | await p.query(`update finfo set categoryName = "${data.categoryName}", 86 | topic = '${data.topic}', content = '${data.content}', url = '${data.url}', headImage = '${data.headImage}' 87 | , infoDes = '${data.infoDes}', city = '${data.city}', categoryId = ${data.categoryId} where infoId=${data.infoId}` ); 88 | } 89 | 90 | module.exports = infoService; --------------------------------------------------------------------------------