├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .stylelintrc.json
├── .umirc.js
├── LICENSE
├── README.md
├── _scripts
├── BlockGenerator
│ ├── index.js
│ ├── package.json
│ └── templates
│ │ ├── package.json
│ │ └── src
│ │ ├── _mock.js
│ │ ├── index.js
│ │ ├── model.js
│ │ ├── service.js
│ │ └── style.less
├── build.js
├── create.js
├── lint-prettier.js
└── prettier.js
├── blocks
├── .DS_Store
├── demo
│ ├── package.json
│ ├── snapshot.png
│ └── src
│ │ ├── index.js
│ │ └── index.less
├── layout-antd-col-12-12
│ ├── package.json
│ ├── snapshot.png
│ └── src
│ │ └── index.js
├── layout-holy-grail
│ ├── package.json
│ ├── snapshot.png
│ └── src
│ │ ├── index.js
│ │ └── index.less
└── table
│ ├── .DS_Store
│ ├── package.json
│ ├── snapshot.png
│ └── src
│ ├── index.tsx
│ └── typing.d.ts
├── jsconfig.json
├── now.json
├── package.json
├── plugin.js
├── templates
├── blank
│ ├── package.json
│ ├── snapshot.png
│ └── src
│ │ ├── _mock.js
│ │ ├── index.js
│ │ ├── model.js
│ │ ├── service.js
│ │ └── style.less
└── user-dashboard
│ ├── package.json
│ ├── snapshot.png
│ └── src
│ ├── _mock.js
│ ├── components
│ ├── UserModal.js
│ ├── Users.css
│ └── Users.js
│ ├── index.js
│ ├── model.js
│ ├── service.js
│ └── utils
│ └── constants.js
├── tsconfig.json
└── typings.d.ts
/.eslintignore:
--------------------------------------------------------------------------------
1 | /functions/mock/**
2 | /scripts
3 | /config
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [require.resolve('@umijs/fabric/dist/eslint')],
3 | rules: {
4 | 'import/no-extraneous-dependencies': 0,
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # npm related
3 | /yarn.lock
4 | /node_modules
5 | /blocks/**/node_modules
6 | /blocks/**/yarn.lock
7 | /templates/**/node_modules
8 | /templates/**/yarn.lock
9 |
10 | # doc
11 | /dist
12 |
13 | # umi related
14 | .umi
15 | .umi-production
16 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.md
2 | **/*.svg
3 | **/*.ejs
4 | **/*.html
5 | package.json
6 | .umi
7 | .umi-production
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "es5",
4 | "printWidth": 100,
5 | "overrides": [
6 | {
7 | "files": ".prettierrc",
8 | "options": { "parser": "json" }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard", "stylelint-config-prettier"],
3 | "rules": {
4 | "declaration-empty-line-before": null,
5 | "no-descending-specificity": null,
6 | "selector-pseudo-class-no-unknown": null,
7 | "selector-pseudo-element-colon-notation": null
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.umirc.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: [
3 | [
4 | 'umi-plugin-block-dev',
5 | {
6 | layout: process.env.LAYOUT || 'ant-design-pro',
7 | menu: {
8 | name: process.env.BLOCK,
9 | icon: 'home',
10 | },
11 | },
12 | ],
13 | [
14 | 'umi-plugin-react',
15 | {
16 | dva: true,
17 | antd: true,
18 | },
19 | ],
20 | // require.resolve('./plugin'),
21 | ],
22 | };
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 umijs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # umi-blocks
2 |
3 | Official block collection from umi community.
4 |
5 | ## Usage
6 |
7 | ### Install Dependencies
8 |
9 | ```bash
10 | $ yarn
11 | ```
12 |
13 | ### View Block or Template
14 |
15 | > Specify the block via BLOCK env.
16 |
17 | ```bash
18 | yarn start templates/user-dashboard
19 | ```
20 |
21 | ### Create Block
22 |
23 | ```bash
24 | # Create
25 | $ BLOCK=templates/my-template yarn run create
26 |
27 | # Debug
28 | $ BLOCK=templates/my-template yarn start
29 | ```
30 |
31 | You can visit umi [doc](https://umijs.org/guide/block.html) for find out more info about umi block.
32 |
33 | If you are done, make a PR for this.
34 |
35 | ## LICENSE
36 |
37 | MIT
38 |
--------------------------------------------------------------------------------
/_scripts/BlockGenerator/index.js:
--------------------------------------------------------------------------------
1 | const Generator = require('yeoman-generator');
2 | const { statSync } = require('fs');
3 | const glob = require('glob');
4 |
5 | class BlockGenerator extends Generator {
6 | writing() {
7 | glob
8 | .sync('**/*', {
9 | cwd: this.templatePath(),
10 | dot: true,
11 | })
12 | .forEach(file => {
13 | const filePath = this.templatePath(file);
14 | if (statSync(filePath).isFile()) {
15 | this.fs.copyTpl(
16 | this.templatePath(filePath),
17 | this.destinationPath(file.replace(/^_/, '.')),
18 | {
19 | name: process.env.BLOCK.split('/')[1],
20 | }
21 | );
22 | }
23 | });
24 | }
25 | }
26 |
27 | module.exports = BlockGenerator;
28 |
--------------------------------------------------------------------------------
/_scripts/BlockGenerator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "block-generator"
3 | }
4 |
--------------------------------------------------------------------------------
/_scripts/BlockGenerator/templates/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= name %>",
3 | "description": "",
4 | "dependencies": {
5 | "umi-request": "^1.0.0"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/_scripts/BlockGenerator/templates/src/_mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'GET /api/BLOCK_NAME/text': {
3 | text: 'I am a blank block',
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/_scripts/BlockGenerator/templates/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'antd';
3 | import { connect } from 'dva';
4 |
5 | import styles from './style.less';
6 |
7 | @connect(({ BLOCK_NAME_CAMEL_CASE }) => BLOCK_NAME_CAMEL_CASE)
8 | class Page extends Component {
9 | componentDidMount() {
10 | const { dispatch } = this.props;
11 | dispatch({
12 | type: 'BLOCK_NAME_CAMEL_CASE/fetch',
13 | });
14 | }
15 |
16 | render() {
17 | const { text } = this.props;
18 | return (
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | export default Page;
27 |
--------------------------------------------------------------------------------
/_scripts/BlockGenerator/templates/src/model.js:
--------------------------------------------------------------------------------
1 | import { getText } from './service';
2 |
3 | export default {
4 | namespace: 'BLOCK_NAME_CAMEL_CASE',
5 | state: {
6 | text: 'loading...',
7 | },
8 |
9 | effects: {
10 | *fetch(_, { call, put }) {
11 | const { text } = yield call(getText);
12 | yield put({
13 | type: 'save',
14 | payload: {
15 | text,
16 | },
17 | });
18 | },
19 | },
20 |
21 | reducers: {
22 | save(state, { payload }) {
23 | return {
24 | ...state,
25 | ...payload,
26 | };
27 | },
28 | },
29 | };
30 |
--------------------------------------------------------------------------------
/_scripts/BlockGenerator/templates/src/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export function getText() {
4 | return request('/api/BLOCK_NAME/text');
5 | }
6 |
--------------------------------------------------------------------------------
/_scripts/BlockGenerator/templates/src/style.less:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 8px;
3 | }
4 |
--------------------------------------------------------------------------------
/_scripts/build.js:
--------------------------------------------------------------------------------
1 | const { readdirSync, readFileSync, writeFileSync, existsSync } = require('fs');
2 | const { join, basename } = require('path');
3 | const mkdirp = require('mkdirp');
4 |
5 | function haveDependencies(pkg, depName) {
6 | if (pkg.dependencies && pkg.dependencies[depName]) {
7 | return true;
8 | }
9 | if (pkg.devDependencies && pkg.devDependencies[depName]) {
10 | return true;
11 | }
12 | return false;
13 | }
14 |
15 | const EXT_NAMES = ['.tsx', '.ts', '.jsx', '.js'];
16 |
17 | function getFile(cwd, fileName) {
18 | for (const ext of EXT_NAMES) {
19 | const file = join(cwd, `${fileName}${ext}`);
20 | if (existsSync(file)) {
21 | return file;
22 | }
23 | }
24 | }
25 |
26 | function haveImport(cwd, name) {
27 | const indexFile = getFile(cwd, 'src/index');
28 | if (!indexFile) return false;
29 | const content = readFileSync(indexFile, 'utf-8');
30 | return content.includes(`'${name}'`) || content.includes(`"${name}"`);
31 | }
32 |
33 | function parseJSON(root) {
34 | const dirs = readdirSync(root);
35 | const type = basename(root);
36 | const list = dirs.reduce((memo = [], dir) => {
37 | if (dir.charAt(0) === '.') return;
38 | const absDirPath = join(root, dir);
39 | const pkg = require(join(absDirPath, 'package.json'));
40 | const url = `https://github.com/umijs/umi-blocks/tree/master/${type}/${dir}`;
41 | const img = `https://github.com/umijs/umi-blocks/blob/master/${type}/${dir}/snapshot.png?raw=true`;
42 | const features = [];
43 | if (haveDependencies(pkg, 'antd') || haveImport(absDirPath, 'antd')) {
44 | features.push('antd');
45 | }
46 | if (getFile(absDirPath, 'src/model')) {
47 | features.push('dva');
48 | }
49 | memo.push({
50 | name: pkg.name,
51 | description: pkg.description,
52 | url,
53 | tags: [],
54 | img,
55 | previewUrl: '',
56 | features,
57 | ...(pkg.block && pkg.block.category ? { category: pkg.block.category } : {}),
58 | });
59 | return memo;
60 | }, []);
61 | return { list };
62 | }
63 |
64 | function generate(root) {
65 | const dist = join(root, '..', 'dist');
66 | const type = basename(root);
67 |
68 | console.log(`Generate json for ${type}`);
69 | mkdirp.sync(dist);
70 |
71 | const json = parseJSON(root);
72 | writeFileSync(join(dist, `${type}.json`), JSON.stringify(json, null, 2), 'utf-8');
73 | }
74 |
75 | generate(join(__dirname, '..', 'templates'));
76 | generate(join(__dirname, '..', 'blocks'));
77 |
--------------------------------------------------------------------------------
/_scripts/create.js:
--------------------------------------------------------------------------------
1 | const { join } = require('path');
2 | const BlockGenerator = require('./BlockGenerator');
3 |
4 | if (!process.env.BLOCK) {
5 | console.log(`
6 | Please specify the BLOCK env,
7 |
8 | e.g.
9 |
10 | $ BLOCK=templates/new-template yarn run create
11 | $ BLOCK=blocks/new-block yarn run create
12 | `);
13 | process.exit(1);
14 | }
15 |
16 | const generator = new BlockGenerator({
17 | name: 'foo',
18 | env: {
19 | cwd: join(__dirname, '..', process.env.BLOCK),
20 | },
21 | resolved: require.resolve(require.resolve('./BlockGenerator')),
22 | });
23 |
24 | generator.run(() => {
25 | console.log('done');
26 | });
27 |
--------------------------------------------------------------------------------
/_scripts/lint-prettier.js:
--------------------------------------------------------------------------------
1 | /**
2 | * copy to https://github.com/facebook/react/blob/master/scripts/prettier/index.js
3 | * prettier api doc https://prettier.io/docs/en/api.html
4 | *----------*****--------------
5 | * lint file is prettier
6 | *----------*****--------------
7 | */
8 |
9 | const prettier = require('prettier');
10 | const fs = require('fs');
11 |
12 | const prettierConfigPath = require.resolve('../.prettierrc');
13 |
14 | const files = process.argv.slice(2);
15 |
16 | let didError = false;
17 | let didWarn = false;
18 |
19 | files.forEach(file => {
20 | const options = prettier.resolveConfig.sync(file, {
21 | config: prettierConfigPath,
22 | });
23 | try {
24 | const fileInfo = prettier.getFileInfo.sync(file);
25 | if (fileInfo.ignored) {
26 | return;
27 | }
28 | const input = fs.readFileSync(file, 'utf8');
29 | const withParserOptions = {
30 | ...options,
31 | parser: fileInfo.inferredParser,
32 | };
33 | const isPrettier = prettier.check(input, withParserOptions);
34 | if (!isPrettier) {
35 | console.log(
36 | `\x1b[31m ${file} is no prettier, please use npm run prettier and git add !\x1b[0m`
37 | );
38 | didWarn = true;
39 | }
40 | } catch (e) {
41 | didError = true;
42 | }
43 | });
44 |
45 | if (didWarn || didError) {
46 | process.exit(1);
47 | }
48 | console.log('\x1b[32m lint prettier success!\x1b[0m');
49 |
--------------------------------------------------------------------------------
/_scripts/prettier.js:
--------------------------------------------------------------------------------
1 | /**
2 | * copy to https://github.com/facebook/react/blob/master/scripts/prettier/index.js
3 | * prettier api doc https://prettier.io/docs/en/api.html
4 | *----------*****--------------
5 | * prettier all js and all ts.
6 | *----------*****--------------
7 | */
8 |
9 | const glob = require('glob');
10 | const prettier = require('prettier');
11 | const fs = require('fs');
12 |
13 | const prettierConfigPath = require.resolve('../.prettierrc');
14 |
15 | let didError = false;
16 |
17 | let files = [];
18 | const jsFiles = glob.sync('ant-design-pro/**/*.js*', {
19 | ignore: ['**/node_modules/**', 'build/**'],
20 | });
21 | const tsFiles = glob.sync('ant-design-pro/**/*.ts*', {
22 | ignore: ['**/node_modules/**', 'build/**'],
23 | });
24 | files = files.concat(jsFiles);
25 | files = files.concat(tsFiles);
26 | if (!files.length) {
27 | return;
28 | }
29 |
30 | files.forEach(file => {
31 | const options = prettier.resolveConfig.sync(file, {
32 | config: prettierConfigPath,
33 | });
34 | const fileInfo = prettier.getFileInfo.sync(file);
35 | if (fileInfo.ignored) {
36 | return;
37 | }
38 | try {
39 | const input = fs.readFileSync(file, 'utf8');
40 | const withParserOptions = {
41 | ...options,
42 | parser: fileInfo.inferredParser,
43 | };
44 | const output = prettier.format(input, withParserOptions);
45 | if (output !== input) {
46 | fs.writeFileSync(file, output, 'utf8');
47 | console.log(`\x1b[34m ${file} is prettier`);
48 | }
49 | } catch (e) {
50 | didError = true;
51 | }
52 | });
53 |
54 | if (didError) {
55 | process.exit(1);
56 | }
57 | console.log('\x1b[32m prettier success!');
58 |
--------------------------------------------------------------------------------
/blocks/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/umi-blocks/f73b2e5a9dc082deba64c7bfbe426bc75f5f5fa0/blocks/.DS_Store
--------------------------------------------------------------------------------
/blocks/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Demo",
3 | "description": "Demo block of umi, with antd.",
4 | "dependencies": {
5 | "antd": "^3.10.9"
6 | },
7 | "block": {
8 | "category": "脚手架"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/blocks/demo/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/umi-blocks/f73b2e5a9dc082deba64c7bfbe426bc75f5f5fa0/blocks/demo/snapshot.png
--------------------------------------------------------------------------------
/blocks/demo/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button } from 'antd';
3 | import styles from './index.less';
4 |
5 | export default () => {
6 | return ;
7 | };
8 |
--------------------------------------------------------------------------------
/blocks/demo/src/index.less:
--------------------------------------------------------------------------------
1 | .container {
2 | color: blue;
3 | }
4 |
--------------------------------------------------------------------------------
/blocks/layout-antd-col-12-12/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "两栏布局 Col 12-12",
3 | "description": "Antd 的 col-12-12 布局。",
4 | "dependencies": {
5 | "antd": "^3.10.9"
6 | },
7 | "block": {
8 | "category": "布局"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/blocks/layout-antd-col-12-12/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/umi-blocks/f73b2e5a9dc082deba64c7bfbe426bc75f5f5fa0/blocks/layout-antd-col-12-12/snapshot.png
--------------------------------------------------------------------------------
/blocks/layout-antd-col-12-12/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Row, Col } from 'antd';
3 |
4 | export default () => {
5 | return (
6 |
7 |
8 | INSERT_BLOCK_PLACEHOLDER:Col 12
9 |
10 |
11 | INSERT_BLOCK_PLACEHOLDER:Col 12
12 |
13 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/blocks/layout-holy-grail/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "圣杯布局",
3 | "description": "包含简单的响应式。",
4 | "block": {
5 | "category": "布局"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/blocks/layout-holy-grail/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/umi-blocks/f73b2e5a9dc082deba64c7bfbe426bc75f5f5fa0/blocks/layout-holy-grail/snapshot.png
--------------------------------------------------------------------------------
/blocks/layout-holy-grail/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './index.less';
3 |
4 | export default function() {
5 | return (
6 |
7 |
INSERT_BLOCK_PLACEHOLDER:HEADER
8 |
9 |
INSERT_BLOCK_PLACEHOLDER:CONTENT
10 |
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/blocks/layout-holy-grail/src/index.less:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | min-height: 100vh;
4 | flex-direction: column;
5 |
6 | header,
7 | footer {
8 | background: #82caff;
9 | flex: 0 0 6em;
10 | }
11 | }
12 |
13 | .body {
14 | display: flex;
15 | flex: 1;
16 | flex-direction: column;
17 |
18 | main {
19 | flex: 1;
20 | background: #fecccc;
21 | }
22 | aside,
23 | nav {
24 | flex: 0 0 12em;
25 | background: #c9c;
26 | }
27 | nav {
28 | order: -1;
29 | }
30 | }
31 |
32 | @media (min-width: 768px) {
33 | .body {
34 | flex-direction: row;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/blocks/table/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/umi-blocks/f73b2e5a9dc082deba64c7bfbe426bc75f5f5fa0/blocks/table/.DS_Store
--------------------------------------------------------------------------------
/blocks/table/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "table",
3 | "description": "查询表格是每一个项目都需要的基本页面。",
4 | "dependencies": {
5 | "@ant-design/pro-table": "^1.0.31",
6 | "@umijs/ui-flag": "1.0.2",
7 | "antd": "^3.10.9"
8 | },
9 | "peerDependencies": {
10 | "react": "^16.8.6",
11 | "react-dom": "^16.8.6"
12 | },
13 | "devDependencies": {
14 | "umi": "^2.13.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/blocks/table/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/umi-blocks/f73b2e5a9dc082deba64c7bfbe426bc75f5f5fa0/blocks/table/snapshot.png
--------------------------------------------------------------------------------
/blocks/table/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Divider, Dropdown, Form, Icon, Menu } from 'antd';
2 | import React, { useState, useRef } from 'react';
3 | import ProTable, { ProColumns, ActionType } from '@ant-design/pro-table';
4 | import UmiUIFlag from '@umijs/ui-flag';
5 | import { FormComponentProps } from 'antd/es/form';
6 | import { TableListItem, TableListParams } from './typing.d';
7 |
8 | export async function queryRule(params?: TableListParams) {
9 | console.log(params);
10 | return { data: [], success: true };
11 | }
12 |
13 | const TableList: React.FC = () => {
14 | const [sorter, setSorter] = useState({});
15 | const actionRef = useRef();
16 | const columns: ProColumns[] = [
17 | {
18 | title: '规则名称',
19 | dataIndex: 'name',
20 | },
21 | {
22 | title: '描述',
23 | dataIndex: 'desc',
24 | },
25 | {
26 | title: '服务调用次数',
27 | dataIndex: 'callNo',
28 | sorter: true,
29 | align: 'right',
30 | renderText: (val: string) => `${val} 万`,
31 | },
32 | {
33 | title: '状态',
34 | dataIndex: 'status',
35 | valueEnum: {
36 | 0: { text: '关闭', status: 'Default' },
37 | 1: { text: '运行中', status: 'Processing' },
38 | 2: { text: '已上线', status: 'Success' },
39 | 3: { text: '异常', status: 'Error' },
40 | },
41 | },
42 | {
43 | title: '上次调度时间',
44 | dataIndex: 'updatedAt',
45 | sorter: true,
46 | valueType: 'dateTime',
47 | },
48 | {
49 | title: '操作',
50 | dataIndex: 'option',
51 | valueType: 'option',
52 | render: () => (
53 | <>
54 | 配置
55 |
56 | 订阅警报
57 | >
58 | ),
59 | },
60 | ];
61 |
62 | return (
63 |
64 | headerTitle="查询表格"
65 | actionRef={actionRef}
66 | rowKey="key"
67 | onChange={(_, _filter, _sorter) => {
68 | setSorter(`${_sorter.field}_${_sorter.order}`);
69 | }}
70 | params={{
71 | sorter,
72 | }}
73 | toolBarRender={(action, { selectedRows }) => [
74 | <>
75 |
76 | >,
77 | ,
80 | selectedRows && selectedRows.length > 0 && (
81 | {
85 | if (e.key === 'remove') {
86 | console.log('remove');
87 | action.reload();
88 | }
89 | }}
90 | selectedKeys={[]}
91 | >
92 | 批量删除
93 | 批量审批
94 |
95 | }
96 | >
97 |
100 |
101 | ),
102 | ]}
103 | tableAlertRender={(selectedRowKeys, selectedRows) => (
104 |
105 | 已选择
{selectedRowKeys.length} 项
106 |
107 | 服务调用次数总计 {selectedRows.reduce((pre, item) => pre + item.callNo, 0)} 万
108 |
109 |
110 | )}
111 | request={params => queryRule(params)}
112 | columns={columns}
113 | />
114 | );
115 | };
116 |
117 | export default Form.create()(TableList);
118 |
--------------------------------------------------------------------------------
/blocks/table/src/typing.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListItem {
2 | key: number;
3 | disabled?: boolean;
4 | href: string;
5 | avatar: string;
6 | name: string;
7 | title: string;
8 | owner: string;
9 | desc: string;
10 | callNo: number;
11 | status: number;
12 | updatedAt: Date;
13 | createdAt: Date;
14 | progress: number;
15 | }
16 |
17 | export interface TableListParams {
18 | sorter?: string;
19 | status?: string;
20 | name?: string;
21 | desc?: string;
22 | key?: number;
23 | pageSize?: number;
24 | currentPage?: number;
25 | }
26 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "emitDecoratorMetadata": true,
4 | "experimentalDecorators": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "blocks",
3 | "version": 2,
4 | "scope": "umijs",
5 | "builds": [
6 | {
7 | "src": "./package.json",
8 | "use": "@now/static-build",
9 | "config": {
10 | "distDir": "./dist"
11 | }
12 | }
13 | ],
14 | "alias": ["blocks.umijs.org"]
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "umi-blocks",
3 | "version": "1.0.0",
4 | "files": [
5 | "blocks",
6 | "dist",
7 | "templates"
8 | ],
9 | "scripts": {
10 | "build": "node ./_scripts/build.js",
11 | "create": "node ./_scripts/create.js",
12 | "lint": "eslint --ext .js src mock tests && npm run lint:style",
13 | "lint-staged": "lint-staged",
14 | "lint-staged:js": "eslint --ext .js",
15 | "lint:fix": "eslint --fix --ext .js src mock tests && npm run lint:style",
16 | "lint:style": "stylelint \"src/**/*.less\" --syntax less",
17 | "prettier": "prettier -c --write \"**/*\"",
18 | "screenshot": "pro screenshot",
19 | "start": "umi block_dev"
20 | },
21 | "husky": {
22 | "hooks": {
23 | "pre-commit": "npm run lint-staged"
24 | }
25 | },
26 | "lint-staged": {
27 | "**/*.less": "stylelint --syntax less",
28 | "x/**/*.{js,jsx}": "npm run lint-staged:js",
29 | "x/**/*.{js,ts,tsx,json,jsx,less}": [
30 | "node ./_scripts/lint-prettier.js",
31 | "git add"
32 | ]
33 | },
34 | "devDependencies": {
35 | "@ant-design/pro-cli": "^1.0.5",
36 | "@ant-design/pro-layout": "^4.5.16",
37 | "@umijs/fabric": "^2.0.0",
38 | "cross-env": "^6.0.0",
39 | "eslint": "^6.8.0",
40 | "glob": "^7.1.4",
41 | "husky": "^4.0.7",
42 | "import-sort-cli": "^6.0.0",
43 | "import-sort-parser-babylon": "^6.0.0",
44 | "import-sort-parser-typescript": "^6.0.0",
45 | "import-sort-style-module": "^6.0.0",
46 | "lint-staged": "^9.5.0",
47 | "mkdirp": "^0.5.1",
48 | "prettier": "1.19.1",
49 | "stylelint": "^13.0.0",
50 | "stylelint-config-prettier": "^8.0.0",
51 | "stylelint-config-standard": "^19.0.0",
52 | "typescript": "^3.5.1",
53 | "umi": "^2.13.0",
54 | "umi-plugin-block-dev": "^3.0.1",
55 | "umi-plugin-react": "^1.15.0",
56 | "umi-request": "^1.2.17",
57 | "yeoman-generator": "^4.1.0"
58 | },
59 | "engines": {
60 | "node": "12.x"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/plugin.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 | import { readdirSync } from 'fs';
3 |
4 | if (process.env.BLOCK) {
5 | process.env.PAGES_PATH = join(process.env.BLOCK, 'src');
6 | } else {
7 | console.log(`
8 | Please specify the BLOCK env,
9 |
10 | e.g.
11 |
12 | $ BLOCK=templates/user-dashboard yarn start
13 | $ BLOCK=blocks/blank yarn start
14 | `);
15 | process.exit(1);
16 | }
17 |
18 | function getRoutes(type) {
19 | const dirs = readdirSync(join(__dirname, type));
20 | const routes = [];
21 | for (const dir of dirs) {
22 | if (dir.charAt(0) === '.') continue;
23 | routes.push({
24 | path: `/${type}/${dir}`,
25 | component: join(type, dir, 'src', 'index.js'),
26 | });
27 | }
28 | return routes;
29 | }
30 |
31 | function getRoute() {
32 | return {
33 | path: '/',
34 | component: join(process.env.BLOCK, 'src', 'index.js'),
35 | };
36 | }
37 |
38 | export default api => {
39 | api.modifyRoutes(routes => {
40 | routes[0].routes = [
41 | getRoute(),
42 | // ...getRoutes('blocks'),
43 | // ...getRoutes('templates'),
44 | ...routes[0].routes.slice(1),
45 | ];
46 | return routes;
47 | });
48 | };
49 |
--------------------------------------------------------------------------------
/templates/blank/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Blank",
3 | "description": "Blank block for quick start a umi page develop, with mock, dva, service and antd.",
4 | "dependencies": {
5 | "umi-request": "^1.0.0"
6 | },
7 | "block": {
8 | "category": "脚手架"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/templates/blank/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/umi-blocks/f73b2e5a9dc082deba64c7bfbe426bc75f5f5fa0/templates/blank/snapshot.png
--------------------------------------------------------------------------------
/templates/blank/src/_mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'GET /api/BLOCK_NAME/text': {
3 | text: 'I am a blank block',
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/templates/blank/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'antd';
3 | import { connect } from 'dva';
4 |
5 | import styles from './style.less';
6 |
7 | @connect(({ BLOCK_NAME_CAMEL_CASE }) => BLOCK_NAME_CAMEL_CASE)
8 | class Page extends Component {
9 | componentDidMount() {
10 | const { dispatch } = this.props;
11 | dispatch({
12 | type: 'BLOCK_NAME_CAMEL_CASE/fetch',
13 | });
14 | }
15 |
16 | render() {
17 | const { text } = this.props;
18 | return (
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | export default Page;
27 |
--------------------------------------------------------------------------------
/templates/blank/src/model.js:
--------------------------------------------------------------------------------
1 | import { getText } from './service';
2 |
3 | export default {
4 | namespace: 'BLOCK_NAME_CAMEL_CASE',
5 | state: {
6 | text: 'loading...',
7 | },
8 |
9 | effects: {
10 | *fetch(_, { call, put }) {
11 | const { text } = yield call(getText);
12 | yield put({
13 | type: 'save',
14 | payload: {
15 | text,
16 | },
17 | });
18 | },
19 | },
20 |
21 | reducers: {
22 | save(state, { payload }) {
23 | return {
24 | ...state,
25 | ...payload,
26 | };
27 | },
28 | },
29 | };
30 |
--------------------------------------------------------------------------------
/templates/blank/src/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | export function getText() {
4 | return request('/api/BLOCK_NAME/text');
5 | }
6 |
--------------------------------------------------------------------------------
/templates/blank/src/style.less:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 8px;
3 | }
4 |
--------------------------------------------------------------------------------
/templates/user-dashboard/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "UserDashboard",
3 | "description": "User dashboard example with umi, antd and dva.",
4 | "dependencies": {
5 | "antd": "^3.23.4",
6 | "umi-request": "^1.0.0"
7 | },
8 | "block": {
9 | "category": "表格"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/templates/user-dashboard/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/umi-blocks/f73b2e5a9dc082deba64c7bfbe426bc75f5f5fa0/templates/user-dashboard/snapshot.png
--------------------------------------------------------------------------------
/templates/user-dashboard/src/_mock.js:
--------------------------------------------------------------------------------
1 | const PAGE_SIZE = 3;
2 | let data = [
3 | {
4 | id: 1,
5 | name: 'Leanne Graham',
6 | email: 'Sincere@april.biz',
7 | website: 'hildegard.org',
8 | },
9 | {
10 | id: 2,
11 | name: 'Ervin Howell',
12 | email: 'Shanna@melissa.tv',
13 | website: 'anastasia.net',
14 | },
15 | {
16 | id: 3,
17 | name: 'Clementine Bauch',
18 | email: 'Nathan@yesenia.net',
19 | website: 'ramiro.info',
20 | },
21 | {
22 | id: 4,
23 | name: 'Patricia Lebsack',
24 | email: 'Julianne.OConner@kory.org',
25 | website: 'kale.biz',
26 | },
27 | {
28 | id: 5,
29 | name: 'Chelsey Dietrich',
30 | email: 'Lucio_Hettinger@annie.ca',
31 | website: 'demarco.info',
32 | },
33 | {
34 | id: 6,
35 | name: 'Mrs. Dennis Schulist',
36 | email: 'Karley_Dach@jasper.info',
37 | website: 'ola.org',
38 | },
39 | {
40 | id: 7,
41 | name: 'Kurtis Weissnat',
42 | email: 'Telly.Hoeger@billy.biz',
43 | website: 'elvis.io',
44 | },
45 | {
46 | id: 8,
47 | name: 'Nicholas Runolfsdottir V',
48 | email: 'Sherwood@rosamond.me',
49 | website: 'jacynthe.com',
50 | },
51 | {
52 | id: 9,
53 | name: 'Glenna Reichert',
54 | email: 'Chaim_McDermott@dana.io',
55 | website: 'conrad.com',
56 | },
57 | ];
58 |
59 | function uid(len) {
60 | len = len || 7;
61 | return Math.random()
62 | .toString(35)
63 | .substr(2, len);
64 | }
65 |
66 | function getData(page) {
67 | const start = (page - 1) * PAGE_SIZE;
68 | return {
69 | status: 'success',
70 | total: data.length,
71 | page,
72 | data: data.slice(start, start + 3),
73 | };
74 | }
75 |
76 | export default {
77 | 'GET /api/BLOCK_NAME': (req, res) => {
78 | res.json(getData(parseInt(req.query.page, 10) || 1));
79 | },
80 | 'DELETE /api/BLOCK_NAME/:id': (req, res) => {
81 | data = data.filter(item => `${item.id}` !== `${req.params.id}`);
82 | res.end('ok');
83 | },
84 | 'PATCH /api/BLOCK_NAME/:id': (req, res) => {
85 | data.forEach(item => {
86 | if (`${item.id}` === `${req.params.id}`) {
87 | Object.assign(item, req.body);
88 | }
89 | });
90 | res.end('ok');
91 | },
92 | 'POST /api/BLOCK_NAME': (req, res) => {
93 | data.push({
94 | ...req.body,
95 | id: uid(),
96 | });
97 | res.end('ok');
98 | },
99 | };
100 |
--------------------------------------------------------------------------------
/templates/user-dashboard/src/components/UserModal.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import { Modal, Form, Input } from 'antd';
3 |
4 | const FormItem = Form.Item;
5 |
6 | class UserEditModal extends Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | visible: false,
11 | };
12 | }
13 |
14 | showModelHandler = e => {
15 | if (e) e.stopPropagation();
16 | this.setState({
17 | visible: true,
18 | });
19 | };
20 |
21 | hideModelHandler = () => {
22 | this.setState({
23 | visible: false,
24 | });
25 | };
26 |
27 | okHandler = () => {
28 | const { onOk } = this.props;
29 | this.props.form.validateFields((err, values) => {
30 | if (!err) {
31 | onOk(values);
32 | this.hideModelHandler();
33 | }
34 | });
35 | };
36 |
37 | render() {
38 | const { children } = this.props;
39 | const { getFieldDecorator } = this.props.form;
40 | const { name, email, website } = this.props.record;
41 | const formItemLayout = {
42 | labelCol: { span: 6 },
43 | wrapperCol: { span: 14 },
44 | };
45 |
46 | return (
47 |
48 | {children}
49 |
55 |
72 |
73 |
74 | );
75 | }
76 | }
77 |
78 | export default Form.create()(UserEditModal);
79 |
--------------------------------------------------------------------------------
/templates/user-dashboard/src/components/Users.css:
--------------------------------------------------------------------------------
1 | .normal {
2 | }
3 |
4 | .operation a {
5 | margin: 0 0.5em;
6 | }
7 |
8 | .create {
9 | margin: 1.5em 0;
10 | }
11 |
--------------------------------------------------------------------------------
/templates/user-dashboard/src/components/Users.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'dva';
2 | import { Table, Pagination, Popconfirm, Button } from 'antd';
3 | import { routerRedux } from 'dva/router';
4 | import styles from './Users.css';
5 | import { PAGE_SIZE } from '../utils/constants';
6 | import UserModal from './UserModal';
7 |
8 | function Users({ dispatch, list: dataSource, loading, total, page: current }) {
9 | function deleteHandler(id) {
10 | dispatch({
11 | type: 'BLOCK_NAME/remove',
12 | payload: id,
13 | });
14 | }
15 |
16 | function pageChangeHandler(page) {
17 | dispatch({
18 | type: 'BLOCK_NAME/fetch',
19 | payload: { page },
20 | });
21 | }
22 |
23 | function editHandler(id, values) {
24 | dispatch({
25 | type: 'BLOCK_NAME/patch',
26 | payload: { id, values },
27 | });
28 | }
29 |
30 | function createHandler(values) {
31 | dispatch({
32 | type: 'BLOCK_NAME/create',
33 | payload: values,
34 | });
35 | }
36 |
37 | const columns = [
38 | {
39 | title: 'Name',
40 | dataIndex: 'name',
41 | key: 'name',
42 | render: text => {text},
43 | },
44 | {
45 | title: 'Email',
46 | dataIndex: 'email',
47 | key: 'email',
48 | },
49 | {
50 | title: 'Website',
51 | dataIndex: 'website',
52 | key: 'website',
53 | },
54 | {
55 | title: 'Operation',
56 | key: 'operation',
57 | render: (text, record) => (
58 |
59 |
60 | Edit
61 |
62 |
63 | Delete
64 |
65 |
66 | ),
67 | },
68 | ];
69 |
70 | return (
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
record.id}
83 | pagination={false}
84 | />
85 |
92 |
93 |
94 | );
95 | }
96 |
97 | function mapStateToProps(state) {
98 | const { list, total, page } = state['BLOCK_NAME'];
99 | return {
100 | list,
101 | total,
102 | page,
103 | loading: state.loading.models['BLOCK_NAME'],
104 | };
105 | }
106 |
107 | export default connect(mapStateToProps)(Users);
108 |
--------------------------------------------------------------------------------
/templates/user-dashboard/src/index.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import { connect } from 'dva';
3 | import Users from './components/Users';
4 |
5 | class BLOCK_NAME_CAMEL_CASE extends Component {
6 | componentDidMount() {
7 | this.props.dispatch({
8 | type: 'BLOCK_NAME/fetch',
9 | payload: {
10 | page: 1,
11 | },
12 | });
13 | }
14 |
15 | render() {
16 | return (
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
24 | export default connect()(BLOCK_NAME_CAMEL_CASE);
25 |
--------------------------------------------------------------------------------
/templates/user-dashboard/src/model.js:
--------------------------------------------------------------------------------
1 | import * as usersService from './service';
2 |
3 | export default {
4 | namespace: 'BLOCK_NAME',
5 | state: {
6 | list: [],
7 | total: null,
8 | page: null,
9 | },
10 | reducers: {
11 | save(state, { payload: { data: list, total, page } }) {
12 | return { ...state, list, total, page };
13 | },
14 | },
15 | effects: {
16 | *fetch({ payload: { page = 1 } }, { call, put }) {
17 | const { data, total, page: currentPage } = yield call(usersService.fetch, { page });
18 | yield put({
19 | type: 'save',
20 | payload: {
21 | data,
22 | total,
23 | page: currentPage,
24 | },
25 | });
26 | },
27 | *remove({ payload: id }, { call, put, select }) {
28 | yield call(usersService.remove, id);
29 | const page = yield select(state => state['BLOCK_NAME'].page);
30 | yield put({ type: 'fetch', payload: { page } });
31 | },
32 | *patch({ payload: { id, values } }, { call, put, select }) {
33 | yield call(usersService.patch, id, values);
34 | const page = yield select(state => state['BLOCK_NAME'].page);
35 | yield put({ type: 'fetch', payload: { page } });
36 | },
37 | *create({ payload: values }, { call, put, select }) {
38 | yield call(usersService.create, values);
39 | const page = yield select(state => state['BLOCK_NAME'].page);
40 | yield put({ type: 'fetch', payload: { page } });
41 | },
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/templates/user-dashboard/src/service.js:
--------------------------------------------------------------------------------
1 | import request from 'umi-request';
2 |
3 | const API_PREFIX = `/api/BLOCK_NAME`;
4 |
5 | export function fetch({ page = 1 }) {
6 | return request(`${API_PREFIX}?page=${page}`);
7 | }
8 |
9 | export function remove(id) {
10 | return request(`${API_PREFIX}/${id}`, {
11 | method: 'DELETE',
12 | });
13 | }
14 |
15 | export function patch(id, values) {
16 | // TODO:
17 | // use umi-request after the issue is closed
18 | // https://github.com/umijs/umi-request/issues/5
19 | return window.fetch(`${API_PREFIX}/${id}`, {
20 | method: 'PATCH',
21 | headers: {
22 | 'Content-Type': 'application/json',
23 | },
24 | body: JSON.stringify(values),
25 | });
26 | }
27 |
28 | export function create(values) {
29 | return request(API_PREFIX, {
30 | method: 'POST',
31 | data: values,
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/templates/user-dashboard/src/utils/constants.js:
--------------------------------------------------------------------------------
1 | export const PAGE_SIZE = 3;
2 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "build/dist",
4 | "module": "esnext",
5 | "target": "esnext",
6 | "lib": ["esnext", "dom"],
7 | "sourceMap": true,
8 | "baseUrl": ".",
9 | "jsx": "react",
10 | "allowSyntheticDefaultImports": true,
11 | "moduleResolution": "node",
12 | "rootDirs": ["/src", "/test", "/mock", "./typings"],
13 | "forceConsistentCasingInFileNames": true,
14 | "noImplicitReturns": true,
15 | "suppressImplicitAnyIndexErrors": true,
16 | "noUnusedLocals": true,
17 | "allowJs": true,
18 | "resolveJsonModule": true,
19 | "experimentalDecorators": true,
20 | "strict": true,
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": [".", "./typings.d"],
26 | "exclude": ["node_modules", "_scripts", "jest", "tslint:latest", "tslint-config-prettier"]
27 | }
28 |
--------------------------------------------------------------------------------
/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.css';
2 | declare module '*.less';
3 | declare module '*.scss';
4 | declare module '*.sass';
5 | declare module '*.svg';
6 | declare module '*.png';
7 | declare module '*.jpg';
8 | declare module '*.jpeg';
9 | declare module '*.gif';
10 | declare module '*.bmp';
11 | declare module '*.tiff';
12 | declare module 'mockjs';
13 |
14 | declare module '@umijs/ui-flag';
15 |
--------------------------------------------------------------------------------