Commit 645f7daf authored by jim's avatar jim Committed by 陈帅

support perttier

parent 08456b98
{
"parser": "babel-eslint",
"extends": "airbnb",
"plugins": ["compat"],
"extends": ["airbnb", "prettier"],
"env": {
"browser": true,
"node": true,
......@@ -20,15 +19,18 @@
"react/jsx-no-bind": [0],
"react/prop-types": [0],
"react/prefer-stateless-function": [0],
"react/jsx-wrap-multilines": ["error", {
"declaration": "parens-new-line",
"assignment": "parens-new-line",
"return": "parens-new-line",
"arrow": "parens-new-line",
"condition": "parens-new-line",
"logical": "parens-new-line",
"prop": "ignore"
}],
"react/jsx-wrap-multilines": [
"error",
{
"declaration": "parens-new-line",
"assignment": "parens-new-line",
"return": "parens-new-line",
"arrow": "parens-new-line",
"condition": "parens-new-line",
"logical": "parens-new-line",
"prop": "ignore"
}
],
"no-else-return": [0],
"no-restricted-syntax": [0],
"import/no-extraneous-dependencies": [0],
......@@ -43,18 +45,20 @@
"no-bitwise": [0],
"no-cond-assign": [0],
"import/no-unresolved": [0],
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "ignore"
}],
"comma-dangle": [
"error",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "ignore"
}
],
"object-curly-newline": [0],
"function-paren-newline": [0],
"no-restricted-globals": [0],
"require-yield": [1],
"compat/compat": "error"
"require-yield": [1]
},
"parserOptions": {
"ecmaFeatures": {
......
......@@ -20,4 +20,3 @@ yarn.lock
package-lock.json
*bak
jsconfig.json
.prettierrc
**/*.md
**/*.svg
\ No newline at end of file
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}
......@@ -14,7 +14,7 @@ const noProxy = process.env.NO_PROXY === 'true';
const proxy = {
// 支持值为 Object 和 Array
'GET /api/currentUser': {
$desc: "获取当前用户接口",
$desc: '获取当前用户接口',
$params: {
pageSize: {
desc: '分页',
......@@ -29,22 +29,26 @@ const proxy = {
},
},
// GET POST 可省略
'GET /api/users': [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}],
'GET /api/users': [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
],
'GET /api/project/notice': getNotice,
'GET /api/activities': getActivities,
'GET /api/rule': getRule,
......@@ -61,7 +65,7 @@ const proxy = {
res.send({ message: 'Ok' });
},
'GET /api/tags': mockjs.mock({
'list|100': [{ name: '@city', 'value|1-100': 150, 'type|0-2': 1 }]
'list|100': [{ name: '@city', 'value|1-100': 150, 'type|0-2': 1 }],
}),
'GET /api/fake_list': getFakeList,
'GET /api/fake_chart_data': getFakeChartData,
......@@ -69,26 +73,26 @@ const proxy = {
'GET /api/profile/advanced': getProfileAdvancedData,
'POST /api/login/account': (req, res) => {
const { password, userName, type } = req.body;
if(password === '888888' && userName === 'admin'){
if (password === '888888' && userName === 'admin') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin'
currentAuthority: 'admin',
});
return ;
return;
}
if(password === '123456' && userName === 'user'){
if (password === '123456' && userName === 'user') {
res.send({
status: 'ok',
type,
currentAuthority: 'user'
currentAuthority: 'user',
});
return ;
return;
}
res.send({
status: 'error',
type,
currentAuthority: 'guest'
currentAuthority: 'guest',
});
},
'POST /api/register': (req, res) => {
......@@ -97,40 +101,40 @@ const proxy = {
'GET /api/notices': getNotices,
'GET /api/500': (req, res) => {
res.status(500).send({
"timestamp": 1513932555104,
"status": 500,
"error": "error",
"message": "error",
"path": "/base/category/list"
timestamp: 1513932555104,
status: 500,
error: 'error',
message: 'error',
path: '/base/category/list',
});
},
'GET /api/404': (req, res) => {
res.status(404).send({
"timestamp": 1513932643431,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/base/category/list/2121212"
timestamp: 1513932643431,
status: 404,
error: 'Not Found',
message: 'No message available',
path: '/base/category/list/2121212',
});
},
'GET /api/403': (req, res) => {
res.status(403).send({
"timestamp": 1513932555104,
"status": 403,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/base/category/list"
timestamp: 1513932555104,
status: 403,
error: 'Unauthorized',
message: 'Unauthorized',
path: '/base/category/list',
});
},
'GET /api/401': (req, res) => {
res.status(401).send({
"timestamp": 1513932555104,
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/base/category/list"
timestamp: 1513932555104,
status: 401,
error: 'Unauthorized',
message: 'Unauthorized',
path: '/base/category/list',
});
},
};
export default noProxy ? {} : delay(proxy, 1000);
export default (noProxy ? {} : delay(proxy, 1000));
{
"extends": "stylelint-config-standard",
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"rules": {
"selector-pseudo-class-no-unknown": null,
"shorthand-property-no-redundant-values": null,
......@@ -17,6 +17,7 @@
"number-no-trailing-zeros": null,
"rule-empty-line-before": null,
"selector-combinator-space-after": null,
"selector-descendant-combinator-no-non-space": null,
"selector-list-comma-newline-after": null,
"selector-pseudo-element-colon-notation": null,
"unit-no-unknown": null,
......
{
"editor.formatOnSave": true
}
......@@ -12,7 +12,7 @@ export default {
},
},
alias: {
'components': path.resolve(__dirname, 'src/components/'),
components: path.resolve(__dirname, 'src/components/'),
},
ignoreMomentLocale: true,
theme: './src/theme.js',
......
......@@ -69,21 +69,23 @@ export function fakeList(count) {
owner: user[i % 10],
title: titles[i % 8],
avatar: avatars[i % 8],
cover: parseInt(i / 4, 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)],
cover: parseInt(i / 4, 10) % 2 === 0 ? covers[i % 4] : covers[3 - i % 4],
status: ['active', 'exception', 'normal'][i % 3],
percent: Math.ceil(Math.random() * 50) + 50,
logo: avatars[i % 8],
href: 'https://ant.design',
updatedAt: new Date(new Date().getTime() - (1000 * 60 * 60 * 2 * i)),
createdAt: new Date(new Date().getTime() - (1000 * 60 * 60 * 2 * i)),
updatedAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),
createdAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),
subDescription: desc[i % 5],
description: '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。',
description:
'在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。',
activeUser: Math.ceil(Math.random() * 100000) + 100000,
newUser: Math.ceil(Math.random() * 1000) + 1000,
star: Math.ceil(Math.random() * 100) + 100,
like: Math.ceil(Math.random() * 100) + 100,
message: Math.ceil(Math.random() * 10) + 10,
content: '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。',
content:
'段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。',
members: [
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png',
......@@ -112,7 +114,7 @@ export function getFakeList(req, res, u) {
const params = parse(url, true).query;
const count = (params.count * 1) || 20;
const count = params.count * 1 || 20;
const result = fakeList(count);
......@@ -287,7 +289,6 @@ export const getActivities = [
},
];
export default {
getNotice,
getActivities,
......
......@@ -7,7 +7,7 @@ const beginDay = new Date().getTime();
const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5];
for (let i = 0; i < fakeY.length; i += 1) {
visitData.push({
x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'),
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: fakeY[i],
});
}
......@@ -16,7 +16,7 @@ const visitData2 = [];
const fakeY2 = [1, 6, 4, 8, 3, 7, 2];
for (let i = 0; i < fakeY2.length; i += 1) {
visitData2.push({
x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'),
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: fakeY2[i],
});
}
......@@ -125,7 +125,7 @@ for (let i = 0; i < 10; i += 1) {
const offlineChartData = [];
for (let i = 0; i < 20; i += 1) {
offlineChartData.push({
x: (new Date().getTime()) + (1000 * 60 * 30 * i),
x: new Date().getTime() + 1000 * 60 * 30 * i,
y1: Math.floor(Math.random() * 100) + 10,
y2: Math.floor(Math.random() * 100) + 10,
});
......@@ -167,8 +167,8 @@ const radarTitleMap = {
contribute: '贡献',
hot: '热度',
};
radarOriginData.forEach((item) => {
Object.keys(item).forEach((key) => {
radarOriginData.forEach(item => {
Object.keys(item).forEach(key => {
if (key !== 'name') {
radarData.push({
name: item.name,
......
......@@ -5,9 +5,12 @@ let tableListDataSource = [];
for (let i = 0; i < 46; i += 1) {
tableListDataSource.push({
key: i,
disabled: ((i % 6) === 0),
disabled: i % 6 === 0,
href: 'https://ant.design',
avatar: ['https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png'][i % 2],
avatar: [
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
][i % 2],
no: `TradeCode ${i}`,
title: `一个任务名称 ${i}`,
owner: '曲丽丽',
......@@ -43,7 +46,7 @@ export function getRule(req, res, u) {
if (params.status) {
const status = params.status.split(',');
let filterDataSource = [];
status.forEach((s) => {
status.forEach(s => {
filterDataSource = filterDataSource.concat(
[...dataSource].filter(data => parseInt(data.status, 10) === parseInt(s[0], 10))
);
......@@ -95,7 +98,10 @@ export function postRule(req, res, u, b) {
tableListDataSource.unshift({
key: i,
href: 'https://ant.design',
avatar: ['https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png'][i % 2],
avatar: [
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
][i % 2],
no: `TradeCode ${i}`,
title: `一个任务名称 ${i}`,
owner: '曲丽丽',
......
......@@ -17,7 +17,8 @@
"lint-staged:js": "eslint --ext .js",
"test": "roadhog test",
"test:component": "roadhog test ./src/components",
"test:all": "node ./tests/run-tests.js"
"test:all": "node ./tests/run-tests.js",
"prettier": "prettier --write ./src/**/**/**/*"
},
"dependencies": {
"@antv/data-set": "^0.8.0",
......@@ -64,31 +65,31 @@
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-markdown": "^1.0.0-beta.6",
"eslint-plugin-react": "^7.0.1",
"eslint-config-prettier": "^2.9.0",
"gh-pages": "^1.0.0",
"husky": "^0.14.3",
"lint-staged": "^6.0.0",
"mockjs": "^1.0.1-beta3",
"prettier": "1.11.1",
"pro-download": "^1.0.1",
"redbox-react": "^1.5.0",
"regenerator-runtime": "^0.11.1",
"roadhog": "^2.3.0",
"roadhog-api-doc": "^0.3.4",
"stylelint": "^8.4.0",
"stylelint-config-prettier": "^3.0.4",
"stylelint-config-standard": "^18.0.0"
},
"optionalDependencies": {
"puppeteer": "^1.1.1"
},
"lint-staged": {
"**/*.{js,jsx,less}": ["prettier --wirter", "git add"],
"**/*.{js,jsx}": "lint-staged:js",
"**/*.less": "stylelint --syntax less"
},
"engines": {
"node": ">=8.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
]
"browserslist": ["> 1%", "last 2 versions", "not ie <= 10"]
}
import { isUrl } from '../utils/utils';
const menuData = [{
name: 'dashboard',
icon: 'dashboard',
path: 'dashboard',
children: [{
name: '分析页',
path: 'analysis',
}, {
name: '监控页',
path: 'monitor',
}, {
name: '工作台',
path: 'workplace',
// hideInBreadcrumb: true,
// hideInMenu: true,
}],
}, {
name: '表单页',
icon: 'form',
path: 'form',
children: [{
name: '基础表单',
path: 'basic-form',
}, {
name: '分步表单',
path: 'step-form',
}, {
name: '高级表单',
authority: 'admin',
path: 'advanced-form',
}],
}, {
name: '列表页',
icon: 'table',
path: 'list',
children: [{
name: '查询表格',
path: 'table-list',
}, {
name: '标准列表',
path: 'basic-list',
}, {
name: '卡片列表',
path: 'card-list',
}, {
name: '搜索列表',
path: 'search',
children: [{
name: '搜索列表(文章)',
path: 'articles',
}, {
name: '搜索列表(项目)',
path: 'projects',
}, {
name: '搜索列表(应用)',
path: 'applications',
}],
}],
}, {
name: '详情页',
icon: 'profile',
path: 'profile',
children: [{
name: '基础详情页',
path: 'basic',
}, {
name: '高级详情页',
path: 'advanced',
authority: 'admin',
}],
}, {
name: '结果页',
icon: 'check-circle-o',
path: 'result',
children: [{
name: '成功',
path: 'success',
}, {
name: '失败',
path: 'fail',
}],
}, {
name: '异常页',
icon: 'warning',
path: 'exception',
children: [{
name: '403',
path: '403',
}, {
name: '404',
path: '404',
}, {
name: '500',
path: '500',
}, {
name: '触发异常',
path: 'trigger',
hideInMenu: true,
}],
}, {
name: '账户',
icon: 'user',
path: 'user',
authority: 'guest',
children: [{
name: '登录',
path: 'login',
}, {
name: '注册',
path: 'register',
}, {
name: '注册结果',
path: 'register-result',
}],
}];
const menuData = [
{
name: 'dashboard',
icon: 'dashboard',
path: 'dashboard',
children: [
{
name: '分析页',
path: 'analysis',
},
{
name: '监控页',
path: 'monitor',
},
{
name: '工作台',
path: 'workplace',
// hideInBreadcrumb: true,
// hideInMenu: true,
},
],
},
{
name: '表单页',
icon: 'form',
path: 'form',
children: [
{
name: '基础表单',
path: 'basic-form',
},
{
name: '分步表单',
path: 'step-form',
},
{
name: '高级表单',
authority: 'admin',
path: 'advanced-form',
},
],
},
{
name: '列表页',
icon: 'table',
path: 'list',
children: [
{
name: '查询表格',
path: 'table-list',
},
{
name: '标准列表',
path: 'basic-list',
},
{
name: '卡片列表',
path: 'card-list',
},
{
name: '搜索列表',
path: 'search',
children: [
{
name: '搜索列表(文章)',
path: 'articles',
},
{
name: '搜索列表(项目)',
path: 'projects',
},
{
name: '搜索列表(应用)',
path: 'applications',
},
],
},
],
},
{
name: '详情页',
icon: 'profile',
path: 'profile',
children: [
{
name: '基础详情页',
path: 'basic',
},
{
name: '高级详情页',
path: 'advanced',
authority: 'admin',
},
],
},
{
name: '结果页',
icon: 'check-circle-o',
path: 'result',
children: [
{
name: '成功',
path: 'success',
},
{
name: '失败',
path: 'fail',
},
],
},
{
name: '异常页',
icon: 'warning',
path: 'exception',
children: [
{
name: '403',
path: '403',
},
{
name: '404',
path: '404',
},
{
name: '500',
path: '500',
},
{
name: '触发异常',
path: 'trigger',
hideInMenu: true,
},
],
},
{
name: '账户',
icon: 'user',
path: 'user',
authority: 'guest',
children: [
{
name: '登录',
path: 'login',
},
{
name: '注册',
path: 'register',
},
{
name: '注册结果',
path: 'register-result',
},
],
},
];
function formatter(data, parentPath = '/', parentAuthority) {
return data.map((item) => {
return data.map(item => {
let { path } = item;
if (!isUrl(path)) {
path = parentPath + item.path;
......
......@@ -5,25 +5,24 @@ import { getMenuData } from './menu';
let routerDataCache;
const modelNotExisted = (app, model) => (
const modelNotExisted = (app, model) =>
// eslint-disable-next-line
!app._models.some(({ namespace }) => {
return namespace === model.substring(model.lastIndexOf('/') + 1);
})
);
});
// wrapper of dynamic
const dynamicWrapper = (app, models, component) => {
// () => require('module')
// transformed by babel-plugin-dynamic-import-node-sync
if (component.toString().indexOf('.then(') < 0) {
models.forEach((model) => {
models.forEach(model => {
if (modelNotExisted(app, model)) {
// eslint-disable-next-line
app.model(require(`../models/${model}`).default);
}
});
return (props) => {
return props => {
if (!routerDataCache) {
routerDataCache = getRouterData(app);
}
......@@ -36,20 +35,20 @@ const dynamicWrapper = (app, models, component) => {
// () => import('module')
return dynamic({
app,
models: () => models.filter(
model => modelNotExisted(app, model)).map(m => import(`../models/${m}.js`)
),
models: () =>
models.filter(model => modelNotExisted(app, model)).map(m => import(`../models/${m}.js`)),
// add routerData prop
component: () => {
if (!routerDataCache) {
routerDataCache = getRouterData(app);
}
return component().then((raw) => {
return component().then(raw => {
const Component = raw.default || raw;
return props => createElement(Component, {
...props,
routerData: routerDataCache,
});
return props =>
createElement(Component, {
...props,
routerData: routerDataCache,
});
});
},
});
......@@ -57,7 +56,7 @@ const dynamicWrapper = (app, models, component) => {
function getFlatMenuData(menus) {
let keys = {};
menus.forEach((item) => {
menus.forEach(item => {
if (item.children) {
keys[item.path] = { ...item };
keys = { ...keys, ...getFlatMenuData(item.children) };
......@@ -68,7 +67,7 @@ function getFlatMenuData(menus) {
return keys;
}
export const getRouterData = (app) => {
export const getRouterData = app => {
const routerConfig = {
'/': {
component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),
......@@ -80,7 +79,9 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, ['monitor'], () => import('../routes/Dashboard/Monitor')),
},
'/dashboard/workplace': {
component: dynamicWrapper(app, ['project', 'activities', 'chart'], () => import('../routes/Dashboard/Workplace')),
component: dynamicWrapper(app, ['project', 'activities', 'chart'], () =>
import('../routes/Dashboard/Workplace')
),
// hideInBreadcrumb: true,
// name: '工作台',
// authority: 'admin',
......@@ -131,7 +132,9 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/BasicProfile')),
},
'/profile/advanced': {
component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/AdvancedProfile')),
component: dynamicWrapper(app, ['profile'], () =>
import('../routes/Profile/AdvancedProfile')
),
},
'/result/success': {
component: dynamicWrapper(app, [], () => import('../routes/Result/Success')),
......@@ -149,7 +152,9 @@ export const getRouterData = (app) => {
component: dynamicWrapper(app, [], () => import('../routes/Exception/500')),
},
'/exception/trigger': {
component: dynamicWrapper(app, ['error'], () => import('../routes/Exception/triggerException')),
component: dynamicWrapper(app, ['error'], () =>
import('../routes/Exception/triggerException')
),
},
'/user': {
component: dynamicWrapper(app, [], () => import('../layouts/UserLayout')),
......@@ -174,7 +179,7 @@ export const getRouterData = (app) => {
// eg. {name,authority ...routerConfig }
const routerData = {};
// The route matches the menu
Object.keys(routerConfig).forEach((path) => {
Object.keys(routerConfig).forEach(path => {
// Regular match item name
// eg. router /user/:id === /user/chen
const pathRegexp = pathToRegexp(path);
......
......@@ -14,7 +14,7 @@ function getActiveData() {
for (let i = 0; i < 24; i += 1) {
activeData.push({
x: `${fixedZero(i)}:00`,
y: Math.floor(Math.random() * 200) + (i * 50),
y: Math.floor(Math.random() * 200) + i * 50,
});
}
return activeData;
......
......@@ -4,29 +4,13 @@ import Authorized from './Authorized';
class AuthorizedRoute extends React.Component {
render() {
const {
component: Component,
render,
authority,
redirectPath,
...rest
} = this.props;
const { component: Component, render, authority, redirectPath, ...rest } = this.props;
return (
<Authorized
authority={authority}
noMatch={
<Route
{...rest}
render={() => <Redirect to={{ pathname: redirectPath }} />}
/>
}
noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
>
<Route
{...rest}
render={props =>
(Component ? <Component {...props} /> : render(props))
}
/>
<Route {...rest} render={props => (Component ? <Component {...props} /> : render(props))} />
</Authorized>
);
}
......
......@@ -20,24 +20,16 @@ describe('test CheckPermissions', () => {
expect(checkPermissions('admin', 'user', target, error)).toEqual('error');
});
it('Correct Array permission authentication', () => {
expect(checkPermissions(['user', 'admin'], 'user', target, error)).toEqual(
'ok'
);
expect(checkPermissions(['user', 'admin'], 'user', target, error)).toEqual('ok');
});
it('Wrong Array permission authentication,currentAuthority error', () => {
expect(
checkPermissions(['user', 'admin'], 'user,admin', target, error)
).toEqual('error');
expect(checkPermissions(['user', 'admin'], 'user,admin', target, error)).toEqual('error');
});
it('Wrong Array permission authentication', () => {
expect(checkPermissions(['user', 'admin'], 'guest', target, error)).toEqual(
'error'
);
expect(checkPermissions(['user', 'admin'], 'guest', target, error)).toEqual('error');
});
it('Wrong Function permission authentication', () => {
expect(checkPermissions(() => false, 'guest', target, error)).toEqual(
'error'
);
expect(checkPermissions(() => false, 'guest', target, error)).toEqual('error');
});
it('Correct Function permission authentication', () => {
expect(checkPermissions(() => true, 'guest', target, error)).toEqual('ok');
......
......@@ -32,7 +32,7 @@ export default class PromiseRender extends React.PureComponent {
// AuthorizedRoute is already instantiated
// Authorized render is already instantiated, children is no instantiated
// Secured is not instantiated
checkIsInstantiation = (target) => {
checkIsInstantiation = target => {
if (!React.isValidElement(target)) {
return target;
}
......
......@@ -5,15 +5,13 @@ import CheckPermissions from './CheckPermissions';
* 默认不能访问任何页面
* default is "NULL"
*/
const Exception403 = () => (
<Exception type="403" style={{ minHeight: 500, height: '80%' }} />
);
const Exception403 = () => <Exception type="403" style={{ minHeight: 500, height: '80%' }} />;
// Determine whether the incoming component has been instantiated
// AuthorizedRoute is already instantiated
// Authorized render is already instantiated, children is no instantiated
// Secured is not instantiated
const checkIsInstantiation = (target) => {
const checkIsInstantiation = target => {
if (!React.isValidElement(target)) {
return target;
}
......
......@@ -11,24 +11,19 @@ export type IReactComponent<P = any> =
| React.ClassicComponentClass<P>;
interface Secured {
(authority: authority, error?: React.ReactNode): <T extends IReactComponent>(
target: T,
) => T;
(authority: authority, error?: React.ReactNode): <T extends IReactComponent>(target: T) => T;
}
export interface AuthorizedRouteProps extends RouteProps {
authority: authority;
}
export class AuthorizedRoute extends React.Component<
AuthorizedRouteProps,
any
> {}
export class AuthorizedRoute extends React.Component<AuthorizedRouteProps, any> {}
interface check {
<T extends IReactComponent, S extends IReactComponent>(
authority: authority,
target: T,
Exception: S,
Exception: S
): T | S;
}
......
......@@ -14,7 +14,7 @@ Authorized.check = check;
* use authority or getAuthority
* @param {string|()=>String} currentAuthority
*/
const renderAuthorize = (currentAuthority) => {
const renderAuthorize = currentAuthority => {
if (currentAuthority) {
if (currentAuthority.constructor.name === 'Function') {
CURRENT = currentAuthority();
......
......@@ -4,9 +4,7 @@ import AvatarItem from './AvatarItem';
export interface IAvatarListProps {
size?: 'large' | 'small' | 'mini' | 'default';
style?: React.CSSProperties;
children:
| React.ReactElement<AvatarItem>
| Array<React.ReactElement<AvatarItem>>;
children: React.ReactElement<AvatarItem> | Array<React.ReactElement<AvatarItem>>;
}
export default class AvatarList extends React.Component<IAvatarListProps, any> {
......
......@@ -18,7 +18,7 @@ const AvatarList = ({ children, size, ...other }) => {
);
};
const Item = ({ src, size, tips, onClick = (() => {}) }) => {
const Item = ({ src, size, tips, onClick = () => {} }) => {
const cls = classNames(styles.avatarItem, {
[styles.avatarItemLarge]: size === 'large',
[styles.avatarItemSmall]: size === 'small',
......@@ -26,14 +26,14 @@ const Item = ({ src, size, tips, onClick = (() => {}) }) => {
});
return (
<li className={cls} onClick={onClick} >
{
tips ? (
<Tooltip title={tips}>
<Avatar src={src} size={size} style={{ cursor: 'pointer' }} />
</Tooltip>
) : <Avatar src={src} size={size} />
}
<li className={cls} onClick={onClick}>
{tips ? (
<Tooltip title={tips}>
<Avatar src={src} size={size} style={{ cursor: 'pointer' }} />
</Tooltip>
) : (
<Avatar src={src} size={size} />
)}
</li>
);
};
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.avatarList {
display: inline-block;
......
import * as React from "react";
import * as React from 'react';
export interface IBarProps {
title: React.ReactNode;
color?: string;
......
......@@ -46,11 +46,11 @@ class Bar extends Component {
}
}
handleRoot = (n) => {
handleRoot = n => {
this.root = n;
};
handleRef = (n) => {
handleRef = n => {
this.node = n;
};
......
import * as React from "react";
import * as React from 'react';
export interface IChartCardProps {
title: React.ReactNode;
action?: React.ReactNode;
......
......@@ -4,7 +4,7 @@ import classNames from 'classnames';
import styles from './index.less';
const renderTotal = (total) => {
const renderTotal = total => {
let totalDom;
switch (typeof total) {
case undefined:
......@@ -33,7 +33,9 @@ const ChartCard = ({
const content = (
<div className={styles.chartCard}>
<div
className={classNames(styles.chartTop, { [styles.chartTopMargin]: !children && !footer })}
className={classNames(styles.chartTop, {
[styles.chartTopMargin]: !children && !footer,
})}
>
<div className={styles.avatar}>{avatar}</div>
<div className={styles.metaWrap}>
......@@ -50,7 +52,11 @@ const ChartCard = ({
</div>
)}
{footer && (
<div className={classNames(styles.footer, { [styles.footerMargin]: !children })}>
<div
className={classNames(styles.footer, {
[styles.footerMargin]: !children,
})}
>
{footer}
</div>
)}
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.chartCard {
position: relative;
......
import * as React from "react";
import * as React from 'react';
export interface IFieldProps {
label: React.ReactNode;
value: React.ReactNode;
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.field {
white-space: nowrap;
......
import * as React from "react";
import * as React from 'react';
export interface IGaugeProps {
title: React.ReactNode;
color?: string;
......
......@@ -4,7 +4,7 @@ import autoHeight from '../autoHeight';
const { Arc, Html, Line } = Guide;
const defaultFormatter = (val) => {
const defaultFormatter = val => {
switch (val) {
case '2':
return '';
......
import * as React from "react";
import * as React from 'react';
export interface IMiniProgressProps {
target: number;
color?: string;
......@@ -7,7 +7,4 @@ export interface IMiniProgressProps {
style?: React.CSSProperties;
}
export default class MiniProgress extends React.Component<
IMiniProgressProps,
any
> {}
export default class MiniProgress extends React.Component<IMiniProgressProps, any> {}
......@@ -6,21 +6,18 @@ import styles from './index.less';
const MiniProgress = ({ target, color = 'rgb(19, 194, 194)', strokeWidth, percent }) => (
<div className={styles.miniProgress}>
<Tooltip title={`目标值: ${target}%`}>
<div
className={styles.target}
style={{ left: (target ? `${target}%` : null) }}
>
<span style={{ backgroundColor: (color || null) }} />
<span style={{ backgroundColor: (color || null) }} />
<div className={styles.target} style={{ left: target ? `${target}%` : null }}>
<span style={{ backgroundColor: color || null }} />
<span style={{ backgroundColor: color || null }} />
</div>
</Tooltip>
<div className={styles.progressWrap}>
<div
className={styles.progress}
style={{
backgroundColor: (color || null),
width: (percent ? `${percent}%` : null),
height: (strokeWidth || null),
backgroundColor: color || null,
width: percent ? `${percent}%` : null,
height: strokeWidth || null,
}}
/>
</div>
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.miniProgress {
padding: 5px 0;
......@@ -9,7 +9,7 @@
position: relative;
}
.progress {
transition: all .4s cubic-bezier(.08, .82, .17, 1) 0s;
transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
border-radius: 1px 0 0 1px;
background-color: @primary-color;
width: 0;
......
......@@ -44,7 +44,7 @@ export default class Pie extends Component {
this.resize.cancel();
}
getG2Instance = (chart) => {
getG2Instance = chart => {
this.chart = chart;
};
......@@ -54,7 +54,7 @@ export default class Pie extends Component {
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
const items = geom.get('dataArray') || []; // 获取图形对应的
const legendData = items.map((item) => {
const legendData = items.map(item => {
/* eslint no-underscore-dangle:0 */
const origin = item[0]._origin;
origin.color = item[0].color;
......@@ -89,7 +89,7 @@ export default class Pie extends Component {
}
}
handleRoot = (n) => {
handleRoot = n => {
this.root = n;
};
......@@ -154,7 +154,7 @@ export default class Pie extends Component {
if (percent) {
selected = false;
tooltip = false;
formatColor = (value) => {
formatColor = value => {
if (value === '占比') {
return color || 'rgba(24, 144, 255, 0.85)';
} else {
......@@ -235,7 +235,9 @@ export default class Pie extends Component {
<li key={item.x} onClick={() => this.handleLegendClick(item, i)}>
<span
className={styles.dot}
style={{ backgroundColor: !item.checked ? '#aaa' : item.color }}
style={{
backgroundColor: !item.checked ? '#aaa' : item.color,
}}
/>
<span className={styles.legendTitle}>{item.x}</span>
<Divider type="vertical" />
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.pie {
position: relative;
......@@ -6,7 +6,7 @@
position: relative;
}
&.hasLegend .chart {
width: ~"calc(100% - 240px)";
width: ~'calc(100% - 240px)';
}
.legend {
position: absolute;
......
import * as React from "react";
import * as React from 'react';
export interface IRadarProps {
title?: React.ReactNode;
height: number;
......
......@@ -21,7 +21,7 @@ export default class Radar extends Component {
}
}
getG2Instance = (chart) => {
getG2Instance = chart => {
this.chart = chart;
};
......@@ -31,7 +31,7 @@ export default class Radar extends Component {
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
const items = geom.get('dataArray') || []; // 获取图形对应的
const legendData = items.map((item) => {
const legendData = items.map(item => {
// eslint-disable-next-line
const origins = item.map(t => t._origin);
const result = {
......@@ -49,7 +49,7 @@ export default class Radar extends Component {
});
};
handleRef = (n) => {
handleRef = n => {
this.node = n;
};
......@@ -60,9 +60,7 @@ export default class Radar extends Component {
const { legendData } = this.state;
legendData[i] = newItem;
const filteredLegendData = legendData
.filter(l => l.checked)
.map(l => l.name);
const filteredLegendData = legendData.filter(l => l.checked).map(l => l.name);
if (this.chart) {
this.chart.filter('name', val => filteredLegendData.indexOf(val) > -1);
......@@ -143,12 +141,7 @@ export default class Radar extends Component {
},
}}
/>
<Geom
type="line"
position="label*value"
color={['name', colors]}
size={1}
/>
<Geom type="line" position="label*value" color={['name', colors]} size={1} />
<Geom
type="point"
position="label*value"
......@@ -178,7 +171,7 @@ export default class Radar extends Component {
<h6>{item.value}</h6>
</div>
</Col>
))}
))}
</Row>
)}
</div>
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.radar {
.legend {
......
import * as React from "react";
import * as React from 'react';
export interface ITagCloudProps {
data: Array<{
name: string;
......
......@@ -39,7 +39,7 @@ class TagCloud extends Component {
this.renderChart();
};
saveRootRef = (node) => {
saveRootRef = node => {
this.root = node;
};
......@@ -77,7 +77,7 @@ class TagCloud extends Component {
@Bind()
@Debounce(500)
renderChart = (nextProps) => {
renderChart = nextProps => {
// const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
const { data, height } = nextProps || this.props;
......
import * as React from "react";
import * as React from 'react';
export interface ITimelineChartProps {
data: Array<{
x: string;
......@@ -11,7 +11,4 @@ export interface ITimelineChartProps {
style?: React.CSSProperties;
}
export default class TimelineChart extends React.Component<
ITimelineChartProps,
any
> {}
export default class TimelineChart extends React.Component<ITimelineChartProps, any> {}
......@@ -48,7 +48,7 @@ export default class TimelineChart extends React.Component {
.source(data)
.transform({
type: 'filter',
callback: (obj) => {
callback: obj => {
const date = obj.x;
return date <= ds.state.end && date >= ds.state.start;
},
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.waterWave {
display: inline-block;
......@@ -22,7 +22,7 @@
}
}
.waterWaveCanvasWrapper {
transform: scale(.5);
transform: scale(0.5);
transform-origin: 0 0;
}
}
......@@ -30,7 +30,7 @@ function getAutoHeight(n) {
return height;
}
const autoHeight = () => (WrappedComponent) => {
const autoHeight = () => WrappedComponent => {
return class extends React.Component {
state = {
computedHeight: 0,
......@@ -45,7 +45,7 @@ const autoHeight = () => (WrappedComponent) => {
}
}
handleRoot = (node) => {
handleRoot = node => {
this.root = node;
};
......
import * as React from "react";
import * as React from 'react';
export interface ICountDownProps {
format?: (time: number) => void;
target: Date | number;
......
......@@ -23,11 +23,14 @@ class CountDown extends Component {
if (this.props.target !== nextProps.target) {
clearTimeout(this.timer);
const { lastTime } = this.initTime(nextProps);
this.setState({
lastTime,
}, () => {
this.tick();
});
this.setState(
{
lastTime,
},
() => {
this.tick();
}
);
}
}
......@@ -37,7 +40,7 @@ class CountDown extends Component {
timer = 0;
interval = 1000;
initTime = (props) => {
initTime = props => {
let lastTime = 0;
let targetTime = 0;
try {
......@@ -54,21 +57,23 @@ class CountDown extends Component {
return {
lastTime: lastTime < 0 ? 0 : lastTime,
};
}
};
// defaultFormat = time => (
// <span>{moment(time).format('hh:mm:ss')}</span>
// );
defaultFormat = (time) => {
defaultFormat = time => {
const hours = 60 * 60 * 1000;
const minutes = 60 * 1000;
const h = Math.floor(time / hours);
const m = Math.floor((time - (h * hours)) / minutes);
const s = Math.floor((time - (h * hours) - (m * minutes)) / 1000);
const m = Math.floor((time - h * hours) / minutes);
const s = Math.floor((time - h * hours - m * minutes) / 1000);
return (
<span>{fixedZero(h)}:{fixedZero(m)}:{fixedZero(s)}</span>
<span>
{fixedZero(h)}:{fixedZero(m)}:{fixedZero(s)}
</span>
);
}
};
tick = () => {
const { onEnd } = this.props;
let { lastTime } = this.state;
......@@ -76,30 +81,36 @@ class CountDown extends Component {
this.timer = setTimeout(() => {
if (lastTime < this.interval) {
clearTimeout(this.timer);
this.setState({
lastTime: 0,
}, () => {
if (onEnd) {
onEnd();
this.setState(
{
lastTime: 0,
},
() => {
if (onEnd) {
onEnd();
}
}
});
);
} else {
lastTime -= this.interval;
this.setState({
lastTime,
}, () => {
this.tick();
});
this.setState(
{
lastTime,
},
() => {
this.tick();
}
);
}
}, this.interval);
}
};
render() {
const { format = this.defaultFormat, onEnd, ...rest } = this.props;
const { lastTime } = this.state;
const result = format(lastTime);
return (<span {...rest}>{result}</span>);
return <span {...rest}>{result}</span>;
}
}
......
......@@ -3,8 +3,16 @@ import classNames from 'classnames';
import { Row } from 'antd';
import styles from './index.less';
export default ({ className, title, col = 3, layout = 'horizontal', gutter = 32,
children, size, ...restProps }) => {
export default ({
className,
title,
col = 3,
layout = 'horizontal',
gutter = 32,
children,
size,
...restProps
}) => {
const clsString = classNames(styles.descriptionList, styles[layout], className, {
[styles.small]: size === 'small',
[styles.large]: size === 'large',
......
......@@ -10,9 +10,6 @@ export interface IDescriptionListProps {
style?: React.CSSProperties;
}
export default class DescriptionList extends React.Component<
IDescriptionListProps,
any
> {
export default class DescriptionList extends React.Component<IDescriptionListProps, any> {
public static Description: typeof Description;
}
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.descriptionList {
// offset the padding-bottom of last row
......@@ -25,10 +25,10 @@
display: table-cell;
&:after {
content: ":";
content: ':';
margin: 0 8px 0 2px;
position: relative;
top: -.5px;
top: -0.5px;
}
}
......@@ -51,7 +51,8 @@
margin-bottom: 12px;
color: @text-color;
}
.term, .detail {
.term,
.detail {
padding-bottom: 8px;
}
}
......
......@@ -7,48 +7,34 @@ export default class EditableItem extends PureComponent {
value: this.props.value,
editable: false,
};
handleChange = (e) => {
handleChange = e => {
const { value } = e.target;
this.setState({ value });
}
};
check = () => {
this.setState({ editable: false });
if (this.props.onChange) {
this.props.onChange(this.state.value);
}
}
};
edit = () => {
this.setState({ editable: true });
}
};
render() {
const { value, editable } = this.state;
return (
<div className={styles.editableItem}>
{
editable ? (
<div className={styles.wrapper}>
<Input
value={value}
onChange={this.handleChange}
onPressEnter={this.check}
/>
<Icon
type="check"
className={styles.icon}
onClick={this.check}
/>
</div>
) : (
<div className={styles.wrapper}>
<span>{value || ' '}</span>
<Icon
type="edit"
className={styles.icon}
onClick={this.edit}
/>
</div>
)
}
{editable ? (
<div className={styles.wrapper}>
<Input value={value} onChange={this.handleChange} onPressEnter={this.check} />
<Icon type="check" className={styles.icon} onClick={this.check} />
</div>
) : (
<div className={styles.wrapper}>
<span>{value || ' '}</span>
<Icon type="edit" className={styles.icon} onClick={this.edit} />
</div>
)}
</div>
);
}
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.editableItem {
line-height: @input-height-base;
......
......@@ -22,15 +22,17 @@ class EditableLinkGroup extends PureComponent {
const { links, linkElement, onAdd } = this.props;
return (
<div className={styles.linkGroup}>
{
links.map(link => (
createElement(linkElement, {
{links.map(link =>
createElement(
linkElement,
{
key: `linkGroup-item-${link.id || link.title}`,
to: link.href,
href: link.href,
}, link.title)
))
}
},
link.title
)
)}
{
<Button size="small" type="primary" ghost onClick={onAdd} icon="plus">
添加
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.linkGroup {
padding: 20px 0 8px 24px;
......
......@@ -125,23 +125,23 @@ export default class Ellipsis extends Component {
}
};
handleRoot = (n) => {
handleRoot = n => {
this.root = n;
};
handleContent = (n) => {
handleContent = n => {
this.content = n;
};
handleNode = (n) => {
handleNode = n => {
this.node = n;
};
handleShadow = (n) => {
handleShadow = n => {
this.shadow = n;
};
handleShadowChildren = (n) => {
handleShadowChildren = n => {
this.shadowChildren = n;
};
......
import * as React from "react";
import * as React from 'react';
export interface IExceptionProps {
type?: "403" | "404" | "500";
type?: '403' | '404' | '500';
title?: React.ReactNode;
desc?: React.ReactNode;
img?: string;
......
......@@ -19,13 +19,15 @@ export default ({ className, linkElement = 'a', type, title, desc, img, actions,
<h1>{title || config[pageType].title}</h1>
<div className={styles.desc}>{desc || config[pageType].desc}</div>
<div className={styles.actions}>
{
actions ||
createElement(linkElement, {
{actions ||
createElement(
linkElement,
{
to: '/',
href: '/',
}, <Button type="primary">返回首页</Button>)
}
},
<Button type="primary">返回首页</Button>
)}
</div>
</div>
</div>
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.exception {
display: flex;
......@@ -12,7 +12,7 @@
zoom: 1;
&:before,
&:after {
content: " ";
content: ' ';
display: table;
}
&:after {
......
......@@ -4,7 +4,4 @@ export interface IFooterToolbarProps {
style?: React.CSSProperties;
}
export default class FooterToolbar extends React.Component<
IFooterToolbarProps,
any
> {}
export default class FooterToolbar extends React.Component<IFooterToolbarProps, any> {}
......@@ -6,10 +6,7 @@ export default class FooterToolbar extends Component {
render() {
const { children, className, extra, ...restProps } = this.props;
return (
<div
className={classNames(className, styles.toolbar)}
{...restProps}
>
<div className={classNames(className, styles.toolbar)} {...restProps}>
<div className={styles.left}>{extra}</div>
<div className={styles.right}>{children}</div>
</div>
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.toolbar {
position: fixed;
......@@ -7,14 +7,14 @@
right: 0;
height: 56px;
line-height: 56px;
box-shadow: 0 -1px 2px rgba(0, 0, 0, .03);
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
background: #fff;
border-top: 1px solid @border-color-split;
padding: 0 24px;
z-index: 9;
&:after {
content: "";
content: '';
display: block;
clear: both;
}
......
import * as React from "react";
import * as React from 'react';
export interface IGlobalFooterProps {
links?: Array<{
title: React.ReactNode;
......@@ -9,7 +9,4 @@ export interface IGlobalFooterProps {
style?: React.CSSProperties;
}
export default class GlobalFooter extends React.Component<
IGlobalFooterProps,
any
> {}
export default class GlobalFooter extends React.Component<IGlobalFooterProps, any> {}
......@@ -6,21 +6,15 @@ export default ({ className, links, copyright }) => {
const clsString = classNames(styles.globalFooter, className);
return (
<div className={clsString}>
{
links && (
<div className={styles.links}>
{links.map(link => (
<a
key={link.key}
target={link.blankTarget ? '_blank' : '_self'}
href={link.href}
>
{link.title}
</a>
))}
</div>
)
}
{links && (
<div className={styles.links}>
{links.map(link => (
<a key={link.key} target={link.blankTarget ? '_blank' : '_self'} href={link.href}>
{link.title}
</a>
))}
</div>
)}
{copyright && <div className={styles.copyright}>{copyright}</div>}
</div>
);
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.globalFooter {
padding: 0 16px;
......@@ -10,7 +10,7 @@
a {
color: @text-color-secondary;
transition: all .3s;
transition: all 0.3s;
&:not(:last-child) {
margin-right: 40px;
......
......@@ -17,7 +17,7 @@ export default class GlobalHeader extends PureComponent {
if (notices.length === 0) {
return {};
}
const newNotices = notices.map((notice) => {
const newNotices = notices.map(notice => {
const newNotice = { ...notice };
if (newNotice.datetime) {
newNotice.datetime = moment(notice.datetime).fromNow();
......@@ -27,13 +27,17 @@ export default class GlobalHeader extends PureComponent {
newNotice.key = newNotice.id;
}
if (newNotice.extra && newNotice.status) {
const color = ({
const color = {
todo: '',
processing: 'blue',
urgent: 'red',
doing: 'gold',
})[newNotice.status];
newNotice.extra = <Tag color={color} style={{ marginRight: 0 }}>{newNotice.extra}</Tag>;
}[newNotice.status];
newNotice.extra = (
<Tag color={color} style={{ marginRight: 0 }}>
{newNotice.extra}
</Tag>
);
}
return newNotice;
});
......@@ -43,40 +47,50 @@ export default class GlobalHeader extends PureComponent {
const { collapsed, onCollapse } = this.props;
onCollapse(!collapsed);
this.triggerResizeEvent();
}
};
@Debounce(600)
triggerResizeEvent() { // eslint-disable-line
triggerResizeEvent = () => {
const event = document.createEvent('HTMLEvents');
event.initEvent('resize', true, false);
window.dispatchEvent(event);
}
};
render() {
const {
currentUser, collapsed, fetchingNotices, isMobile, logo,
onNoticeVisibleChange, onMenuClick, onNoticeClear,
currentUser,
collapsed,
fetchingNotices,
isMobile,
logo,
onNoticeVisibleChange,
onMenuClick,
onNoticeClear,
} = this.props;
const menu = (
<Menu className={styles.menu} selectedKeys={[]} onClick={onMenuClick}>
<Menu.Item disabled><Icon type="user" />个人中心</Menu.Item>
<Menu.Item disabled><Icon type="setting" />设置</Menu.Item>
<Menu.Item key="triggerError"><Icon type="close-circle" />触发报错</Menu.Item>
<Menu.Item disabled>
<Icon type="user" />个人中心
</Menu.Item>
<Menu.Item disabled>
<Icon type="setting" />设置
</Menu.Item>
<Menu.Item key="triggerError">
<Icon type="close-circle" />触发报错
</Menu.Item>
<Menu.Divider />
<Menu.Item key="logout"><Icon type="logout" />退出登录</Menu.Item>
<Menu.Item key="logout">
<Icon type="logout" />退出登录
</Menu.Item>
</Menu>
);
const noticeData = this.getNoticeData();
return (
<div className={styles.header}>
{isMobile && (
[
(
<Link to="/" className={styles.logo} key="logo">
<img src={logo} alt="logo" width="32" />
</Link>
),
<Divider type="vertical" key="line" />,
]
)}
{isMobile && [
<Link to="/" className={styles.logo} key="logo">
<img src={logo} alt="logo" width="32" />
</Link>,
<Divider type="vertical" key="line" />,
]}
<Icon
className={styles.trigger}
type={collapsed ? 'menu-unfold' : 'menu-fold'}
......@@ -87,10 +101,10 @@ export default class GlobalHeader extends PureComponent {
className={`${styles.action} ${styles.search}`}
placeholder="站内搜索"
dataSource={['搜索提示一', '搜索提示二', '搜索提示三']}
onSearch={(value) => {
onSearch={value => {
console.log('input', value); // eslint-disable-line
}}
onPressEnter={(value) => {
onPressEnter={value => {
console.log('enter', value); // eslint-disable-line
}}
/>
......@@ -102,7 +116,7 @@ export default class GlobalHeader extends PureComponent {
className={styles.action}
>
<Icon type="question-circle-o" />
</a >
</a>
</Tooltip>
<NoticeIcon
className={styles.action}
......@@ -141,7 +155,9 @@ export default class GlobalHeader extends PureComponent {
<span className={styles.name}>{currentUser.name}</span>
</span>
</Dropdown>
) : <Spin size="small" style={{ marginLeft: 8 }} />}
) : (
<Spin size="small" style={{ marginLeft: 8 }} />
)}
</div>
</div>
);
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.header {
height: 64px;
padding: 0 12px 0 0;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
position: relative;
}
......@@ -42,7 +42,7 @@ i.trigger {
font-size: 20px;
line-height: 64px;
cursor: pointer;
transition: all .3s, padding 0s;
transition: all 0.3s, padding 0s;
padding: 0 24px;
&:hover {
background: @primary-1;
......@@ -56,7 +56,7 @@ i.trigger {
cursor: pointer;
padding: 0 12px;
display: inline-block;
transition: all .3s;
transition: all 0.3s;
height: 100%;
> i {
font-size: 16px;
......@@ -79,7 +79,7 @@ i.trigger {
.avatar {
margin: 20px 8px 20px 0;
color: @primary-color;
background: rgba(255, 255, 255, .85);
background: rgba(255, 255, 255, 0.85);
vertical-align: middle;
}
}
......
......@@ -8,7 +8,4 @@ export interface IHeaderSearchProps {
style?: React.CSSProperties;
}
export default class HeaderSearch extends React.Component<
IHeaderSearchProps,
any
> {}
export default class HeaderSearch extends React.Component<IHeaderSearchProps, any> {}
......@@ -28,42 +28,39 @@ export default class HeaderSearch extends PureComponent {
componentWillUnmount() {
clearTimeout(this.timeout);
}
onKeyDown = (e) => {
onKeyDown = e => {
if (e.key === 'Enter') {
this.timeout = setTimeout(() => {
this.props.onPressEnter(this.state.value); // Fix duplicate onPressEnter
}, 0);
}
}
onChange = (value) => {
};
onChange = value => {
this.setState({ value });
if (this.props.onChange) {
this.props.onChange();
}
}
};
enterSearchMode = () => {
this.setState({ searchMode: true }, () => {
if (this.state.searchMode) {
this.input.focus();
}
});
}
};
leaveSearchMode = () => {
this.setState({
searchMode: false,
value: '',
});
}
};
render() {
const { className, placeholder, ...restProps } = this.props;
const inputClass = classNames(styles.input, {
[styles.show]: this.state.searchMode,
});
return (
<span
className={classNames(className, styles.headerSearch)}
onClick={this.enterSearchMode}
>
<span className={classNames(className, styles.headerSearch)} onClick={this.enterSearchMode}>
<Icon type="search" key="Icon" />
<AutoComplete
key="AutoComplete"
......@@ -74,7 +71,9 @@ export default class HeaderSearch extends PureComponent {
>
<Input
placeholder={placeholder}
ref={(node) => { this.input = node; }}
ref={node => {
this.input = node;
}}
onKeyDown={this.onKeyDown}
onBlur={this.leaveSearchMode}
/>
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.headerSearch {
:global(.anticon-search) {
......@@ -6,7 +6,7 @@
font-size: 16px;
}
.input {
transition: width .3s, margin-left .3s;
transition: width 0.3s, margin-left 0.3s;
width: 0;
background: transparent;
border-radius: 0;
......
......@@ -8,7 +8,7 @@ import map from './map';
const FormItem = Form.Item;
function generator({ defaultProps, defaultRules, type }) {
return (WrappedComponent) => {
return WrappedComponent => {
return class BasicComponent extends Component {
static contextTypes = {
form: PropTypes.object,
......@@ -41,7 +41,7 @@ function generator({ defaultProps, defaultRules, type }) {
clearInterval(this.interval);
}
}, 1000);
}
};
render() {
const { getFieldDecorator } = this.context.form;
const options = {};
......@@ -93,7 +93,7 @@ function generator({ defaultProps, defaultRules, type }) {
}
const LoginItem = {};
Object.keys(map).forEach((item) => {
Object.keys(map).forEach(item => {
LoginItem[item] = generator({
defaultProps: map[item].props,
defaultRules: map[item].rules,
......
import * as React from "react";
import Button from "antd/lib/button";
import * as React from 'react';
import Button from 'antd/lib/button';
export interface LoginProps {
defaultActiveKey?: string;
onTabChange?: (key: string) => void;
......
......@@ -34,19 +34,19 @@ class Login extends Component {
getChildContext() {
return {
tabUtil: {
addTab: (id) => {
addTab: id => {
this.setState({
tabs: [...this.state.tabs, id],
});
},
removeTab: (id) => {
removeTab: id => {
this.setState({
tabs: this.state.tabs.filter(currentId => currentId !== id),
});
},
},
form: this.props.form,
updateActive: (activeItem) => {
updateActive: activeItem => {
const { type, active } = this.state;
if (active[type]) {
active[type].push(activeItem);
......@@ -59,28 +59,26 @@ class Login extends Component {
},
};
}
onSwitch = (type) => {
onSwitch = type => {
this.setState({
type,
});
this.props.onTabChange(type);
}
handleSubmit = (e) => {
};
handleSubmit = e => {
e.preventDefault();
const { active, type } = this.state;
const activeFileds = active[type];
this.props.form.validateFields(activeFileds, { force: true },
(err, values) => {
this.props.onSubmit(err, values);
}
);
}
this.props.form.validateFields(activeFileds, { force: true }, (err, values) => {
this.props.onSubmit(err, values);
});
};
render() {
const { className, children } = this.props;
const { type, tabs } = this.state;
const TabChildren = [];
const otherChildren = [];
React.Children.forEach(children, (item) => {
React.Children.forEach(children, item => {
if (!item) {
return;
}
......@@ -94,21 +92,21 @@ class Login extends Component {
return (
<div className={classNames(className, styles.login)}>
<Form onSubmit={this.handleSubmit}>
{
tabs.length ? (
<div>
<Tabs
animated={false}
className={styles.tabs}
activeKey={type}
onChange={this.onSwitch}
>
{TabChildren}
</Tabs>
{otherChildren}
</div>
) : [...children]
}
{tabs.length ? (
<div>
<Tabs
animated={false}
className={styles.tabs}
activeKey={type}
onChange={this.onSwitch}
>
{TabChildren}
</Tabs>
{otherChildren}
</div>
) : (
[...children]
)}
</Form>
</div>
);
......@@ -117,7 +115,7 @@ class Login extends Component {
Login.Tab = LoginTab;
Login.Submit = LoginSubmit;
Object.keys(LoginItem).forEach((item) => {
Object.keys(LoginItem).forEach(item => {
Login[item] = LoginItem[item];
});
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.login {
.tabs {
padding: 0 2px;
margin: 0 -2px;
......
......@@ -10,9 +10,12 @@ const map = {
prefix: <Icon type="user" className={styles.prefixIcon} />,
placeholder: 'admin',
},
rules: [{
required: true, message: 'Please enter username!',
}],
rules: [
{
required: true,
message: 'Please enter username!',
},
],
},
Password: {
component: Input,
......@@ -22,9 +25,12 @@ const map = {
type: 'password',
placeholder: '888888',
},
rules: [{
required: true, message: 'Please enter password!',
}],
rules: [
{
required: true,
message: 'Please enter password!',
},
],
},
Mobile: {
component: Input,
......@@ -33,11 +39,16 @@ const map = {
prefix: <Icon type="mobile" className={styles.prefixIcon} />,
placeholder: 'mobile number',
},
rules: [{
required: true, message: 'Please enter mobile number!',
}, {
pattern: /^1\d{10}$/, message: 'Wrong mobile number format!',
}],
rules: [
{
required: true,
message: 'Please enter mobile number!',
},
{
pattern: /^1\d{10}$/,
message: 'Wrong mobile number format!',
},
],
},
Captcha: {
component: Input,
......@@ -46,9 +57,12 @@ const map = {
prefix: <Icon type="mail" className={styles.prefixIcon} />,
placeholder: 'captcha',
},
rules: [{
required: true, message: 'Please enter Captcha!',
}],
rules: [
{
required: true,
message: 'Please enter Captcha!',
},
],
},
};
......
......@@ -16,7 +16,4 @@ export interface INoticeIconTabProps {
style?: React.CSSProperties;
}
export default class NoticeIconTab extends React.Component<
INoticeIconTabProps,
any
> {}
export default class NoticeIconTab extends React.Component<INoticeIconTabProps, any> {}
......@@ -4,14 +4,18 @@ import classNames from 'classnames';
import styles from './NoticeList.less';
export default function NoticeList({
data = [], onClick, onClear, title, locale, emptyText, emptyImage,
data = [],
onClick,
onClear,
title,
locale,
emptyText,
emptyImage,
}) {
if (data.length === 0) {
return (
<div className={styles.notFound}>
{emptyImage ? (
<img src={emptyImage} alt="not found" />
) : null}
{emptyImage ? <img src={emptyImage} alt="not found" /> : null}
<div>{emptyText || locale.emptyText}</div>
</div>
);
......@@ -48,7 +52,8 @@ export default function NoticeList({
})}
</List>
<div className={styles.clear} onClick={onClear}>
{locale.clear}{title}
{locale.clear}
{title}
</div>
</div>
);
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.list {
max-height: 400px;
overflow: auto;
.item {
transition: all .3s;
transition: all 0.3s;
overflow: hidden;
cursor: pointer;
padding-left: 24px;
......@@ -20,7 +20,7 @@
}
&.read {
opacity: .4;
opacity: 0.4;
}
&:last-child {
border-bottom: 0;
......@@ -69,7 +69,7 @@
color: @text-color;
border-radius: 0 0 @border-radius-base @border-radius-base;
border-top: 1px solid @border-color-split;
transition: all .3s;
transition: all 0.3s;
cursor: pointer;
&:hover {
......
......@@ -30,19 +30,21 @@ export default class NoticeIcon extends PureComponent {
onItemClick = (item, tabProps) => {
const { onItemClick } = this.props;
onItemClick(item, tabProps);
}
onTabChange = (tabType) => {
};
onTabChange = tabType => {
this.setState({ tabType });
this.props.onTabChange(tabType);
}
};
getNotificationBox() {
const { children, loading, locale } = this.props;
if (!children) {
return null;
}
const panes = React.Children.map(children, (child) => {
const title = child.props.list && child.props.list.length > 0
? `${child.props.title} (${child.props.list.length})` : child.props.title;
const panes = React.Children.map(children, child => {
const title =
child.props.list && child.props.list.length > 0
? `${child.props.title} (${child.props.list.length})`
: child.props.title;
return (
<TabPane tab={title} key={child.props.title}>
<List
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.popover {
width: 336px;
......@@ -10,7 +10,7 @@
.noticeButton {
cursor: pointer;
display: inline-block;
transition: all .3s;
transition: all 0.3s;
}
.icon {
......
......@@ -10,7 +10,4 @@ export interface INumberInfoProps {
style?: React.CSSProperties;
}
export default class NumberInfo extends React.Component<
INumberInfoProps,
any
> {}
export default class NumberInfo extends React.Component<INumberInfoProps, any> {}
......@@ -3,15 +3,11 @@ import { Icon } from 'antd';
import classNames from 'classnames';
import styles from './index.less';
export default ({
theme, title, subTitle, total, subTotal, status, suffix, gap, ...rest
}) => (
export default ({ theme, title, subTitle, total, subTotal, status, suffix, gap, ...rest }) => (
<div
className={
classNames(styles.numberInfo, {
[styles[`numberInfo${theme}`]]: theme,
})
}
className={classNames(styles.numberInfo, {
[styles[`numberInfo${theme}`]]: theme,
})}
{...rest}
>
{title && <div className={styles.numberInfoTitle}>{title}</div>}
......@@ -21,14 +17,12 @@ export default ({
{total}
{suffix && <em className={styles.suffix}>{suffix}</em>}
</span>
{
(status || subTotal) && (
<span className={styles.subTotal}>
{subTotal}
{status && <Icon type={`caret-${status}`} />}
</span>
)
}
{(status || subTotal) && (
<span className={styles.subTotal}>
{subTotal}
{status && <Icon type={`caret-${status}`} />}
</span>
)}
</div>
</div>
);
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.numberInfo {
.suffix {
......@@ -11,7 +11,7 @@
color: @text-color;
font-size: @font-size-lg;
margin-bottom: 16px;
transition: all .3s;
transition: all 0.3s;
}
.numberInfoSubTitle {
color: @text-color-secondary;
......
......@@ -10,7 +10,7 @@ const { TabPane } = Tabs;
export function getBreadcrumb(breadcrumbNameMap, url) {
let breadcrumb = breadcrumbNameMap[url];
if (!breadcrumb) {
Object.keys(breadcrumbNameMap).forEach((item) => {
Object.keys(breadcrumbNameMap).forEach(item => {
if (pathToRegexp(item).test(url)) {
breadcrumb = breadcrumbNameMap[item];
}
......@@ -26,7 +26,7 @@ export default class PageHeader extends PureComponent {
location: PropTypes.object,
breadcrumbNameMap: PropTypes.object,
};
onChange = (key) => {
onChange = key => {
if (this.props.onTabChange) {
this.props.onTabChange(key);
}
......@@ -36,17 +36,12 @@ export default class PageHeader extends PureComponent {
routes: this.props.routes || this.context.routes,
params: this.props.params || this.context.params,
routerLocation: this.props.location || this.context.location,
breadcrumbNameMap:
this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
breadcrumbNameMap: this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
};
};
// Generated according to props
conversionFromProps = () => {
const {
breadcrumbList,
breadcrumbSeparator,
linkElement = 'a',
} = this.props;
const { breadcrumbList, breadcrumbSeparator, linkElement = 'a' } = this.props;
return (
<Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
{breadcrumbList.map(item => (
......@@ -57,7 +52,7 @@ export default class PageHeader extends PureComponent {
{
[linkElement === 'a' ? 'href' : 'to']: item.href,
},
item.title,
item.title
)
: item.title}
</Breadcrumb.Item>
......@@ -72,14 +67,13 @@ export default class PageHeader extends PureComponent {
// Loop data mosaic routing
const extraBreadcrumbItems = pathSnippets.map((url, index) => {
const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
const isLinkable =
index !== pathSnippets.length - 1 && currentBreadcrumb.component;
const isLinkable = index !== pathSnippets.length - 1 && currentBreadcrumb.component;
return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
<Breadcrumb.Item key={url}>
{createElement(
isLinkable ? linkElement : 'span',
{ [linkElement === 'a' ? 'href' : 'to']: url },
currentBreadcrumb.name,
currentBreadcrumb.name
)}
</Breadcrumb.Item>
) : null;
......@@ -92,9 +86,9 @@ export default class PageHeader extends PureComponent {
{
[linkElement === 'a' ? 'href' : 'to']: '/',
},
'首页',
'首页'
)}
</Breadcrumb.Item>,
</Breadcrumb.Item>
);
return (
<Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
......@@ -108,12 +102,7 @@ export default class PageHeader extends PureComponent {
*/
conversionBreadcrumbList = () => {
const { breadcrumbList, breadcrumbSeparator } = this.props;
const {
routes,
params,
routerLocation,
breadcrumbNameMap,
} = this.getBreadcrumbProps();
const { routes, params, routerLocation, breadcrumbNameMap } = this.getBreadcrumbProps();
if (breadcrumbList && breadcrumbList.length) {
return this.conversionFromProps();
}
......@@ -151,7 +140,7 @@ export default class PageHeader extends PureComponent {
href: paths.join('/') || '/',
to: paths.join('/') || '/',
},
route.breadcrumbName,
route.breadcrumbName
)
);
};
......@@ -191,9 +180,7 @@ export default class PageHeader extends PureComponent {
</div>
<div className={styles.row}>
{content && <div className={styles.content}>{content}</div>}
{extraContent && (
<div className={styles.extraContent}>{extraContent}</div>
)}
{extraContent && <div className={styles.extraContent}>{extraContent}</div>}
</div>
</div>
</div>
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.pageHeader {
background: @component-background;
......@@ -61,19 +61,27 @@
}
}
.title, .action, .content, .extraContent, .main {
.title,
.action,
.content,
.extraContent,
.main {
flex: auto;
}
.title, .action {
.title,
.action {
margin-bottom: 16px;
}
.logo, .content, .extraContent {
.logo,
.content,
.extraContent {
margin-bottom: 16px;
}
.action, .extraContent {
.action,
.extraContent {
text-align: right;
}
......@@ -105,7 +113,8 @@
display: block;
}
.action, .extraContent {
.action,
.extraContent {
margin-left: 0;
text-align: left;
}
......@@ -124,7 +133,8 @@
.pageHeader {
.action {
:global {
.ant-btn-group, .ant-btn {
.ant-btn-group,
.ant-btn {
display: block;
margin-bottom: 8px;
}
......
......@@ -17,35 +17,29 @@ const routerData = {
};
describe('test getBreadcrumb', () => {
it('Simple url', () => {
expect(getBreadcrumb(routerData, '/dashboard/analysis').name).toEqual(
'分析页',
);
expect(getBreadcrumb(routerData, '/dashboard/analysis').name).toEqual('分析页');
});
it('Parameters url', () => {
expect(getBreadcrumb(routerData, '/userinfo/2144').name).toEqual(
'用户信息',
);
expect(getBreadcrumb(routerData, '/userinfo/2144').name).toEqual('用户信息');
});
it('The middle parameter url', () => {
expect(getBreadcrumb(routerData, '/userinfo/2144/addr').name).toEqual(
'收货订单',
);
expect(getBreadcrumb(routerData, '/userinfo/2144/addr').name).toEqual('收货订单');
});
it('Loop through the parameters', () => {
const urlNameList = urlToList('/userinfo/2144/addr').map((url) => {
const urlNameList = urlToList('/userinfo/2144/addr').map(url => {
return getBreadcrumb(routerData, url).name;
});
expect(urlNameList).toEqual(['用户列表', '用户信息', '收货订单']);
});
it('a path', () => {
const urlNameList = urlToList('/userinfo').map((url) => {
const urlNameList = urlToList('/userinfo').map(url => {
return getBreadcrumb(routerData, url).name;
});
expect(urlNameList).toEqual(['用户列表']);
});
it('Secondary path', () => {
const urlNameList = urlToList('/userinfo/2144').map((url) => {
const urlNameList = urlToList('/userinfo/2144').map(url => {
return getBreadcrumb(routerData, url).name;
});
expect(urlNameList).toEqual(['用户列表', '用户信息']);
......
......@@ -4,7 +4,13 @@ import { Icon } from 'antd';
import styles from './index.less';
export default function Result({
className, type, title, description, extra, actions, ...restProps
className,
type,
title,
description,
extra,
actions,
...restProps
}) {
const iconMap = {
error: <Icon className={styles.error} type="close-circle" />,
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.result {
text-align: center;
......
......@@ -12,7 +12,7 @@ const { SubMenu } = Menu;
// icon: 'setting',
// icon: 'http://demo.com/icon.png',
// icon: <Icon type="setting" />,
const getIcon = (icon) => {
const getIcon = icon => {
if (typeof icon === 'string' && icon.indexOf('http') === 0) {
return <img src={icon} alt="icon" className={`${styles.icon} sider-menu-item-img`} />;
}
......@@ -23,7 +23,7 @@ const getIcon = (icon) => {
};
export const getMeunMatcheys = (flatMenuKeys, path) => {
return flatMenuKeys.filter((item) => {
return flatMenuKeys.filter(item => {
return pathToRegexp(item).test(path);
});
};
......@@ -52,7 +52,7 @@ export default class SiderMenu extends PureComponent {
getDefaultCollapsedSubMenus(props) {
const { location: { pathname } } = props || this.props;
return urlToList(pathname)
.map((item) => {
.map(item => {
return getMeunMatcheys(this.flatMenuKeys, item)[0];
})
.filter(item => item);
......@@ -64,7 +64,7 @@ export default class SiderMenu extends PureComponent {
*/
getFlatMenuKeys(menus) {
let keys = [];
menus.forEach((item) => {
menus.forEach(item => {
if (item.children) {
keys = keys.concat(this.getFlatMenuKeys(item.children));
}
......@@ -77,7 +77,7 @@ export default class SiderMenu extends PureComponent {
* Judge whether it is http link.return a or Link
* @memberof SiderMenu
*/
getMenuItemPath = (item) => {
getMenuItemPath = item => {
const itemPath = this.conversionPath(item.path);
const icon = getIcon(item.icon);
const { target, name } = item;
......@@ -111,7 +111,7 @@ export default class SiderMenu extends PureComponent {
/**
* get SubMenu or Item
*/
getSubMenuOrItem = (item) => {
getSubMenuOrItem = item => {
if (item.children && item.children.some(child => child.name)) {
const childrenItems = this.getNavMenuItems(item.children);
// 当无子菜单时就不展示菜单
......@@ -125,8 +125,8 @@ export default class SiderMenu extends PureComponent {
<span>{item.name}</span>
</span>
) : (
item.name
)
item.name
)
}
key={item.path}
>
......@@ -136,22 +136,20 @@ export default class SiderMenu extends PureComponent {
}
return null;
} else {
return (
<Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>
);
return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
}
};
/**
* 获得菜单子节点
* @memberof SiderMenu
*/
getNavMenuItems = (menusData) => {
getNavMenuItems = menusData => {
if (!menusData) {
return [];
}
return menusData
.filter(item => item.name && !item.hideInMenu)
.map((item) => {
.map(item => {
// make dom
const ItemDom = this.getSubMenuOrItem(item);
return this.checkPermissionItem(item.authority, ItemDom);
......@@ -161,13 +159,11 @@ export default class SiderMenu extends PureComponent {
// Get the currently selected menu
getSelectedMenuKeys = () => {
const { location: { pathname } } = this.props;
return urlToList(pathname).map(itemPath =>
getMeunMatcheys(this.flatMenuKeys, itemPath).pop(),
);
return urlToList(pathname).map(itemPath => getMeunMatcheys(this.flatMenuKeys, itemPath).pop());
};
// conversion Path
// 转化路径
conversionPath = (path) => {
conversionPath = path => {
if (path && path.indexOf('http') === 0) {
return path;
} else {
......@@ -182,13 +178,10 @@ export default class SiderMenu extends PureComponent {
}
return ItemDom;
};
isMainMenu = (key) => {
return this.menus.some(
item =>
key && (item.key === key || item.path === key),
);
}
handleOpenChange = (openKeys) => {
isMainMenu = key => {
return this.menus.some(item => key && (item.key === key || item.path === key));
};
handleOpenChange = openKeys => {
const lastOpenKey = openKeys[openKeys.length - 1];
const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1;
this.setState({
......@@ -202,8 +195,8 @@ export default class SiderMenu extends PureComponent {
const menuProps = collapsed
? {}
: {
openKeys,
};
openKeys,
};
// if pathname can't match, use the nearest parent's key
let selectedKeys = this.getSelectedMenuKeys();
if (!selectedKeys.length) {
......
import { getMeunMatcheys } from './SiderMenu';
const meun = [
'/dashboard',
'/userinfo',
'/dashboard/name',
'/userinfo/:id',
'/userinfo/:id/info',
];
const meun = ['/dashboard', '/userinfo', '/dashboard/name', '/userinfo/:id', '/userinfo/:id/info'];
describe('test meun match', () => {
it('simple path', () => {
......@@ -17,20 +11,14 @@ describe('test meun match', () => {
});
it('Secondary path', () => {
expect(getMeunMatcheys(meun, '/dashboard/name')).toEqual([
'/dashboard/name',
]);
expect(getMeunMatcheys(meun, '/dashboard/name')).toEqual(['/dashboard/name']);
});
it('Parameter path', () => {
expect(getMeunMatcheys(meun, '/userinfo/2144')).toEqual([
'/userinfo/:id',
]);
expect(getMeunMatcheys(meun, '/userinfo/2144')).toEqual(['/userinfo/:id']);
});
it('three parameter path', () => {
expect(getMeunMatcheys(meun, '/userinfo/2144/info')).toEqual([
'/userinfo/:id/info',
]);
expect(getMeunMatcheys(meun, '/userinfo/2144/info')).toEqual(['/userinfo/:id/info']);
});
});
......@@ -3,17 +3,20 @@ import React from 'react';
import DrawerMenu from 'rc-drawer-menu';
import SiderMenu from './SiderMenu';
export default props => (
export default props =>
props.isMobile ? (
<DrawerMenu
parent={null}
level={null}
iconChild={null}
open={!props.collapsed}
onMaskClick={() => { props.onCollapse(true); }}
onMaskClick={() => {
props.onCollapse(true);
}}
width="256px"
>
<SiderMenu {...props} collapsed={props.isMobile ? false : props.collapsed} />
</DrawerMenu>
) : <SiderMenu {...props} />
);
) : (
<SiderMenu {...props} />
);
......@@ -51,7 +51,12 @@
}
.ant-menu-inline-collapsed {
& > .ant-menu-item .sider-menu-item-img + span,
& > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item .sider-menu-item-img + span,
&
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-item
.sider-menu-item-img
+ span,
& > .ant-menu-submenu > .ant-menu-submenu-title .sider-menu-item-img + span {
max-width: 0;
display: inline-block;
......@@ -60,7 +65,7 @@
}
.ant-menu-item .sider-menu-item-img + span,
.ant-menu-submenu-title .sider-menu-item-img + span {
transition: opacity .3s @ease-in-out, width .3s @ease-in-out;
transition: opacity 0.3s @ease-in-out, width 0.3s @ease-in-out;
opacity: 1;
}
}
......@@ -11,16 +11,12 @@ export default ({ title, children, last, block, grid, ...rest }) => {
return (
<div className={cls} {...rest}>
{
title && (
<div className={styles.label}>
<span>{title}</span>
</div>
)
}
<div className={styles.content}>
{children}
</div>
{title && (
<div className={styles.label}>
<span>{title}</span>
</div>
)}
<div className={styles.content}>{children}</div>
</div>
);
};
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.standardFormRow {
border-bottom: 1px dashed @border-color-split;
......
......@@ -4,7 +4,7 @@ import styles from './index.less';
function initTotalList(columns) {
const totalList = [];
columns.forEach((column) => {
columns.forEach(column => {
if (column.needTotal) {
totalList.push({ ...column, total: 0 });
}
......@@ -37,7 +37,7 @@ class StandardTable extends PureComponent {
handleRowSelectChange = (selectedRowKeys, selectedRows) => {
let needTotalList = [...this.state.needTotalList];
needTotalList = needTotalList.map((item) => {
needTotalList = needTotalList.map(item => {
return {
...item,
total: selectedRows.reduce((sum, val) => {
......@@ -51,15 +51,15 @@ class StandardTable extends PureComponent {
}
this.setState({ selectedRowKeys, needTotalList });
}
};
handleTableChange = (pagination, filters, sorter) => {
this.props.onChange(pagination, filters, sorter);
}
};
cleanSelectedKeys = () => {
this.handleRowSelectChange([], []);
}
};
render() {
const { selectedRowKeys, needTotalList } = this.state;
......@@ -83,22 +83,22 @@ class StandardTable extends PureComponent {
<div className={styles.standardTable}>
<div className={styles.tableAlert}>
<Alert
message={(
message={
<Fragment>
已选择 <a style={{ fontWeight: 600 }}>{selectedRowKeys.length}</a> 项&nbsp;&nbsp;
{
needTotalList.map(item => (
<span style={{ marginLeft: 8 }} key={item.dataIndex}>{item.title}总计&nbsp;
<span style={{ fontWeight: 600 }}>
{item.render ? item.render(item.total) : item.total}
</span>
{needTotalList.map(item => (
<span style={{ marginLeft: 8 }} key={item.dataIndex}>
{item.title}总计&nbsp;
<span style={{ fontWeight: 600 }}>
{item.render ? item.render(item.total) : item.total}
</span>
)
)
}
<a onClick={this.cleanSelectedKeys} style={{ marginLeft: 24 }}>清空</a>
</span>
))}
<a onClick={this.cleanSelectedKeys} style={{ marginLeft: 24 }}>
清空
</a>
</Fragment>
)}
}
type="info"
showIcon
/>
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.standardTable {
:global {
......
......@@ -5,7 +5,4 @@ export interface ITagSelectOptionProps {
style?: React.CSSProperties;
}
export default class TagSelectOption extends React.Component<
ITagSelectOptionProps,
any
> {}
export default class TagSelectOption extends React.Component<ITagSelectOptionProps, any> {}
......@@ -7,11 +7,7 @@ import styles from './index.less';
const { CheckableTag } = Tag;
const TagSelectOption = ({ children, checked, onChange, value }) => (
<CheckableTag
checked={checked}
key={value}
onChange={state => onChange(value, state)}
>
<CheckableTag checked={checked} key={value} onChange={state => onChange(value, state)}>
{children}
</CheckableTag>
);
......@@ -29,7 +25,7 @@ class TagSelect extends Component {
}
}
onChange = (value) => {
onChange = value => {
const { onChange } = this.props;
if (!('value' in this.props)) {
this.setState({ value });
......@@ -37,15 +33,15 @@ class TagSelect extends Component {
if (onChange) {
onChange(value);
}
}
};
onSelectAll = (checked) => {
onSelectAll = checked => {
let checkedTags = [];
if (checked) {
checkedTags = this.getAllTags();
}
this.onChange(checkedTags);
}
};
getAllTags() {
let { children } = this.props;
......@@ -66,19 +62,21 @@ class TagSelect extends Component {
checkedTags.splice(index, 1);
}
this.onChange(checkedTags);
}
};
handleExpand = () => {
this.setState({
expand: !this.state.expand,
});
}
};
isTagSelectOption = (node) => {
return node && node.type && (
node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption'
isTagSelectOption = node => {
return (
node &&
node.type &&
(node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption')
);
}
};
render() {
const { value, expand } = this.state;
......@@ -92,15 +90,11 @@ class TagSelect extends Component {
});
return (
<div className={cls} style={style}>
<CheckableTag
checked={checkedAll}
key="tag-select-__all__"
onChange={this.onSelectAll}
>
<CheckableTag checked={checkedAll} key="tag-select-__all__" onChange={this.onSelectAll}>
全部
</CheckableTag>
{
value && React.Children.map(children, (child) => {
{value &&
React.Children.map(children, child => {
if (this.isTagSelectOption(child)) {
return React.cloneElement(child, {
key: `tag-select-${child.props.value}`,
......@@ -110,15 +104,12 @@ class TagSelect extends Component {
});
}
return child;
})
}
{
expandable && (
<a className={styles.trigger} onClick={this.handleExpand}>
{expand ? '收起' : '展开'} <Icon type={expand ? 'up' : 'down'} />
</a>
)
}
})}
{expandable && (
<a className={styles.trigger} onClick={this.handleExpand}>
{expand ? '收起' : '展开'} <Icon type={expand ? 'up' : 'down'} />
</a>
)}
</div>
);
}
......
@import "~antd/lib/style/themes/default.less";
@import '~antd/lib/style/themes/default.less';
.tagSelect {
user-select: none;
......@@ -7,7 +7,7 @@
overflow: hidden;
max-height: 32px;
line-height: 32px;
transition: all .3s;
transition: all 0.3s;
:global {
.ant-tag {
padding: 0 8px;
......@@ -16,7 +16,7 @@
}
}
&.expanded {
transition: all .3s;
transition: all 0.3s;
max-height: 200px;
}
.trigger {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment