diff --git a/.roadhogrc.js b/.roadhogrc.js new file mode 100644 index 0000000..e2f79c0 --- /dev/null +++ b/.roadhogrc.js @@ -0,0 +1,26 @@ +const path = require('path') + +const svgSpriteDirs = [ + path.resolve(__dirname, 'src/svg/'), +] + +export default { + entry: 'src/index.js', + svgSpriteLoaderDirs: svgSpriteDirs, + "theme": "src/theme.js", + "env": { + "development": { + "extraBabelPlugins": [ + "dva-hmr", + "transform-runtime", + ["import", { "libraryName": "antd", "style": true }] + ] + }, + "production": { + "extraBabelPlugins": [ + "transform-runtime", + ["import", { "libraryName": "antd", "style": true}] + ] + } + } +} diff --git a/.roadhogrc.mock.js b/.roadhogrc.mock.js new file mode 100644 index 0000000..fc068f0 --- /dev/null +++ b/.roadhogrc.mock.js @@ -0,0 +1,5 @@ +const mock = {} +require('fs').readdirSync(require('path').join(__dirname + '/src/mock')).forEach(function(file) { + Object.assign(mock, require('./src/mock/' + file)) +}) +module.exports = mock diff --git a/README.md b/README.md index 22871f1..2f132c5 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ - [x] 规范代码: EsLint; 自用[开发配置](assets/standard.md) - [ ] 数据可视化页面 - [ ] 模拟消息收发 -- [ ] 升级dva-cli +- [ ] 升级为roadhog ## 开发及构建 diff --git a/mock/app.js b/mock/app.js deleted file mode 100644 index 4f413f7..0000000 --- a/mock/app.js +++ /dev/null @@ -1,74 +0,0 @@ -const Cookie = require('js-cookie') -import mockStorge from '../src/utils/mockStorge' - -const userPermission = { - DEFAULT: ['dashboard', 'chart'], - ADMIN: ['dashboard', 'users', 'UIElement', 'UIElementIconfont', 'chart'], - DEVELOPER: ['dashboard', 'users', 'UIElement', 'UIElementIconfont', 'chart'], -} - -let dataKey = mockStorge('AdminUsers', [ - { - username: 'admin', - password: 'admin', - permissions: userPermission.ADMIN, - }, - { - username: 'guest', - password: 'guest', - permissions: userPermission.DEFAULT, - }, - { - username: '吴彦祖', - password: '123456', - permissions: userPermission.DEVELOPER, - }, -]) - -module.exports = { - 'POST /api/login' (req, res) { - const userItem = JSON.parse(req.body) - const response = { - success: false, - message: '', - } - const currentUser = global[dataKey].filter((item) => { - return item.username === userItem.username - }) - if (currentUser.length) { - if (currentUser[0].password === userItem.password) { - const now = new Date() - now.setDate(now.getDate() + 1) - Cookie.set('user_session', now.getTime(), { path: '/' }) - Cookie.set('user_name', userItem.username, { path: '/' }) - response.message = '登录成功' - response.success = true - response.userPermissions = global[dataKey].filter(_ => _.username === userItem.username)[0].permissions - } else { - response.message = '密码不正确' - } - } else { - response.message = '用户不存在' - } - res.json(response) - }, - - 'GET /api/userInfo' (req, res) { - const response = { - success: Cookie.get('user_session') && Cookie.get('user_session') > new Date().getTime(), - username: Cookie.get('user_name') || '', - message: '', - userPermissions: global[dataKey].filter(_ => _.username === Cookie.get('user_name'))[0].permissions, - } - res.json(response) - }, - - 'POST /api/logout' (req, res) { - Cookie.remove('user_session', { path: '/' }) - Cookie.remove('user_name', { path: '/' }) - res.json({ - success: true, - message: '退出成功', - }) - }, -} diff --git a/package.json b/package.json index 770f9cc..9550420 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,10 @@ "devDependencies": { "atool-build": "^0.7.6", "axios": "^0.15.3", - "babel-eslint": "^6.0.4", + "babel-eslint": "^6.1.2", "babel-plugin-dev-expression": "^0.2.1", - "babel-plugin-dva-hmr": "^0.1.0", - "babel-plugin-import": "^1.0.1", + "babel-plugin-dva-hmr": "^0.3.2", + "babel-plugin-import": "^1.1.1", "babel-plugin-transform-runtime": "^6.9.0", "babel-polyfill": "^6.23.0", "babel-runtime": "^6.9.2", @@ -41,17 +41,19 @@ "eslint-plugin-jsx-a11y": "^1.4.2", "eslint-plugin-react": "^5.1.1", "glob": "^7.0.5", + "jsonp": "^0.2.1", + "less-vars-to-js": "^1.1.2", "mockjs": "^1.0.1-beta3", "rc-tween-one": "^1.0.0", "redbox-react": "^1.2.10", + "roadhog": "^0.6.0-beta1", "watchjs": "^0.0.0" }, "pre-commit": [ "lint" ], "scripts": { - "start": "dora --plugins \"proxy,webpack?publicPath=/,webpack-hmr,browser-history?index=/src/index.html\"", - "dev": "dora --plugins \"webpack?publicPath=/,webpack-hmr,browser-history?index=/src/index.html\"", + "dev": "set BROWSER=none&&roadhog server", "lint": "eslint --fix --ext .js src", "build": "atool-build" }, diff --git a/proxy.config.js b/proxy.config.js deleted file mode 100644 index 569b217..0000000 --- a/proxy.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const mock = {} - -require('fs').readdirSync(require('path').join(`${__dirname}/mock`)).forEach((file) => { - Object.assign(mock, require(`./mock/${file}`)) -}) - -module.exports = mock diff --git a/src/components/vars.less b/src/components/vars.less index db29419..407bec2 100644 --- a/src/components/vars.less +++ b/src/components/vars.less @@ -1,185 +1,13 @@ -@import "../../node_modules/antd/lib/style/themes/default.less"; // // Prefix -// @ant-prefix : ant; -// -// // Color -// @primary-color : #2db7f5; -// @info-color : #2db7f5; -// @success-color : #87d068; -// @error-color : #f50; -// @warning-color : #fa0; -// @normal-color : #d9d9d9; -// -// // ------ Base & Require ------ -// @body-background : #fff; -// @font-family : -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif; -// @code-family : Consolas,Menlo,Courier,monospace; -// @text-color : #666; -// @heading-color : #404040; -// @font-size-base : 12px; -// @line-height-base : 1.5; -// @line-height-computed : floor((@font-size-base * @line-height-base)); -// @border-radius-base : 6px; -// @border-radius-sm : 4px; -// -// // ICONFONT -// @iconfont-css-prefix : anticon; -// @icon-url : "https://at.alicdn.com/t/font_1473840929_824008"; -// -// // LINK -// @link-color : #2db7f5; -// @link-hover-color : tint(@link-color, 20%); -// @link-active-color : shade(@link-color, 5%); -// @link-hover-decoration : none; -// -// // Disabled cursor for form controls and buttons. -// @cursor-disabled : not-allowed; -// -// // Animation -// @ease-out : cubic-bezier(0.215, 0.61, 0.355, 1); -// @ease-in : cubic-bezier(0.55, 0.055, 0.675, 0.19); -// @ease-in-out : cubic-bezier(0.645, 0.045, 0.355, 1); -// @ease-out-back : cubic-bezier(0.12, 0.4, 0.29, 1.46); -// @ease-in-back : cubic-bezier(0.71, -0.46, 0.88, 0.6); -// @ease-in-out-back : cubic-bezier(0.71, -0.46, 0.29, 1.46); -// @ease-out-circ : cubic-bezier(0.08, 0.82, 0.17, 1); -// @ease-in-circ : cubic-bezier(0.6, 0.04, 0.98, 0.34); -// @ease-in-out-circ : cubic-bezier(0.78, 0.14, 0.15, 0.86); -// @ease-out-quint : cubic-bezier(0.23, 1, 0.32, 1); -// @ease-in-quint : cubic-bezier(0.755, 0.05, 0.855, 0.06); -// @ease-in-out-quint : cubic-bezier(0.86, 0, 0.07, 1); -// -// // Border color -// @border-color-base : #d9d9d9; // base border outline a component -// @border-color-split : #e9e9e9; // split border inside a component -// -// // Outline -// @outline-blur-size : 0; -// @outline-width : 2px; -// @outline-color : @primary-color; -// -// // Background color -// @background-color-base : #f7f7f7; // basic gray background -// -// // Shadow -// @shadow-color : rgba(100, 100, 100, .2); -// @box-shadow-base : @shadow-1-down; -// @shadow-1-up : 0 -1px 6px @shadow-color; -// @shadow-1-down : 0 1px 6px @shadow-color; -// @shadow-1-left : -1px 0 6px @shadow-color; -// @shadow-1-right : 1px 0 6px @shadow-color; -// @shadow-2 : 0 1px 8px @shadow-color; -// -// // Buttons -// @btn-font-weight : 500; -// @btn-border-radius-base : @border-radius-base; -// @btn-border-radius-sm : @border-radius-sm; -// -// @btn-primary-color : #fff; -// @btn-primary-bg : @primary-color; -// @btn-group-border : shade(@primary-color, 5%); -// -// @btn-default-color : @text-color; -// @btn-default-bg : @background-color-base; -// @btn-default-border : @border-color-base; -// -// @btn-ghost-color : @text-color; -// @btn-ghost-bg : transparent; -// @btn-ghost-border : @border-color-base; -// -// @btn-disable-color : #ccc; -// @btn-disable-bg : @background-color-base; -// @btn-disable-border : @border-color-base; -// -// @btn-padding-base : 4px 15px; -// -// @btn-font-size-lg : 14px; -// @btn-padding-lg : 4px 15px 5px 15px; -// -// @btn-padding-sm : 1px 7px; -// -// @btn-circle-size : 28px; -// @btn-circle-size-lg : 32px; -// @btn-circle-size-sm : 22px; -// -// // Media queries breakpoints -// // Extra small screen / phone -// @screen-xs : 480px; -// @screen-xs-min : @screen-xs; -// @screen-xs-max : (@screen-xs-min - 1); -// -// // Small screen / tablet -// @screen-sm : 768px; -// @screen-sm-min : @screen-sm; -// @screen-sm-max : (@screen-sm-min - 1); -// -// // Medium screen / desktop -// @screen-md : 992px; -// @screen-md-min : @screen-md; -// @screen-md-max : (@screen-md-min - 1); -// -// // Large screen / wide desktop -// @screen-lg : 1200px; -// @screen-lg-min : @screen-lg; -// @screen-lg-max : (@screen-lg-min - 1); -// -// // Layout and Grid system -// @grid-columns : 24; -// @grid-gutter-width : 0; -// -// // Container sizes -// @container-sm : (720px + @grid-gutter-width); -// @container-md : (940px + @grid-gutter-width); -// @container-lg : (1140px + @grid-gutter-width); -// -// // z-index list -// @zindex-affix : 10; -// @zindex-back-top : 10; -// @zindex-modal-mask : 1000; -// @zindex-modal : 1000; -// @zindex-notification : 1010; -// @zindex-message : 1010; -// @zindex-popover : 1030; -// @zindex-picker : 1050; -// @zindex-dropdown : 1050; -// @zindex-tooltip : 1060; -// -// // Form -// // -------------------------------- -// // Legend -// @legend-color : #999; -// @legend-border-color : @border-color-base; -// // Label -// @label-required-color : #f50; -// @label-color : @text-color; -// // Input -// @input-height-base: 28px; -// @input-height-lg: 32px; -// @input-height-sm: 22px; -// -// @input-padding-horizontal : 7px; -// @input-padding-vertical-base : 4px; -// @input-padding-vertical-sm : 1px; -// @input-padding-vertical-lg : 6px; -// -// @input-placeholder-color : #ccc; -// @input-color : @text-color; -// @input-border-color : @border-color-base; -// @input-bg : #fff; -// -// @input-hover-border-color : @primary-color; -// @input-disabled-bg : @background-color-base; -// -// @form-item-margin-bottom : 24px; +@import '~antd/lib/style/themes/default.less'; // https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less /*========================*/ /*=======Antd Admin=======*/ /*========================*/ -//Color @dark-half: #494949; -@purple: #d897eb; // Shadow +@purple: #d897eb; @shadow-1: 4px 4px 20px 0 rgba(0,0,0,0.01); -@shadow-2: 4px 4px 40px 0 rgba(0,0,0,0.05); //transition +@shadow-2: 4px 4px 40px 0 rgba(0,0,0,0.05); @transition-ease-in: all 0.3s @ease-in; -@transition-ease-out: all 0.3s @ease-out; //mix +@transition-ease-out: all 0.3s @ease-out; .text-overflow() { overflow: hidden; diff --git a/src/mock/app.js b/src/mock/app.js new file mode 100644 index 0000000..2e40bbb --- /dev/null +++ b/src/mock/app.js @@ -0,0 +1,86 @@ +const userPermission = { + DEFAULT: ['dashboard', 'chart'], + ADMIN: ['dashboard', 'users', 'UIElement', 'UIElementIconfont', 'chart'], + DEVELOPER: ['dashboard', 'users', 'UIElement', 'UIElementIconfont', 'chart'], +} + +const adminUsers = [ + { + username: 'admin', + password: 'admin', + permissions: userPermission.ADMIN, + }, + { + username: 'guest', + password: 'guest', + permissions: userPermission.DEFAULT, + }, + { + username: '吴彦祖', + password: '123456', + permissions: userPermission.DEVELOPER, + }, +] + +module.exports = { + 'POST /api/login' (req, res) { + console.log(req.query) + console.log(req.params) + console.log(req.body) + // console.log(req.baseUrl) + // console.log(req.cookies) + // console.log(req.hostname) + // console.log(req.fresh) + // const userItem = JSON.parse(req.body) + // const response = { + // success: false, + // message: '', + // } + // const currentUser = AdminUsers.filter((item) => { + // return item.username === userItem.username + // }) + // if (currentUser.length) { + // if (currentUser[0].password === userItem.password) { + // const now = new Date() + // now.setDate(now.getDate() + 1) + // // Cookie.set('user_session', now.getTime(), { path: '/' }) + // // Cookie.set('user_name', userItem.username, { path: '/' }) + // response.message = '登录成功' + // response.success = true + // response.userPermissions = AdminUsers.filter(_ => _.username === userItem.username)[0].permissions + // } else { + // response.message = '密码不正确' + // } + // } else { + // response.message = '用户不存在' + // } + // res.json(req) + // res.end({ success: true }) + const now = new Date() + now.setDate(now.getDate() + 1) + res.cookie('user_session', now.getTime(), { maxAge: 900000, httpOnly: true }) + res.json({ + success: true, + message: '登录', + }) + }, + + 'GET /api/userInfo' (req, res) { + const response = { + // success: Cookie.get('user_session') && Cookie.get('user_session') > new Date().getTime(), + // username: Cookie.get('user_name') || '', + message: '', + } + if (response.success) { + // response.userPermissions = AdminUsers.filter(_ => _.username === Cookie.get('user_name'))[0].permissions + } + res.json(response) + }, + + 'POST /api/logout' (req, res) { + res.json({ + success: true, + message: '退出成功', + }) + }, +} diff --git a/mock/dashboard.js b/src/mock/dashboard.js similarity index 93% rename from mock/dashboard.js rename to src/mock/dashboard.js index 489529c..bab721a 100644 --- a/mock/dashboard.js +++ b/src/mock/dashboard.js @@ -1,8 +1,7 @@ -import { color } from '../src/utils/theme' +import { color } from '../utils/theme' const Mock = require('mockjs') -import mockStorge from '../src/utils/mockStorge' -let dataKey = mockStorge('Dashboard', Mock.mock({ +const Dashboard = Mock.mock({ 'sales|8': [ { 'name|+1': 2008, @@ -120,10 +119,10 @@ let dataKey = mockStorge('Dashboard', Mock.mock({ number: 4324, }, ], -})) +}) module.exports = { 'GET /api/dashboard' (req, res) { - res.json(global[dataKey]) + res.json(Dashboard) }, } diff --git a/mock/users.js b/src/mock/users.js similarity index 91% rename from mock/users.js rename to src/mock/users.js index eecdebf..b0fbf62 100644 --- a/mock/users.js +++ b/src/mock/users.js @@ -1,8 +1,7 @@ const qs = require('qs') const Mock = require('mockjs') -import mockStorge from '../src/utils/mockStorge' -let dataKey = mockStorge('UsersList', Mock.mock({ +let antdAdminUsersList = Mock.mock({ 'data|100': [ { 'id|+1': 1, @@ -23,13 +22,15 @@ let dataKey = mockStorge('UsersList', Mock.mock({ total: 100, current: 1, }, -})) +}) -let usersListData = global[dataKey] + +let usersListData = antdAdminUsersList module.exports = { 'GET /api/users' (req, res) { + console.log(req.query) const page = qs.parse(req.query) const pageSize = page.pageSize || 10 const currentPage = page.page || 1 @@ -69,7 +70,7 @@ module.exports = { usersListData.page.total = usersListData.data.length usersListData.page.current = 1 - global[dataKey] = usersListData + antdAdminUsersList = usersListData res.json({ success: true, data: usersListData.data, page: usersListData.page }) }, @@ -86,7 +87,7 @@ module.exports = { usersListData.page.total = usersListData.data.length - global[dataKey] = usersListData + antdAdminUsersList = usersListData res.json({ success: true, data: usersListData.data, page: usersListData.page }) }, @@ -104,7 +105,7 @@ module.exports = { return item }) - global[dataKey] = usersListData + antdAdminUsersList = usersListData res.json({ success: true, data: usersListData.data, page: usersListData.page }) }, diff --git a/src/utils/config.js b/src/utils/config.js index 66cb426..f2f023b 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -10,4 +10,6 @@ module.exports = { crossDomains: [ 'http://www.zuimeitianqi.com', ], + YQL: ['http://www.zuimeitianqi.com'], + CORS: ['http://localhost:7001'], } diff --git a/src/utils/index.js b/src/utils/index.js index 549ff48..7e96b25 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -3,7 +3,7 @@ import menu from './menu' import request from './request' import classnames from 'classnames' import { color } from './theme' -require('./mock.js') +// require('./mock.js') // 连字符转驼峰 String.prototype.hyphenToHump = function () { diff --git a/src/utils/mockStorge.js b/src/utils/mockStorge.js index 62bb98d..68527b7 100644 --- a/src/utils/mockStorge.js +++ b/src/utils/mockStorge.js @@ -1,5 +1,5 @@ const Watch = require('watchjs') -import config from './config' +const config = require('./config') export default function mockStorge (name, defaultValue) { let key = config.prefix + name diff --git a/src/utils/request.js b/src/utils/request.js index 032b6ee..01a824d 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -1,16 +1,38 @@ import axios from 'axios' import qs from 'qs' -import config from './config' +import { YQL, CORS } from './config' +import jsonp from 'jsonp' +import lodash from 'lodash' const fetch = (options) => { - const { + let { method = 'get', data, + fetchType, url, } = options + + if (fetchType === 'JSONP') { + return new Promise((resolve, reject) => { + jsonp(url, { + param: `${qs.stringify(data)}&callback`, + name: `jsonp_${new Date().getTime()}`, + timeout: 4000, + }, (error, result) => { + if (error) { + reject(error) + } + resolve({ statusText: 'OK', status: 200, data: result }) + }) + }) + } else if (fetchType === 'YQL') { + url = `http://query.yahooapis.com/v1/public/yql?q=select * from json where url='${options.url}?${qs.stringify(options.data)}'&format=json` + data = null + } + switch (method.toLowerCase()) { case 'get': - return axios.get(`${url}${options.data ? `?${qs.stringify(options.data)}` : ''}`) + return axios.get(`${url}${!lodash.isEmpty(data) ? `?${qs.stringify(data)}` : ''}`) case 'delete': return axios.delete(url, { data }) case 'head': @@ -27,17 +49,22 @@ const fetch = (options) => { } export default function request (options) { - if (options.url.indexOf('//') > -1) { - if (config.crossDomains && config.crossDomains.indexOf(`${options.url.split('//')[0]}//${options.url.split('//')[1].split('/')[0]}`) > -1) { - options.isCross = true - options.url = `http://query.yahooapis.com/v1/public/yql?q=select * from json where url='${options.url}?${qs.stringify(options.data)}'&format=json` - delete options.data + if (options.url && options.url.indexOf('//') > -1) { + const origin = `${options.url.split('//')[0]}//${options.url.split('//')[1].split('/')[0]}` + if (window.location.origin !== origin) { + if (CORS && CORS.indexOf(origin) > -1) { + options.fetchType = 'CORS' + } else if (YQL && YQL.indexOf(origin) > -1) { + options.fetchType = 'YQL' + } else { + options.fetchType = 'JSONP' + } } } return fetch(options).then((response) => { const { statusText, status } = response - let data = options.isCross ? response.data.query.results.json : response.data + let data = options.fetchType === 'YQL' ? response.data.query.results.json : response.data return { code: 0, status, @@ -45,7 +72,11 @@ export default function request (options) { ...data, } }).catch((error) => { - const { response = { statusText: 'Network Error' } } = error + const { + response = { + statusText: error.message || 'Network Error', + }, + } = error return { code: 1, message: response.statusText } }) } diff --git a/theme.config.js b/theme.config.js new file mode 100644 index 0000000..0bf2003 --- /dev/null +++ b/theme.config.js @@ -0,0 +1,8 @@ +const fs = require('fs') +const path = require('path') +const lessToJs = require('less-vars-to-js') + +module.exports = () => { + const themePath = path.join(__dirname, './src/components/vars.less') + return lessToJs(fs.readFileSync(themePath, 'utf8')) +} diff --git a/webpack.config.js b/webpack.configtest.js similarity index 100% rename from webpack.config.js rename to webpack.configtest.js