├── .gitignore
├── src
├── components
│ ├── Form
│ │ ├── util
│ │ │ └── index.js
│ │ ├── components
│ │ │ ├── Func.js
│ │ │ ├── FormLineHOC.js
│ │ │ ├── Switch.js
│ │ │ ├── Number.js
│ │ │ ├── DateTime.js
│ │ │ ├── Select.js
│ │ │ ├── TextArea.js
│ │ │ ├── Checkbox.js
│ │ │ ├── Input.js
│ │ │ └── DateRange.js
│ │ ├── index.js
│ │ └── style
│ │ │ └── form.scss
│ ├── DataDisplay
│ │ ├── List
│ │ │ ├── index.js
│ │ │ ├── style
│ │ │ │ ├── list.scss
│ │ │ │ └── item.scss
│ │ │ └── components
│ │ │ │ ├── List.js
│ │ │ │ └── Item.js
│ │ ├── Tag
│ │ │ ├── index.js
│ │ │ ├── style
│ │ │ │ └── index.scss
│ │ │ └── components
│ │ │ │ └── Tag.js
│ │ ├── Badge
│ │ │ ├── index.js
│ │ │ ├── style
│ │ │ │ └── badge.scss
│ │ │ └── components
│ │ │ │ └── Badge.js
│ │ ├── Carousel
│ │ │ ├── index.js
│ │ │ ├── util
│ │ │ │ └── index.js
│ │ │ └── style
│ │ │ │ └── carousel.scss
│ │ ├── Tooltip
│ │ │ ├── index.js
│ │ │ ├── components
│ │ │ │ ├── StateComponent.js
│ │ │ │ └── Tooltip.js
│ │ │ ├── style
│ │ │ │ └── tooltip.scss
│ │ │ └── util
│ │ │ │ └── index.js
│ │ └── ListTitle
│ │ │ ├── index.js
│ │ │ ├── components
│ │ │ └── ListTitle.js
│ │ │ └── style
│ │ │ └── index.scss
│ ├── DataEntry
│ │ ├── Picker
│ │ │ ├── index.js
│ │ │ └── style
│ │ │ │ └── picker.scss
│ │ ├── Figure
│ │ │ ├── index.js
│ │ │ ├── style
│ │ │ │ └── figure.scss
│ │ │ └── components
│ │ │ │ └── Figure.js
│ │ ├── Switch
│ │ │ ├── index.js
│ │ │ ├── style
│ │ │ │ └── index.scss
│ │ │ └── components
│ │ │ │ └── Switch.js
│ │ ├── Button
│ │ │ ├── index.js
│ │ │ ├── components
│ │ │ │ └── Button.js
│ │ │ └── style
│ │ │ │ └── index.scss
│ │ ├── Uploader
│ │ │ ├── index.js
│ │ │ └── style
│ │ │ │ └── uploader.scss
│ │ ├── DataPicker
│ │ │ ├── index.js
│ │ │ ├── style
│ │ │ │ └── datePicker.scss
│ │ │ └── util
│ │ │ │ └── index.js
│ │ └── PickerView
│ │ │ ├── index.js
│ │ │ ├── style
│ │ │ ├── picker-view.scss
│ │ │ └── picker-column.scss
│ │ │ └── components
│ │ │ ├── PickerColumn.js
│ │ │ └── PickerView.js
│ ├── Feedback
│ │ ├── Toast
│ │ │ ├── index.js
│ │ │ ├── style
│ │ │ │ ├── notification.scss
│ │ │ │ └── notice.scss
│ │ │ └── components
│ │ │ │ ├── Toast.js
│ │ │ │ ├── Notice.js
│ │ │ │ └── Notification.js
│ │ └── Popover
│ │ │ ├── index.js
│ │ │ └── components
│ │ │ └── Popover.js
│ ├── Tools
│ │ └── Tools.js
│ └── Gesture
│ │ └── Touchable.js
├── static
│ ├── img
│ │ ├── star-bucks.jpg
│ │ └── screen-shot-20171109.png
│ └── sass
│ │ ├── component.scss
│ │ ├── index.scss
│ │ └── base.scss
├── routes
│ ├── Form
│ │ ├── containers
│ │ │ └── Input
│ │ │ │ ├── index.js
│ │ │ │ └── InputPage.js
│ │ └── index.js
│ ├── DataDisplay
│ │ ├── containers
│ │ │ ├── Badge
│ │ │ │ ├── index.js
│ │ │ │ └── BadgePage.js
│ │ │ ├── List
│ │ │ │ ├── index.js
│ │ │ │ └── ListPage.js
│ │ │ ├── Tooltip
│ │ │ │ ├── index.js
│ │ │ │ └── TooltipPage.js
│ │ │ └── Carousel
│ │ │ │ ├── index.js
│ │ │ │ └── CarouselPage.js
│ │ └── index.js
│ ├── Feedback
│ │ ├── containers
│ │ │ └── Toast
│ │ │ │ ├── index.js
│ │ │ │ └── ToastPage.js
│ │ └── index.js
│ ├── DataEntry
│ │ ├── containers
│ │ │ ├── Picker
│ │ │ │ ├── index.js
│ │ │ │ └── PickerPage.js
│ │ │ ├── Switch
│ │ │ │ ├── index.js
│ │ │ │ └── SwitchPage.js
│ │ │ ├── Button
│ │ │ │ ├── index.js
│ │ │ │ └── ButtonPage.js
│ │ │ ├── Uploader
│ │ │ │ ├── index.js
│ │ │ │ └── UploaderPage.js
│ │ │ ├── DatePicker
│ │ │ │ ├── index.js
│ │ │ │ └── DatePickerPage.js
│ │ │ └── PickerView
│ │ │ │ ├── index.js
│ │ │ │ └── PickerViewPage.js
│ │ └── index.js
│ ├── index.js
│ └── Home
│ │ └── index.js
├── containers
│ ├── AppContainer.js
│ ├── Feedback
│ │ └── PopoverPage.js
│ ├── Form
│ │ ├── TextAreaPage.js
│ │ ├── SelectPage.js
│ │ ├── SwitchPage.js
│ │ ├── CheckboxPage.js
│ │ ├── NumberPage.js
│ │ ├── DateTimePage.js
│ │ ├── FormPage.js
│ │ └── DateRangePage.js
│ └── DataDisplay
│ │ ├── ListPage.js
│ │ └── TagPage.js
├── layout
│ └── index.js
├── main.js
└── index.html
├── bin
├── server.js
├── compile.js
└── online.js
├── config
├── environments.js
└── index.js
├── README.md
├── online.json
├── server
└── main.js
├── LICENSE
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | npm-debug.log
2 | /node_modules
3 | /tests
4 | /.idea
5 | /dist
6 | /logs
--------------------------------------------------------------------------------
/src/components/Form/util/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/28.
3 | */
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/DataDisplay/List/index.js:
--------------------------------------------------------------------------------
1 | import List from './components/List'
2 |
3 | export default List;
--------------------------------------------------------------------------------
/src/components/DataEntry/Picker/index.js:
--------------------------------------------------------------------------------
1 | import Picker from './components/Picker';
2 |
3 | export default Picker;
--------------------------------------------------------------------------------
/src/static/img/star-bucks.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aus0049/react-component/HEAD/src/static/img/star-bucks.jpg
--------------------------------------------------------------------------------
/src/static/img/screen-shot-20171109.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aus0049/react-component/HEAD/src/static/img/screen-shot-20171109.png
--------------------------------------------------------------------------------
/src/components/DataDisplay/Tag/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/10.
3 | */
4 | import Tag from './components/Tag'
5 |
6 | export default Tag;
--------------------------------------------------------------------------------
/src/components/DataDisplay/Badge/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/20.
3 | */
4 | import Badge from './components/Badge'
5 |
6 | export default Badge;
--------------------------------------------------------------------------------
/src/components/DataEntry/Figure/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/6.
3 | */
4 | import Figure from './components/Figure'
5 |
6 | export default Figure
--------------------------------------------------------------------------------
/src/components/DataEntry/Switch/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/7.
3 | */
4 | import Switch from './components/Switch'
5 |
6 | export default Switch;
--------------------------------------------------------------------------------
/src/components/Feedback/Toast/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/8/24.
3 | */
4 | import Toast from './components/Toast'
5 |
6 | export default Toast;
--------------------------------------------------------------------------------
/src/components/DataEntry/Button/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/7.
3 | */
4 | import Button from './components/Button'
5 |
6 | export default Button;
7 |
--------------------------------------------------------------------------------
/src/components/Feedback/Popover/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/9/6.
3 | */
4 | import Popover from './components/Popover'
5 |
6 | export default Popover;
--------------------------------------------------------------------------------
/src/components/DataDisplay/Carousel/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/8.
3 | */
4 | import Carousel from './components/Carousel'
5 |
6 | export default Carousel;
--------------------------------------------------------------------------------
/src/components/DataDisplay/Tooltip/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/13.
3 | */
4 | import Tooltip from './components/Tooltip'
5 |
6 | export default Tooltip;
--------------------------------------------------------------------------------
/src/components/DataEntry/Uploader/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/5.
3 | */
4 | import Uploader from './components/Uploader'
5 |
6 | export default Uploader;
--------------------------------------------------------------------------------
/src/components/DataDisplay/ListTitle/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/14.
3 | */
4 | import ListTitle from './components/ListTitle'
5 |
6 | export default ListTitle;
--------------------------------------------------------------------------------
/src/components/DataEntry/DataPicker/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/7.
3 | */
4 | import DataPicker from './components/DatePicker';
5 |
6 | export default DataPicker;
--------------------------------------------------------------------------------
/src/components/DataEntry/PickerView/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/7.
3 | */
4 | import PickerView from './components/PickerView'
5 |
6 | export default PickerView;
--------------------------------------------------------------------------------
/src/components/DataEntry/PickerView/style/picker-view.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | $prefixCls: 'zby-picker-view';
4 |
5 | // PickerView
6 | .#{$prefixCls} {
7 | display: flex;
8 | align-items: center;
9 | }
--------------------------------------------------------------------------------
/bin/server.js:
--------------------------------------------------------------------------------
1 | const config = require('../config')
2 | const server = require('../server/main')
3 | const debug = require('debug')('app:bin:server')
4 | const port = config.server_port
5 |
6 | server.listen(port)
7 | debug(`🍺 项目顺利跑起来 http://localhost:${port}.`)
8 |
--------------------------------------------------------------------------------
/src/components/Tools/Tools.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/4/1.
3 | */
4 | import { browserHistory } from 'react-router'
5 |
6 | const Tool = {
7 | linkTo (path) {
8 | browserHistory.push(path);
9 | }
10 | };
11 |
12 | export default Tool
--------------------------------------------------------------------------------
/src/routes/Form/containers/Input/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/28.
3 | */
4 | export default () => ({
5 | path: 'input',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./InputPage').default)
9 | }, 'input')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataDisplay/containers/Badge/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/20.
3 | */
4 | export default () => ({
5 | path: 'badge',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./BadgePage').default)
9 | }, 'badge')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataDisplay/containers/List/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/14.
3 | */
4 | export default () => ({
5 | path: 'list',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./ListPage').default)
9 | }, 'list')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/Feedback/containers/Toast/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/18.
3 | */
4 | export default () => ({
5 | path: 'toast',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./ToastPage').default)
9 | }, 'toast')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/Picker/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/7.
3 | */
4 | export default () => ({
5 | path: 'picker',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./PickerPage').default)
9 | }, 'picker')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/Switch/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/17.
3 | */
4 | export default () => ({
5 | path: 'switch',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./SwitchPage').default)
9 | }, 'switch')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataDisplay/containers/Tooltip/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/13.
3 | */
4 | export default () => ({
5 | path: 'tooltip',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./TooltipPage').default)
9 | }, 'tooltip')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/Button/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/13.
3 | */
4 | export default () => ({
5 | path: 'button',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./ButtonPage').default)
9 | }, 'button')
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/Uploader/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/19.
3 | */
4 | export default () => ({
5 | path: 'uploader',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./UploaderPage').default)
9 | }, 'uploader')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataDisplay/containers/Carousel/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/10.
3 | */
4 | export default () => ({
5 | path: 'carousel',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./CarouselPage').default)
9 | }, 'carousel')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/DatePicker/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/7.
3 | */
4 | export default () => ({
5 | path: 'date-picker',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./DatePickerPage').default)
9 | }, 'date-picker')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/PickerView/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/20.
3 | */
4 | export default () => ({
5 | path: 'picker-view',
6 | getComponents(location, callback) {
7 | require.ensure([], function (require) {
8 | callback(null, require('./PickerViewPage').default)
9 | }, 'picker-view')
10 | }
11 | })
--------------------------------------------------------------------------------
/src/components/Feedback/Toast/style/notification.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-notification';
6 |
7 | .#{$prefixCls}-box {
8 | position: fixed;
9 | left: 50%;
10 | top: 30%;
11 | width: 40%;
12 | height: 300px;
13 |
14 | transform: translate3D(-50%, -50%, 0);
15 | z-index: 110;
16 | }
--------------------------------------------------------------------------------
/src/routes/Form/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/28.
3 | */
4 | export default () => ({
5 | path: 'form',
6 | getChildRoutes (nextState, callback) {
7 | require.ensure([], (require) => {
8 | callback(null, [
9 | require('./containers/Input').default(),
10 | ]);
11 | }, 'form')
12 | },
13 | })
--------------------------------------------------------------------------------
/src/routes/Feedback/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/18.
3 | */
4 | export default () => ({
5 | path: 'feedback',
6 | getChildRoutes (nextState, callback) {
7 | require.ensure([], (require) => {
8 | callback(null, [
9 | require('./containers/Toast/index').default()
10 | ]);
11 | }, 'feedback')
12 | },
13 | })
--------------------------------------------------------------------------------
/src/components/Form/components/Func.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/8/8.
3 | */
4 | // 表单添加报错
5 | export function showError (errorText) {
6 | const errorBox = document.createElement('div');
7 | errorBox.className = 'zby-form-error';
8 | errorBox.innerText = errorText;
9 | document.body.appendChild(errorBox);
10 | }
11 |
12 | export function clearError () {
13 | document.querySelector('.zby-form-error').remove();
14 | }
--------------------------------------------------------------------------------
/config/environments.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | development: (config) => ({
3 | compiler_public_path: `http://${config.server_host}:${config.server_port}/`
4 | }),
5 | production: (config) => ({
6 | compiler_public_path: '/',
7 | compiler_fail_on_warning: false,
8 | compiler_hash_type: 'chunkhash',
9 | compiler_devtool: null,
10 | compiler_stats: {
11 | chunks: true,
12 | chunkModules: true,
13 | colors: true
14 | }
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/DataDisplay/Tooltip/components/StateComponent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/16.
3 | */
4 | import React from 'react'
5 |
6 | // 不能给传入的children增加div
7 | // 但是又要获取其真实 只能用ref
8 | // 无状态组件没有this 不能绑定ref
9 | // 转化一下 邦到一个有状态组件上
10 | class StateComponent extends React.Component {
11 | constructor (props) {
12 | super(props);
13 | }
14 | render () {
15 | return this.props.component;
16 | }
17 | }
18 |
19 | export default StateComponent
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-component
2 |
3 | 移动端react组件库,参考Antd-mobile制作。
4 |
5 | 效果图:
6 | 
7 |
8 | [在线预览地址](http://45.77.244.137:3000/), 外网速度较慢,请耐心等待。
9 |
10 | # 执行步骤
11 |
12 | ```
13 | git clone https://github.com/Aus0049/react-component.git
14 | npm install
15 | npm start
16 | ```
17 |
18 | 浏览器打开`localhost:3000`,即可查看效果。
19 |
20 | 专门适配移动端,组件不定期更新🍺
21 |
22 | 2017年10月份开始会对所有组件进行重构,更改项目目录结构,为了年底在npm上发包。
23 |
24 |
--------------------------------------------------------------------------------
/src/routes/DataDisplay/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/14.
3 | */
4 | export default () => ({
5 | path: 'data-display',
6 | getChildRoutes (nextState, callback) {
7 | require.ensure([], (require) => {
8 | callback(null, [
9 | require('./containers/List/').default(),
10 | require('./containers/Carousel/').default(),
11 | require('./containers/Tooltip/').default(),
12 | require('./containers/Badge/').default(),
13 | ]);
14 | }, 'data-display')
15 | },
16 | })
--------------------------------------------------------------------------------
/src/containers/AppContainer.js:
--------------------------------------------------------------------------------
1 | // 项目的根组件
2 | import React, { Component } from 'react'
3 | import { browserHistory, Router } from 'react-router'
4 |
5 | class AppContainer extends Component {
6 | // 验证参数
7 | static propTypes = {
8 | routes : React.PropTypes.object.isRequired
9 | };
10 | // 禁止reRender
11 | shouldComponentUpdate () {
12 | return false
13 | }
14 |
15 | render () {
16 | const { routes } = this.props;
17 |
18 | return (
19 |
20 |
21 |
22 | )
23 | }
24 | }
25 |
26 | export default AppContainer
27 |
--------------------------------------------------------------------------------
/online.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "react-component", // 应用名称
3 | "script" : "./bin/online.js", // 实际启动脚本
4 | "cwd" : "./", // 当前工作路径
5 | "watch": [ // 监控变化的目录,一旦变化,自动重启
6 | "bin",
7 | "build",
8 | "src"
9 | ],
10 | "ignore_watch" : [ // 从监控目录中排除
11 | "node_modules",
12 | "config",
13 | "dist",
14 | "server"
15 | ],
16 | "watch_options": {
17 | "followSymlinks": false
18 | },
19 | "error_file" : "./logs/app-err.log", // 错误日志路径
20 | "out_file" : "./logs/app-out.log", // 普通日志路径
21 | "env": {
22 | "NODE_ENV": "production", // 环境参数,当前指定为生产环境
23 | "PORT": 3000,
24 | "DEBUG": "app:*"
25 | }
26 | }
--------------------------------------------------------------------------------
/src/components/Form/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/17.
3 | */
4 | import Input from './components/Input'
5 | import TextArea from './components/TextArea'
6 | import DateRange from './components/DateRange'
7 | import DateTime from './components/DateTime'
8 | import Select from './components/Select'
9 | import Checkbox from './components/Checkbox'
10 | import Number from './components/Number'
11 | import Switch from './components/Switch'
12 | import Form from './components/Form'
13 | import Validate from './components/Validate'
14 |
15 | export {
16 | Input,
17 | TextArea,
18 | Number,
19 | DateRange,
20 | DateTime,
21 | Select,
22 | Checkbox,
23 | Switch,
24 | Form,
25 | Validate
26 | }
--------------------------------------------------------------------------------
/src/components/Feedback/Popover/components/Popover.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/9/6.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 |
7 | class Popover extends React.Component {
8 | constructor (props) {
9 | super(props);
10 | this.state = {
11 | isAppear: false
12 | }
13 | }
14 | handle (e) {
15 | this.setState({isAppear: true});
16 | }
17 | render () {
18 |
19 | return (
20 |
21 | {this.props.children}
22 | {this.state.isAppear ?
1111
: ''}
23 |
24 | );
25 | }
26 | }
27 |
28 | export default Popover
--------------------------------------------------------------------------------
/src/routes/DataEntry/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/13.
3 | */
4 | export default () => ({
5 | path: 'data-entry',
6 | getChildRoutes (nextState, callback) {
7 | require.ensure([], (require) => {
8 | callback(null, [
9 | require('./containers/Button/index').default(),
10 | require('./containers/Switch/index').default(),
11 | require('./containers/DatePicker/index').default(),
12 | require('./containers/Picker/index').default(),
13 | require('./containers/PickerView/index').default(),
14 | require('./containers/Uploader/index').default(),
15 | ]);
16 | }, 'data-entry')
17 | },
18 | })
19 |
--------------------------------------------------------------------------------
/bin/compile.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra')
2 | const debug = require('debug')('app:bin:compile')
3 | const webpackCompiler = require('../build/webpack-compiler')
4 | const webpackConfig = require('../build/webpack.config')
5 | const config = require('../config')
6 |
7 | const paths = config.utils_paths
8 |
9 | const compile = () => {
10 | return Promise.resolve()
11 | .then(() => webpackCompiler(webpackConfig))
12 | .then(stats => {
13 | fs.copySync(paths.client('static'), paths.dist())
14 | })
15 | .then(() => {
16 | debug('Compilation completed successfully.')
17 | })
18 | .catch((err) => {
19 | debug('Compiler encountered an error.', err)
20 | process.exit(1)
21 | })
22 | }
23 |
24 | compile()
25 |
--------------------------------------------------------------------------------
/src/components/DataEntry/Uploader/style/uploader.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | $prefixCls: 'zby-uploader';
4 |
5 | // Uploader
6 | .#{$prefixCls} {
7 | width: 100%;
8 | padding: 20px;
9 | background: #fff;
10 |
11 | .#{$prefixCls}-button {
12 | display: inline-block;
13 | width: 160px;
14 | height: 160px;
15 | border-radius: 5px;
16 | border: 2px dashed #d9d9d9;
17 | vertical-align: top;
18 | text-align: center;
19 | color: #999;
20 |
21 | box-sizing: border-box;
22 |
23 | .fa {
24 | display: inline-block;
25 | margin-top: 10px;
26 | font-size: 60px;
27 | line-height: 90px;
28 | }
29 |
30 | .text {
31 | font-size: 28px;
32 | }
33 | }
34 |
35 | .file-input {
36 | display: none;
37 | }
38 | }
--------------------------------------------------------------------------------
/src/components/DataDisplay/ListTitle/components/ListTitle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/4/1.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import '../style/index.scss'
7 |
8 | const ListTitle = (props) => {
9 | const {title, align} = props;
10 |
11 | return (
12 | {title}
13 | )
14 | };
15 |
16 | // 用于展示列表最上方 列表数据的title
17 | // title: title展示名称 必填 字数最好不要太多 溢出文字会...显示
18 | // align: 对齐方式 可选值 枚举 "center" "right" 默认或者不填是左对齐
19 | ListTitle.propTypes = {
20 | title: React.PropTypes.string.isRequired,
21 | align: React.PropTypes.oneOf(['left', 'center', 'right'])
22 | };
23 | ListTitle.defaultProps = {
24 | align: 'left'
25 | };
26 |
27 | export default ListTitle
--------------------------------------------------------------------------------
/src/components/DataDisplay/ListTitle/style/index.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | $viColor: #00b4a5;
4 | $darkViColor: #009486;
5 | $linearViColor: linear-gradient(to right, #31D5A1, #00B4A5);
6 | $lightGray: #ddd;
7 | $darkGray: #bbb;
8 |
9 | // px2rem并没有自动实现该功能 还是手动加上吧
10 | @mixin font-dpr($font-size){
11 | font-size: $font-size;
12 |
13 | [data-dpr="2"] & {
14 | font-size: $font-size * 2;
15 | }
16 |
17 | [data-dpr="3"] & {
18 | font-size: $font-size * 3;
19 | }
20 | }
21 |
22 | // list title
23 | .zby-list-title {
24 | padding: 0 40px;
25 | @include font-dpr(14px);
26 | line-height: 80px;
27 | overflow: hidden;
28 | text-overflow: ellipsis;
29 | white-space:nowrap;
30 | vertical-align: top;
31 |
32 | &.center {
33 | text-align: center;
34 | }
35 |
36 | &.right {
37 | text-align: right;
38 | }
39 | }
--------------------------------------------------------------------------------
/src/static/sass/component.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | $viColor: #00b4a5;
4 | $darkViColor: #009486;
5 | $linearViColor: linear-gradient(to right, #31D5A1, #00B4A5);
6 | $lightGray: #ddd;
7 | $darkGray: #bbb;
8 |
9 | // px2rem并没有自动实现该功能 还是手动加上吧
10 | @mixin font-dpr($font-size){
11 | font-size: $font-size;
12 |
13 | [data-dpr="2"] & {
14 | font-size: $font-size * 2;
15 | }
16 |
17 | [data-dpr="3"] & {
18 | font-size: $font-size * 3;
19 | }
20 | }
21 |
22 | // *
23 | a {
24 | background: transparent;
25 | text-decoration: none;
26 |
27 | &:focus {
28 | outline: none;
29 | }
30 | &:hover {
31 | text-decoration: none;
32 | }
33 | }
34 |
35 | // Mask
36 | .zby-mask {
37 | position: fixed;
38 | left: 0;
39 | right: 0;
40 | top: 0;
41 | bottom: 0;
42 | background: #000;
43 | z-index: 100;
44 | opacity: .3;
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/src/layout/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import 'normalize.css'
3 | import 'sass/index.scss'
4 | import 'sass/component.scss'
5 | import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
6 | require('font-awesome/css/font-awesome.css');
7 |
8 |
9 | class Layout extends React.Component {
10 | render () {
11 | return (
12 |
19 |
20 | {this.props.children}
21 |
22 |
23 | )
24 | }
25 | }
26 |
27 | export default Layout
28 |
--------------------------------------------------------------------------------
/src/containers/Feedback/PopoverPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/9/6.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import Button from '../../components/DataEntry/Button/components/Button'
7 | import Popover from '../../components/Feedback/Popover/'
8 | import Tools from '../../components/Tools/Tools'
9 |
10 | const PopoverPage = () => {
11 | return (
12 |
13 |
14 | {Tools.linkTo('/index')}} />
15 | Toast
16 |
17 |
18 |
23 |
24 | )
25 | }
26 |
27 | export default PopoverPage
--------------------------------------------------------------------------------
/src/components/DataDisplay/Carousel/util/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/10.
3 | */
4 | // 简易实现jq animate
5 | // 不断调用setInterval 实现动画
6 | export const animationFunc = (obj, style, time, callback) => {
7 | const currentStyle = obj.style;
8 | const diffObj = {};
9 | const step = 20, intervalNum = time / step;
10 | let num = 0;
11 |
12 | for(let i in style){
13 | diffObj[i] = (Number.parseFloat(style[i]) - Number.parseFloat(currentStyle[i])) / intervalNum;
14 | }
15 |
16 | // 开始调用
17 | const timer = setInterval(()=>{
18 | if(num < intervalNum){
19 | for(let i in diffObj){
20 | currentStyle[i] = Number.parseFloat(currentStyle[i]) + diffObj[i] + 'px';
21 | }
22 |
23 | num++;
24 | } else {
25 | clearInterval(timer);
26 | // 回调
27 | if(callback) callback();
28 | }
29 | }, step);
30 | };
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/Uploader/UploaderPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/19.
3 | */
4 | import React from 'react'
5 | import ListTitle from 'components/DataDisplay/ListTitle/'
6 | import Uploader from 'components/DataEntry/Uploader/'
7 | import Tools from 'components/Tools/Tools'
8 |
9 | class UploaderPage extends React.Component {
10 | constructor (props) {
11 | super(props);
12 | this.state = {};
13 | }
14 | render () {
15 | return (
16 |
17 |
18 | {Tools.linkTo('/')}} />
19 | Uploader
20 |
21 |
22 |
23 |
27 |
28 | )
29 | }
30 | }
31 |
32 | export default UploaderPage
33 |
--------------------------------------------------------------------------------
/src/components/DataDisplay/Carousel/style/carousel.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-carousel';
6 |
7 | // Carousel
8 | .#{$prefixCls} {
9 | width: 100%;
10 | position: relative;
11 | overflow: hidden;
12 |
13 | .#{$prefixCls}-list {
14 | overflow: hidden;
15 |
16 | .#{$prefixCls}-figure {
17 | float: left;
18 | height: 300px;
19 | background: #333333;
20 | }
21 | }
22 |
23 | .#{$prefixCls}-dot-box {
24 | position: absolute;
25 | left: 50%;
26 | bottom: 15px;
27 |
28 | transform: translate(-50%, 0);
29 |
30 | .#{$prefixCls}-dot {
31 | display: inline-block;
32 | margin-right: 20px;
33 | width: 15px;
34 | height: 15px;
35 | border-radius: 50%;
36 | background: #fff;
37 | transition: all .3s;
38 |
39 | &.active {
40 | transform: scale(1.5, 1.5);
41 | }
42 |
43 | &:last-of-type {
44 | margin-right: 0;
45 | }
46 | }
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/src/components/DataDisplay/List/style/list.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-list';
6 | $itemPrefixCls: 'zby-item';
7 |
8 | /* List */
9 | .#{$prefixCls} {
10 | &-header {
11 | padding: 0 40px;
12 | @include font-dpr(14px);
13 | line-height: 80px;
14 | overflow: hidden;
15 | text-overflow: ellipsis;
16 | white-space:nowrap;
17 | vertical-align: top;
18 | }
19 |
20 | &-footer {
21 | padding: 0 40px;
22 | @include font-dpr(14px);
23 | line-height: 80px;
24 | overflow: hidden;
25 | text-overflow: ellipsis;
26 | white-space:nowrap;
27 | vertical-align: top;
28 | }
29 |
30 | &-body {
31 | position: relative;
32 | background-color: #fff;
33 | @include border-top();
34 |
35 | &:after {
36 | @include list-item-bottom-line(0px);
37 | }
38 |
39 | div:not(:last-child) {
40 | .#{$itemPrefixCls}:after,
41 | &.#{$itemPrefixCls}:after {
42 | @include list-item-bottom-line();
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/components/DataDisplay/Tag/style/index.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | $viColor: #00b4a5;
4 | $darkViColor: #009486;
5 | $linearViColor: linear-gradient(to right, #31D5A1, #00B4A5);
6 | $lightGray: #ddd;
7 | $darkGray: #bbb;
8 |
9 | // Tag
10 | .zby-tag-box {
11 | display: inline-block;
12 | position: relative;
13 | margin: 0 10px 20px 10px;
14 | padding: 16px 20px;
15 | border: 1px solid $darkGray;
16 | border-radius: 5px;
17 | background: #fff;
18 | font-size: 28px;
19 | color: #888;
20 | word-break: break-all;
21 |
22 | &.selected {
23 | border-color: $viColor;
24 | color: $viColor;
25 | }
26 |
27 | &.disabled {
28 | color: #bbb;
29 | background: #ddd;
30 | border-color: #ddd;
31 | }
32 |
33 | .close {
34 | position: absolute;
35 | right: -20px;
36 | top: -20px;
37 | width: 40px;
38 | height: 40px;
39 | border: 1px solid $darkGray;
40 | background: #fff;
41 | border-radius: 50%;
42 | font-size: 24px;
43 | line-height: 40px;
44 | color: #888;
45 | text-align: center;
46 | }
47 | }
--------------------------------------------------------------------------------
/server/main.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const debug = require('debug')('app:server')
3 | const webpack = require('webpack')
4 | const webpackConfig = require('../build/webpack.config')
5 | const config = require('../config')
6 |
7 | const app = express()
8 | const paths = config.utils_paths
9 |
10 | app.use(require('connect-history-api-fallback')())
11 |
12 | if (config.env === 'development') {
13 | const compiler = webpack(webpackConfig)
14 |
15 | debug('Enable webpack dev and HMR middleware')
16 | app.use(require('webpack-dev-middleware')(compiler, {
17 | publicPath : webpackConfig.output.publicPath,
18 | contentBase : paths.client(),
19 | hot : true,
20 | quiet : config.compiler_quiet,
21 | noInfo : config.compiler_quiet,
22 | lazy : false,
23 | stats : config.compiler_stats
24 | }))
25 | app.use(require('webpack-hot-middleware')(compiler))
26 |
27 | app.use(express.static(paths.client('static')))
28 | } else {
29 | app.use(express.static(paths.dist()))
30 | }
31 |
32 | module.exports = app
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017
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 |
--------------------------------------------------------------------------------
/src/components/DataDisplay/Badge/style/badge.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-badge';
6 |
7 | // Badge
8 | .#{$prefixCls} {
9 | display: inline-block;
10 | margin-top: -5px;
11 | line-height: 1.5;
12 | vertical-align: middle;
13 |
14 | &.dot {
15 | position: absolute;
16 | display: block;
17 | right: -5px;
18 | top: -5px;
19 | height: 20px;
20 | width: 20px;
21 | border-radius: 100%;
22 | background: #ff5b05;
23 | z-index: 10;
24 | }
25 |
26 | &.ribbon {
27 | position: absolute;
28 | top: 40px;
29 | right: 0;
30 | transform: rotate(45deg);
31 | transform-origin: right bottom;
32 |
33 | .#{$prefixCls}-text {
34 | display: inline-block;
35 | width: 80px;
36 | border-radius: 0;
37 | }
38 | }
39 |
40 | &-text {
41 | height: 32px;
42 | min-width: 20px;
43 | padding: 0 15px;
44 | border-radius: 24px;
45 | text-align: center;
46 | font-size: 24px;
47 | color: #fff;
48 | background-color: #ff5b05;
49 | white-space: nowrap;
50 | }
51 | }
52 |
53 | .#{$prefixCls}-wrap {
54 | position: relative;
55 | }
--------------------------------------------------------------------------------
/src/components/Form/components/FormLineHOC.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/12/1.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import Icon from 'component-font-awesome'
7 | import Tooltip from 'components/DataDisplay/Tooltip/'
8 | import '../style/form.scss'
9 |
10 | // 生成表单的HOC
11 | export default function FormLineHOC(WrappedComponent) {
12 | // 反向继承
13 | return class FormLine extends WrappedComponent {
14 | render () {
15 | const {prefixCls, labelName, errorText, required} = this.props;
16 |
17 | return (
18 |
19 |
20 |
21 |
{labelName}
22 |
23 | {errorText ?
24 |
{super.render()}
25 | :
26 | super.render()
27 | }
28 |
29 | );
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/bin/online.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/8.
3 | */
4 | // 压缩之后在线运行
5 | const express = require('express')
6 | const webpack = require('webpack')
7 | const webpackConfig = require('../build/webpack.config')
8 | const config = require('../config')
9 | const debug = require('debug')('app:prod:online')
10 | const fs = require('fs-extra')
11 | const webpackCompiler = require('../build/webpack-compiler')
12 |
13 | const app = express()
14 | const paths = config.utils_paths
15 |
16 | app.use(require('connect-history-api-fallback')())
17 |
18 | const compile = () => {
19 | return Promise.resolve()
20 | .then(() => webpackCompiler(webpackConfig))
21 | .then(stats => {
22 | fs.copySync(paths.client('static'), paths.dist())
23 | })
24 | .then(() => {
25 | debug('Compilation completed successfully.')
26 | })
27 | .catch((err) => {
28 | debug('Compiler encountered an error.', err)
29 | process.exit(1)
30 | })
31 | };
32 |
33 | compile()
34 | .then(()=>{
35 | app.use(express.static(paths.dist()))
36 | debug(`🍺server running on port: ${config.server_port}🍺`)
37 | app.listen(config.server_port);
38 | });
39 |
--------------------------------------------------------------------------------
/src/components/Feedback/Toast/style/notice.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-notice';
6 |
7 | // Notice
8 | .#{$prefixCls} {
9 | position: absolute;
10 | box-sizing: border-box;
11 | left: 50%;
12 | top: 30%;
13 | margin: 0;
14 | width: 100%;
15 | padding: 20px 30px;
16 | background: rgba(0,0,0, .7);
17 | border-radius: 10px;
18 | text-align: center;
19 | font-size: 28px;
20 | line-height: 42px;
21 | color: #fff;
22 | z-index: 1110;
23 |
24 | transition: all .3s;
25 | transform: translate3D(-50%, -50%, 0) scale(1, 1);
26 |
27 | &.leave {
28 | transform: translate3D(-50%, -100%, 0);
29 | opacity: 0;
30 | }
31 |
32 | // 正常提示
33 | &.info {
34 | animation: Flop .5s linear;
35 | }
36 |
37 | // 成功提示
38 | &.success {
39 | animation: BombIn .3s;
40 | }
41 |
42 | // 警告提示⚠️
43 | &.warning {
44 | animation: slideInFromBottom .3s;
45 | }
46 |
47 | // 错误提示
48 | &.error {
49 | animation: shake .5s;
50 | }
51 |
52 | .#{$prefixCls}-icon {
53 | padding: 20px;
54 | font-size: 80px;
55 | text-align: center;
56 | color: #fff;
57 | }
58 |
59 | .#{$prefixCls}-content {
60 | font-size: 28px;
61 | text-align: center;
62 | color: #fff;
63 | }
64 | }
--------------------------------------------------------------------------------
/src/components/DataEntry/Picker/style/picker.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-picker';
6 |
7 | // Picker
8 | .#{$prefixCls} {
9 | .#{$prefixCls}-popup-mask {
10 | position: fixed;
11 | left: 0;
12 | right: 0;
13 | top: 0;
14 | bottom: 0;
15 | z-index: 1000;
16 | background-color: rgba(0,0,0,.4);
17 | transition: all .3s;
18 |
19 | &.hide {
20 | z-index: -1;
21 | background-color: rgba(0,0,0,0);
22 | }
23 | }
24 |
25 | .#{$prefixCls}-popup-wrap {
26 | position: fixed;
27 | left: 0;
28 | right: 0;
29 | bottom: -100%;
30 | z-index: 1000;
31 | background-color: #fff;
32 | transition: all .3s;
33 |
34 | &.popup {
35 | bottom: 0;
36 | }
37 |
38 | .#{$prefixCls}-popup-header {
39 | display: flex;
40 | border-bottom: 1px solid #ddd;
41 |
42 | .#{$prefixCls}-popup-item {
43 | padding: 10px 30px;
44 | height: 60px;
45 | font-size: 34px;
46 | line-height: 60px;
47 | color: #000;
48 | text-align: center;
49 |
50 | &.#{$prefixCls}-header-title {
51 | flex: 1;
52 | }
53 |
54 | &.#{$prefixCls}-header-left,
55 | &.#{$prefixCls}-header-right {
56 | color: $viColor;
57 | }
58 | }
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/src/components/DataEntry/DataPicker/style/datePicker.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-picker';
6 |
7 | // Picker
8 | .#{$prefixCls} {
9 | .#{$prefixCls}-popup-mask {
10 | position: fixed;
11 | left: 0;
12 | right: 0;
13 | top: 0;
14 | bottom: 0;
15 | z-index: 1000;
16 | background-color: rgba(0,0,0,.4);
17 | transition: all .3s;
18 |
19 | &.hide {
20 | z-index: -1;
21 | background-color: rgba(0,0,0,0);
22 | }
23 | }
24 |
25 | .#{$prefixCls}-popup-wrap {
26 | position: fixed;
27 | left: 0;
28 | right: 0;
29 | bottom: -100%;
30 | z-index: 1000;
31 | background-color: #fff;
32 | transition: all .3s;
33 |
34 | &.popup {
35 | bottom: 0;
36 | }
37 |
38 | .#{$prefixCls}-popup-header {
39 | display: flex;
40 | border-bottom: 1px solid #ddd;
41 |
42 | .#{$prefixCls}-popup-item {
43 | padding: 10px 30px;
44 | height: 60px;
45 | font-size: 34px;
46 | line-height: 60px;
47 | color: #000;
48 | text-align: center;
49 |
50 | &.#{$prefixCls}-header-title {
51 | flex: 1;
52 | }
53 |
54 | &.#{$prefixCls}-header-left,
55 | &.#{$prefixCls}-header-right {
56 | color: $viColor;
57 | }
58 | }
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | // 项目入口文件
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import AppContainer from './containers/AppContainer'
5 |
6 | // 接受服务器端传来的state
7 | // 初始化state
8 |
9 | // 定义根节点的render
10 | // render的时候 传进去store和router
11 | const MOUNT_NODE = document.getElementById('root')
12 |
13 | let render = () => {
14 | const routes = require('./routes/index').default()
15 |
16 | ReactDOM.render(
17 | ,
18 | MOUNT_NODE
19 | )
20 | }
21 |
22 | // 开发环境下 开启开发工具调试
23 | if (window.__DEV__) {
24 | if (window.devToolsExtension) {
25 | window.devToolsExtension.open()
26 | }
27 | }
28 |
29 | // This code is excluded from production bundle
30 | // 开发环境下的报错
31 | if (window.__DEV__) {
32 | if (module.hot) {
33 | // Development render functions
34 | const renderApp = render
35 | const renderError = (error) => {
36 | const RedBox = require('redbox-react').default
37 |
38 | ReactDOM.render(, MOUNT_NODE)
39 | }
40 |
41 | // Wrap render in try/catch
42 | render = () => {
43 | try {
44 | renderApp()
45 | } catch (error) {
46 | renderError(error)
47 | }
48 | }
49 |
50 | // Setup hot module replacement
51 | module.hot.accept('./routes/index', () =>
52 | setImmediate(() => {
53 | ReactDOM.unmountComponentAtNode(MOUNT_NODE)
54 | render()
55 | })
56 | )
57 | }
58 | }
59 |
60 | // 非开发环境下 正常执行
61 | render()
62 |
--------------------------------------------------------------------------------
/src/components/DataDisplay/List/components/List.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/4/1.
3 | */
4 | import React from 'react'
5 | import Item from './Item'
6 | import classNames from 'classnames'
7 | import '../style/list.scss'
8 |
9 | // 用于包裹item的外层组件
10 | const List = (props) => {
11 | const { prefixCls, className, renderHeader, renderFooter, children, ...restProps } = props;
12 | const wrapClass = classNames(prefixCls, className);
13 |
14 | return (
15 |
16 | {renderHeader ?
17 | {typeof renderHeader === 'function' ? renderHeader() : renderHeader}
18 |
: null}
19 | {children ?
{children}
: null}
20 | {renderFooter ?
21 | {typeof renderFooter === 'function' ? renderFooter() : renderFooter}
22 |
: null}
23 |
24 | )
25 | };
26 |
27 | // 列表展示数据项
28 | List.Item = Item;
29 |
30 | // List中的item 组件
31 | List.PropTypes = {
32 | prefixCls: React.PropTypes.string, // class前缀
33 | className: React.PropTypes.string, // 自定义class
34 | renderHeader: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.func]), // list上面的说明
35 | renderFooter: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.func]) // list结束的说明
36 | };
37 |
38 | List.defaultProps = {
39 | prefixCls: 'zby-list'
40 | };
41 |
42 | export default List
--------------------------------------------------------------------------------
/src/components/DataDisplay/Badge/components/Badge.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/20.
3 | */
4 | import React from 'react'
5 | import '../style/badge.scss'
6 |
7 | const Badge = (props) => {
8 | const {prefixCls, text, type, overflowCount, style, children} = props;
9 | let count = text;
10 |
11 | if(type === 'dot'){
12 | return (
13 |
14 | {children}
15 |
16 |
17 | );
18 | } else if (type === 'ribbon') {
19 | return (
20 |
21 | {text}
22 |
23 | );
24 | }
25 |
26 | if(text === 0) return null;
27 |
28 | if (overflowCount && Number.parseInt(text) > overflowCount) {
29 | count = `${overflowCount}+`;
30 | }
31 |
32 | return (
33 |
34 | {count}
35 |
36 | );
37 | };
38 |
39 | Badge.propTypes = {
40 | prefixCls: React.PropTypes.string, // 前缀class
41 | text: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]), // 徽章显示的字
42 | overflowCount: React.PropTypes.number, // 封顶数值 如99 text>99时候显示 99+ 为0的话不设置封顶
43 | type: React.PropTypes.oneOf(['ribbon', 'dot', 'common']) // 展现形式 右上角还是点
44 | };
45 |
46 | Badge.defaultProps = {
47 | prefixCls: 'zby-badge',
48 | text: 0,
49 | overflowCount: 99,
50 | type: 'common'
51 | };
52 |
53 | export default Badge;
--------------------------------------------------------------------------------
/src/static/sass/index.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | // px2rem并没有自动实现该功能 还是手动加上吧
4 | @mixin font-dpr($font-size){
5 | font-size: $font-size;
6 |
7 | [data-dpr="2"] & {
8 | font-size: $font-size * 2;
9 | }
10 |
11 | [data-dpr="3"] & {
12 | font-size: $font-size * 3;
13 | }
14 | }
15 |
16 | body {
17 | overflow: hidden;
18 | background-color: #f8f8fb;
19 | color: #666;
20 |
21 | &:after {
22 | content: '';
23 | position: fixed;
24 | left: 0;
25 | right: 0;
26 | top: 0;
27 | bottom: 0;
28 | background-color: #f8f8fb;
29 | z-index: -1;
30 | }
31 | }
32 |
33 | // 首页样式
34 | .page {
35 | padding-bottom: 40px;
36 |
37 | .title {
38 | padding-left: 40px;
39 |
40 | .fa {
41 | display: inline-block;
42 | margin-right: 10px;
43 | }
44 | }
45 | }
46 |
47 | .button-box {
48 | padding: 0 40px;
49 | }
50 |
51 | .button-group {
52 | display: flex;
53 | justify-content: space-between;
54 | }
55 |
56 | // 进出场动画
57 | // 飞入
58 | .slide-in-appear {
59 | transform: translate3D(100%, 0, 0);
60 | transition: all 300ms linear;
61 |
62 | &.slide-in-appear-active {
63 | transform: translate3D(0, 0, 0);
64 | }
65 | }
66 |
67 | .slide-in-enter {
68 | transform: translate3D(100%, 0, 0);
69 | transition: all 300ms linear;
70 |
71 | &.slide-in-enter-active {
72 | transform: translate3D(0, 0, 0);
73 | }
74 | }
75 |
76 | // 向左滑出
77 | .slide-in-leave {
78 | position: absolute;
79 | left: 0;
80 | top: 0;
81 | opacity: 1;
82 | transform: translate3D(0, 0, 0);
83 | transition: all 300ms linear;
84 |
85 | &.slide-in-leave-active {
86 | opacity: 0.01;
87 | transform: translate3D(-100%, 0, 0);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/components/DataEntry/PickerView/style/picker-column.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | $prefixCls: 'zby-picker-column';
4 |
5 | .#{$prefixCls} {
6 | flex: 1;
7 |
8 | &-list {
9 | position: relative;
10 | height: 400px;
11 | overflow: hidden;
12 |
13 | .#{$prefixCls}-window {
14 | position: absolute;
15 | left: 0;
16 | right: 0;
17 | top: 0;
18 | bottom: 0;
19 | background-image:
20 | linear-gradient(to bottom, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.4)),
21 | linear-gradient(to top, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.4));
22 | background-position: top, bottom;
23 | background-size: 100% 166px;
24 | background-repeat: no-repeat;
25 | z-index: 3;
26 | }
27 |
28 | .#{$prefixCls}-indicator {
29 | box-sizing: border-box;
30 | position: absolute;
31 | top: 50%;
32 | left: 0;
33 | right: 0;
34 | height: 68px;
35 | border-top: 1px solid #ddd;
36 | border-bottom: 1px solid #ddd;
37 | transform: translate3D(0, -50%, 0);
38 | z-index: 3;
39 | }
40 |
41 | .#{$prefixCls}-content {
42 | position: absolute;
43 | left: 0;
44 | top: 0;
45 | right: 0;
46 | padding: 166px 0;
47 | transform: translate3D(0, 0, 0);
48 | z-index: 1;
49 |
50 | .#{$prefixCls}-col {
51 | height: 68px;
52 | padding: 0 10px;
53 | font-size: 34px;
54 | color: #000;
55 | text-align: center;
56 | line-height: 68px;
57 | overflow: hidden;
58 | text-overflow: ellipsis;
59 | white-space:nowrap;
60 |
61 | &.selected {
62 | font-size: 38px;
63 | }
64 | }
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/src/containers/Form/TextAreaPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/18.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import {TextArea} from '../../components/Form/'
7 | import Tools from '../../components/Tools/Tools'
8 |
9 | class TextAreaPage extends React.Component {
10 | constructor (props) {
11 | super(props);
12 | this.state = {
13 | value1: '一二三四五六七八九十一二三四五六七八九十',
14 | value2: 'ABC',
15 | value3: '一二三'
16 | };
17 | }
18 | handleChange (type, e) {
19 | const value = e.target.value;
20 | console.log({[type]: value});
21 | this.setState({
22 | [type]: value
23 | });
24 | }
25 | render () {
26 | const {value1, value2, value3} = this.state;
27 |
28 | return (
29 |
30 |
31 | {Tools.linkTo('/index')}} />
32 | Input
33 |
34 |
35 |
36 |
37 |
38 |
43 |
47 |
51 |
52 |
53 | )
54 | }
55 | }
56 |
57 | export default TextAreaPage
--------------------------------------------------------------------------------
/src/containers/DataDisplay/ListPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/4/1.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import List from '../../components/DataDisplay/List/'
7 | import Tools from '../../components/Tools/Tools'
8 |
9 | const Item = List.Item;
10 |
11 | const ListPage = () => {
12 | return (
13 |
14 |
15 | {Tools.linkTo('/index')}} />
16 | List
17 |
18 |
19 |
20 |
21 | - 基本标题
22 |
23 |
24 |
25 |
26 | - 基本标题
27 |
28 |
29 |
30 | - 基本标题
31 | - 基本标题
35 |
36 |
37 |
38 |
39 |
40 | - {alert('点击成功')}}
43 | >基本标题
44 |
45 | - {alert('长按成功')}}
48 | >基本标题
49 |
50 |
51 |
52 |
53 |
54 | - 基本标题
58 |
59 |
60 |
61 | )
62 | };
63 |
64 | export default ListPage
--------------------------------------------------------------------------------
/src/components/Form/components/Switch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/31.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import { default as Sw } from '../../DataEntry/Switch/'
7 | import '../style/form.scss'
8 |
9 | const Switch = (props) => {
10 | const {required, labelName, readOnly, value, controlled, attachedText, theme, onChange} = props;
11 |
12 | function handleChange (value) {
13 | onChange({value: value});
14 | }
15 |
16 | return (
17 |
18 |
19 |
20 |
{labelName}
21 |
22 |
23 |
31 |
32 |
33 | );
34 | };
35 |
36 | function empty() {}
37 |
38 | Switch.propTypes = {
39 | required: React.PropTypes.bool,
40 | labelName: React.PropTypes.string.isRequired,
41 | readOnly: React.PropTypes.bool,
42 | value: React.PropTypes.bool.isRequired,
43 | controlled: React.PropTypes.bool,
44 | attachedText: React.PropTypes.array,
45 | theme: React.PropTypes.oneOf(['iOS', 'android']), // 主题 枚举 iOS风格和Android风格
46 | onChange: React.PropTypes.func,
47 | };
48 |
49 | Switch.defaultProps = {
50 | required: false,
51 | readOnly: false,
52 | controlled: true,
53 | theme: 'iOS',
54 | onChange: empty
55 | };
56 |
57 | export default Switch
--------------------------------------------------------------------------------
/src/components/DataDisplay/Tag/components/Tag.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/6/22.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import Touchable from 'rc-touchable'
7 | import '../style/index.scss'
8 |
9 | const Tag = (props) => {
10 | const {content, selected, closeable, disabled, onClick, onClose, style} = props;
11 |
12 | const handleClick = () => {
13 | if(onClick) onClick(props);
14 | };
15 |
16 | const handleClose = () => {
17 | if(onClose) onClose(props);
18 | };
19 |
20 | let closeDOM = [];
21 |
22 | if(closeable) {
23 | closeDOM.push(
24 |
28 |
29 |
30 | );
31 | }
32 |
33 | return (
34 |
35 |
40 | {content}
41 |
42 | {closeDOM}
43 |
44 | );
45 | };
46 |
47 | Tag.propTypes = {
48 | content: React.PropTypes.any, // tag显示内容
49 | selected: React.PropTypes.bool, // 当前选中状态
50 | closeable: React.PropTypes.bool, // 是否显示关闭按钮
51 | disabled: React.PropTypes.bool, // 是否可点击
52 | onClick: React.PropTypes.func, // 点击之后的回调
53 | onClose: React.PropTypes.func, // 关闭之后的回调函数
54 | style: React.PropTypes.object // 自定义样式
55 | };
56 |
57 | Tag.defaultProps = {
58 | selected: false,
59 | closeable: false,
60 | disabled: false
61 | };
62 |
63 | export default Tag
64 |
--------------------------------------------------------------------------------
/src/containers/Form/SelectPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/19.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import {Select} from '../../components/Form/'
7 | import Tools from '../../components/Tools/Tools'
8 |
9 | class SelectPage extends React.Component {
10 | constructor (props) {
11 | super(props);
12 | this.state = {
13 | value1: {value: '选项一'},
14 | value2: {value: '选项二'},
15 | value3: '选项三'
16 | };
17 | }
18 | handleChange (type, value) {
19 | this.setState({
20 | [type]: value
21 | });
22 | }
23 | render () {
24 | const {value1, value2, value3} = this.state;
25 | const data = [{label: '选项一', value: '选项一'}, {label: '选项二', value: '选项二'}, {label: '选项三', value: '选项三'}];
26 |
27 | return (
28 |
29 |
30 | {Tools.linkTo('/index')}} />
31 | Select
32 |
33 |
34 |
35 |
36 |
37 |
43 |
50 |
51 |
52 | )
53 | }
54 | }
55 |
56 | export default SelectPage
--------------------------------------------------------------------------------
/src/components/Form/components/Number.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/31.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import '../style/form.scss'
7 |
8 | const Number = (props) => {
9 | const {required, labelName, readOnly, controlled, value, placeHolder, unit, onChange} = props;
10 |
11 | function handleChange (e) {
12 | onChange({value: e.target.value});
13 | }
14 |
15 | return (
16 |
17 |
18 |
19 |
{labelName}
20 |
21 |
22 | {readOnly ?
{value ? value : placeHolder}
:
23 | controlled ?
:
27 |
}
31 |
{unit}
32 |
33 |
34 | )
35 | };
36 |
37 | function empty() {}
38 |
39 | Number.PropTypes = {
40 | required: React.PropTypes.bool,
41 | labelName: React.PropTypes.string.isRequired,
42 | readOnly: React.PropTypes.bool,
43 | controlled: React.PropTypes.bool,
44 | value: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]),
45 | placeHolder: React.PropTypes.string,
46 | unit: React.PropTypes.string.isRequired,
47 | onChange: React.PropTypes.func,
48 | };
49 |
50 | Number.defaultProps = {
51 | required: false,
52 | readOnly: false,
53 | controlled: true,
54 | onChange: empty
55 | };
56 |
57 | export default Number;
58 |
--------------------------------------------------------------------------------
/src/containers/Form/SwitchPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/31.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import {Switch} from '../../components/Form/'
7 | import Tools from '../../components/Tools/Tools'
8 |
9 | class SwitchPage extends React.Component {
10 | constructor (props) {
11 | super(props);
12 | this.state = {
13 | value1: true,
14 | value2: true,
15 | value3: true
16 | };
17 | }
18 | handleChange (type, value) {
19 | this.setState({
20 | [type]: value.value
21 | });
22 | }
23 | render () {
24 | const {value1, value2, value3} = this.state;
25 |
26 | return (
27 |
28 |
29 | {Tools.linkTo('/index')}} />
30 | Switch
31 |
32 |
33 |
34 |
35 |
36 |
42 |
49 |
54 |
55 |
56 | )
57 | }
58 | }
59 |
60 | export default SwitchPage
--------------------------------------------------------------------------------
/src/containers/Form/CheckboxPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/27.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import {Checkbox} from '../../components/Form/'
7 | import Tools from '../../components/Tools/Tools'
8 |
9 | class CheckboxPage extends React.Component {
10 | constructor (props) {
11 | super(props);
12 | this.state = {
13 | value1: {required: true, labelName: '单选', value: ['cherry']},
14 | value2: {required: true, readOnly: true, labelName: '整个readonly', value: ['apple', 'banana', 'cherry', 'date']}
15 | };
16 | }
17 | handleChange (type, value) {
18 | this.setState((previousState)=>{
19 | previousState[type] = Object.assign(previousState[type], value);
20 | return {...previousState}
21 | });
22 | }
23 | render () {
24 | const {value1, value2} = this.state;
25 | const options1 = [
26 | {label: '苹果', value: 'apple'}, {label: '香蕉', value: 'banana'},
27 | {label: '樱桃', value: 'cherry', disabled: true}, {label: '枣', value: 'date'}
28 | ];
29 |
30 | return (
31 |
32 |
33 | {Tools.linkTo('/index')}} />
34 | Checkbox
35 |
36 |
37 |
38 |
39 |
44 |
48 |
49 |
50 | )
51 | }
52 | }
53 |
54 | export default CheckboxPage
--------------------------------------------------------------------------------
/src/components/Form/components/DateTime.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/26.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import DatePicker from '../../DataEntry/DataPicker/'
7 | import List from '../../DataDisplay/List/'
8 | import '../style/form.scss'
9 |
10 | const DateTime = (props) => {
11 | const {required, labelName, value, readOnly, kind, onChange} = props;
12 | let valueText = ;
13 |
14 | if(value){
15 | valueText = kind === 'date' ? value.format('YYYY-MM-DD') : value.format('YYYY-MM-DD HH:mm');
16 | }
17 |
18 | return (
19 |
20 |
21 |
22 |
{labelName}
23 |
24 |
25 | {readOnly ?
26 |
{valueText}
27 | :
28 |
{onChange({value: value})}}
33 | >
34 | {valueText}
35 |
36 | }
37 |
38 |
39 | );
40 | };
41 |
42 | function empty() {}
43 |
44 | DateTime.PropTypes = {
45 | required: React.PropTypes.bool,
46 | labelName: React.PropTypes.string.isRequired,
47 | value: React.PropTypes.string,
48 | readOnly: React.PropTypes.bool,
49 | kind: React.PropTypes.oneOf(['date', 'time']),
50 | onChange: React.PropTypes.func
51 | };
52 |
53 | DateTime.defaultProps = {
54 | required: false,
55 | readOnly: false,
56 | kind: 'date',
57 | onChange: empty
58 | };
59 |
60 | export default DateTime;
--------------------------------------------------------------------------------
/src/components/Feedback/Toast/components/Toast.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/6/13.
3 | */
4 | import Notification from './Notification'
5 |
6 | // Toast组件比较特殊
7 | // 因为不会被直接渲染在DOM中
8 | // 而是动态插入页面中
9 | // Toast组件核心就是通过Notification暴露的重写方法 动态改变Notification
10 | let newNotification;
11 |
12 | // 获得一个Notification
13 | const getNewNotification = () => {
14 | // 单例 保持页面始终只有一个Notification
15 | if (!newNotification) {
16 | newNotification = Notification.reWrite();
17 | }
18 |
19 | return newNotification;
20 | };
21 |
22 | // notice方法实际上就是集合参数 完成对Notification的改变
23 | const notice = (type, content, mask = false, iconClass, onClose, duration) => {
24 | let notificationInstance = getNewNotification();
25 |
26 | notificationInstance.notice({
27 | duration,
28 | type,
29 | mask,
30 | iconClass,
31 | content,
32 | onClose: () => {
33 | if (onClose) onClose();
34 | },
35 | });
36 | };
37 |
38 | export default {
39 | // 无动画
40 | show: (content, mask, iconClass, onClose, duration) => (notice(undefined, content, mask, iconClass, onClose, duration)),
41 | // 翻转效果
42 | info: (content, mask, iconClass, onClose, duration) => (notice('info', content, mask, iconClass, onClose, duration)),
43 | // 缩放效果
44 | success: (content, mask, iconClass, onClose, duration) => (notice('success', content, mask, iconClass, onClose, duration)),
45 | // 从下方滑入
46 | warning: (content, mask, iconClass, onClose, duration) => (notice('warning', content, mask, iconClass, onClose, duration)),
47 | // 抖动
48 | error: (content, mask, iconClass, onClose, duration) => (notice('error', content, mask, iconClass, onClose, duration)),
49 | // loading
50 | loading: (content) => (notice(undefined, content || '加载中...', true, 'fa-circle-o-notch fa-spin', undefined, 0)),
51 | // 销毁
52 | hide() {
53 | if (newNotification) {
54 | newNotification.destroy();
55 | newNotification = null;
56 | }
57 | },
58 | }
--------------------------------------------------------------------------------
/src/components/DataDisplay/List/style/item.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-item';
6 |
7 | .#{$prefixCls} {
8 | box-sizing: border-box;
9 | position: relative;
10 | display: flex;
11 | padding: 20px 40px;
12 | min-height: 88px;
13 | vertical-align: middle;
14 | overflow: hidden;
15 | align-items: center;
16 | transition: background-color .3s;
17 |
18 | &.#{$prefixCls}-active {
19 | background-color: $lightGray;
20 | }
21 |
22 | &.disabled {
23 | .#{$prefixCls}-content,
24 | .#{$prefixCls}-extra,
25 | .#{$prefixCls}-subtitle {
26 | color: $darkGray;
27 | }
28 | }
29 |
30 | // 缩略图
31 | img {
32 | display: inline-block;
33 | width: 80px;
34 | height: 80px;
35 | border-radius: 50%;
36 | background: $darkGray;
37 | overflow: hidden;
38 | }
39 |
40 | .fa-thumb {
41 | display: inline-block;
42 | margin-right: 20px;
43 | font-size: 32px;
44 | line-height: 1.5;
45 | }
46 |
47 | // 主题
48 | .#{$prefixCls}-content {
49 | flex: 1;
50 | font-size: 32px;
51 | line-height: 1.5;
52 | color: #000;
53 | overflow: hidden;
54 |
55 | &.wrap {
56 | overflow: hidden;
57 | text-overflow: ellipsis;
58 | white-space: nowrap;
59 | }
60 |
61 | // 副标题
62 | .#{$prefixCls}-subtitle {
63 | color: $textGray;
64 | font-size: 28px;
65 | line-height: 1.5;
66 | margin-top: .12rem;
67 | }
68 | }
69 |
70 | // 右侧额外提示
71 | .#{$prefixCls}-extra {
72 | flex-basis: 36%;
73 | color: $textGray;
74 | font-size: 28px;
75 | text-align: right;
76 |
77 | &.wrap {
78 | overflow: hidden;
79 | text-overflow: ellipsis;
80 | white-space: nowrap;
81 | }
82 | }
83 |
84 | // 右侧箭头
85 | .#{$prefixCls}-arrow {
86 | display: block;
87 | width: 30px;
88 | height: 56px;
89 | margin-left: 10px;
90 | text-align: center;
91 |
92 | .fa {
93 | display: block;
94 | font-size: 48px;
95 | color: $textGray;
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/src/containers/Form/NumberPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/31.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import {Number} from '../../components/Form/'
7 | import Tools from '../../components/Tools/Tools'
8 |
9 | class NumberPage extends React.Component {
10 | constructor (props) {
11 | super(props);
12 | this.state = {
13 | value1: 12.2,
14 | value2: 0,
15 | value3: '1.2'
16 | };
17 | }
18 | handleChange (type, value) {
19 | this.setState({
20 | [type]: value.value
21 | });
22 | }
23 | render () {
24 | const {value1, value2, value3} = this.state;
25 |
26 | return (
27 |
28 |
29 | {Tools.linkTo('/index')}} />
30 | Number
31 |
32 |
33 |
34 |
35 |
36 |
44 |
52 |
59 |
60 |
61 | )
62 | }
63 | }
64 |
65 | export default NumberPage
66 |
--------------------------------------------------------------------------------
/src/components/DataEntry/DataPicker/util/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/7.
3 | */
4 | import moment from 'moment'
5 |
6 | // 公共 函数和数据
7 | export const monthArray = [
8 | {label: '1月', value: '0'},{label: '2月', value: '1'},{label: '3月', value: '2'},{label: '4月', value: '3'},
9 | {label: '5月', value: '4'},{label: '6月', value: '5'},{label: '7月', value: '6'},{label: '8月', value: '7'},
10 | {label: '9月', value: '8'},{label: '10月', value: '9'},{label: '11月', value: '10'},{label: '12月', value: '11'}
11 | ];
12 |
13 | export const hourArray = [
14 | {label: '0点', value: '0'},{label: '1点', value: '1'},{label: '2点', value: '2'},{label: '3点', value: '3'},
15 | {label: '4点', value: '4'},{label: '5点', value: '5'},{label: '6点', value: '6'},{label: '7点', value: '7'},
16 | {label: '8点', value: '8'},{label: '9点', value: '9'},{label: '10点', value: '10'},{label: '11点', value: '11'},
17 | {label: '12点', value: '12'},{label: '13点', value: '13'},{label: '14点', value: '14'},{label: '15点', value: '15'},
18 | {label: '16点', value: '16'},{label: '17点', value: '17'},{label: '18点', value: '18'},{label: '19点', value: '19'},
19 | {label: '20点', value: '20'},{label: '21点', value: '21'},{label: '22点', value: '22'},{label: '23点', value: '23'}
20 | ];
21 |
22 | export const checkDaysByYearMonth = (value) => {
23 | const month = value.month();
24 |
25 | // 判断大小月
26 | if([0,2,4,6,7,9,11].indexOf(month) >= 0){
27 | // 大月 31天
28 | return 31;
29 | } else if ([3,5,8,10].indexOf(month) >= 0) {
30 | // 小月 30天
31 | return 30;
32 | } else {
33 | // 2月 判断是否闰年
34 | if(moment([value.year()]).isLeapYear()){
35 | // 闰年 29天
36 | return 29;
37 | }
38 |
39 | return 28;
40 | }
41 | };
42 |
43 | export const resetPosition = (array, newValue, index) => {
44 | // 如果比最后一个值大 去最后一个 否则 取第一个
45 | // 取第一个
46 | if(Number.parseInt(newValue[index]) > Number.parseInt(array[array.length - 1].value)){
47 | newValue[index] = array[array.length - 1].value;
48 | } else {
49 | newValue[index] = array[0].value;
50 | }
51 |
52 | return newValue;
53 | };
--------------------------------------------------------------------------------
/src/routes/index.js:
--------------------------------------------------------------------------------
1 | // 路由入口文件
2 | import Layout from '../layout/index'
3 | import Home from './Home/'
4 |
5 | export const createRoutes = () => ({
6 | path: '/',
7 | component: Layout,
8 | indexRoute: { component: Home },
9 | getChildRoutes(location, callback) {
10 | require.ensure([], function (require) {
11 | callback(null, [
12 | require('./DataDisplay').default(),
13 | require('./DataEntry').default(),
14 | require('./Feedback').default(),
15 | require('./Form').default(),
16 | ])
17 | })
18 | }
19 | // childRoutes: [
20 | // {path: 'index', component: Home},
21 | // {path: '/data-entry', childRoutes: [
22 | // {path: 'button', component: Button},
23 | // {path: 'switch', component: Switch},
24 | // {path: 'date-picker', component: DatePicker},
25 | // {path: 'picker', component: Picker},
26 | // {path: 'picker-view', component: PickerView},
27 | // {path: 'uploader', component: Uploader},
28 | // ]},
29 | // {path: '/data-display', childRoutes: [
30 | // {path: 'list', component: List},
31 | // {path: 'carousel', component: Carousel},
32 | // {path: 'Tag', component: TagPage},
33 | // ]},
34 | // {path: '/form', childRoutes: [
35 | // {path: 'input', component: Input},
36 | // {path: 'textarea', component: TextArea},
37 | // {path: 'number', component: Number},
38 | // {path: 'switch', component: FormSwitch},
39 | // {path: 'select', component: Select},
40 | // {path: 'date-range', component: DateRange},
41 | // {path: 'date-time', component: DateTime},
42 | // {path: 'checkbox', component: Checkbox},
43 | // {path: 'form', component: Form},
44 | // ]},
45 | // {path: '/feedback', childRoutes: [
46 | // {path: 'list', component: ToastPage},
47 | // {path: 'popover', component: PopoverPage},
48 | // ]}
49 | // ]
50 | });
51 |
52 | export default createRoutes
--------------------------------------------------------------------------------
/src/components/Form/components/Select.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/19.
3 | */
4 | import React from 'react'
5 | import Picker from '../../DataEntry/Picker/'
6 | import List from '../../DataDisplay/List/'
7 | import classNames from 'classnames'
8 | import '../style/form.scss'
9 |
10 | const Select = (props) => {
11 | const {required, labelName, value, data, readOnly, error, onChange} = props;
12 |
13 | function handleChange (value) {
14 | onChange({value: value[0]});
15 | }
16 |
17 | const valueText = function () {
18 | let result;
19 | for (let i of data) {
20 | if (i.value === value) {
21 | result = i.label;
22 | }
23 | }
24 |
25 | return result;
26 | }();
27 |
28 | return (
29 |
30 |
31 |
32 |
{labelName}
33 |
34 |
35 | {readOnly ?
36 |
{value}
37 | :
38 |
45 | {valueText}
46 | }
47 |
48 |
49 | );
50 | };
51 |
52 | function empty() {}
53 |
54 | Select.PropTypes = {
55 | required: React.PropTypes.bool,
56 | labelName: React.PropTypes.string.isRequired,
57 | data: React.PropTypes.array.isRequired,
58 | value: React.PropTypes.string,
59 | readOnly: React.PropTypes.bool,
60 | error: React.PropTypes.string,
61 | onChange: React.PropTypes.func
62 | };
63 |
64 | Select.defaultProps = {
65 | required: false,
66 | value: [],
67 | readOnly: false,
68 | onChange: empty
69 | };
70 |
71 | export default Select;
--------------------------------------------------------------------------------
/src/components/Form/components/TextArea.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/18.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import '../style/form.scss'
7 | import {showError, clearError} from './Func'
8 |
9 | const TextArea = (props) => {
10 | const {required, labelName, readOnly, controlled, value, placeHolder, error, onChange} = props;
11 |
12 | function handleChange (e) {
13 | onChange({value: e.target.value});
14 | }
15 |
16 | function handleFocus () {
17 | if(!!error){
18 | showError(error);
19 | }
20 | }
21 |
22 | function handleBlur () {
23 | if(!!error){
24 | clearError(error);
25 | }
26 | }
27 |
28 | return (
29 |
30 |
31 |
32 |
{labelName}
33 |
34 |
35 | {readOnly ?
{value ? value : placeHolder}
:
36 | controlled ?
:
41 |
}
45 |
46 |
47 | )
48 | };
49 |
50 | function empty() {}
51 |
52 | TextArea.PropTypes = {
53 | required: React.PropTypes.bool,
54 | labelName: React.PropTypes.string.isRequired,
55 | readOnly: React.PropTypes.bool,
56 | controlled: React.PropTypes.bool,
57 | value: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]),
58 | placeHolder: React.PropTypes.string,
59 | error: React.PropTypes.string,
60 | onChange: React.PropTypes.func,
61 | };
62 |
63 | TextArea.defaultProps = {
64 | required: false,
65 | readOnly: false,
66 | controlled: true,
67 | onChange: empty
68 | };
69 |
70 | export default TextArea;
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/Button/ButtonPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/13.
3 | */
4 | import React from 'react'
5 | import ListTitle from 'components/DataDisplay/ListTitle/'
6 | import List from 'components/DataDisplay/List/'
7 | import Button from 'components/DataEntry/Button/'
8 | import Tools from 'components/Tools/Tools'
9 |
10 | const Item = List.Item;
11 |
12 | const ButtonPage = () => {
13 | return (
14 |
15 |
16 | {Tools.linkTo('/')}} />
17 | Button
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | - inline}>行内按钮
46 | - inline}
49 | >
50 | 行内按钮
51 |
52 |
53 |
54 | )
55 | };
56 |
57 | export default ButtonPage
--------------------------------------------------------------------------------
/src/routes/DataDisplay/containers/List/ListPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/14.
3 | */
4 | import React from 'react'
5 | import List from 'components/DataDisplay/List/'
6 | import Tools from 'components/Tools/Tools'
7 |
8 | const Item = List.Item;
9 |
10 | const ListPage = () => {
11 | return (
12 |
13 |
14 | {Tools.linkTo('/')}} />
15 | List
16 |
17 |
18 |
19 | - 基本标题
20 | - {alert('click')}}>点击事件
21 | - {alert('long-press')}}>长按事件
22 | - 禁止点击
23 | }
25 | arrow="horizontal"
26 | subtitle="前端工程师 💻&🎧️️"
27 | >
28 | Aus
29 |
30 | - 我的消息
31 | - 我的订单
32 |
33 |
34 |
35 | -
36 | 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
37 |
38 | -
39 | 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
40 |
41 | -
42 | 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
43 |
44 | -
45 | 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
46 |
47 |
48 |
49 | )
50 | };
51 |
52 | export default ListPage
--------------------------------------------------------------------------------
/src/components/DataEntry/Button/components/Button.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/4/7.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import Touchable from 'rc-touchable'
7 | import '../style/index.scss'
8 |
9 | // button 组件
10 | const Button = (props) => {
11 | const {prefixCls, type, disabled, group, inline, loading, activeClassName, iconClass, className, onClick, children} = props;
12 |
13 | const cn = classNames([prefixCls,{
14 | 'inline': inline,
15 | 'group': group,
16 | 'primary': type === 'primary',
17 | 'ghost': type === 'ghost',
18 | 'disabled': disabled || loading
19 | }, className]);
20 |
21 | // 有loading的时候,显示loading icon
22 | const iconDOM = loading ?
23 |
24 | : iconClass ? : '';
25 |
26 | return (
27 |
32 |
33 | {iconDOM}
34 | {children}
35 |
36 |
37 | )
38 | };
39 |
40 | Button.propTypes = {
41 | prefixCls: React.PropTypes.string, // 前缀class
42 | type: React.PropTypes.oneOf(['default', 'primary', 'ghost']), // 类型 枚举 有 default(白底黑字) primary(绿底白字) ghost(白底绿字) 三种
43 | disabled: React.PropTypes.bool, // 是否不可点击 不可点击时 样式会有调整 默认false
44 | group: React.PropTypes.bool, // 是否 按按钮组显示 默认是false
45 | inline: React.PropTypes.bool, // 是否是行内显示 默认false
46 | loading: React.PropTypes.bool, // 是否显示loading loading时候按钮显示loading icon并且不可点击
47 | activeClassName: React.PropTypes.string, // 点击时候的类名
48 | iconClass: React.PropTypes.string, // icon类名 只支持awesome的icon
49 | className: React.PropTypes.string, // 自定义class
50 | onClick: React.PropTypes.func, // 点击的回调函数
51 | };
52 |
53 | Button.defaultProps = {
54 | prefixCls: 'zby-button',
55 | type: 'default',
56 | disabled: false,
57 | group: false,
58 | inline: false,
59 | loading: false,
60 | activeClassName: 'zby-button-active'
61 | };
62 |
63 | export default Button
--------------------------------------------------------------------------------
/src/components/DataEntry/Button/style/index.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-button';
6 |
7 | // Button
8 | .#{$prefixCls} {
9 | display: block;
10 | margin: 20px 0;
11 | height: 100px;
12 | border-radius: 10px;
13 | background-color: #fff;
14 | border: 2px solid $lightGray;
15 | text-align: center;
16 | line-height: 100px;
17 | font-size: 32px;
18 | overflow: hidden;
19 | clear: both;
20 | outline: none;
21 | opacity: 1;
22 |
23 | transition: opacity .5s;
24 |
25 | &.inline {
26 | display: inline-block;
27 | margin: 0 10px;
28 | padding: 0 20px;
29 | height: auto;
30 | font-size: 28px;
31 | line-height: 50px;
32 | vertical-align: middle;
33 | }
34 | // 按钮组按钮
35 | &.group {
36 | margin: 20px 10px;
37 | padding: 0 40px;
38 | flex: 1;
39 | }
40 |
41 | // disabled的时候 default的按钮是灰色的
42 | &.disabled {
43 | background-color: $lightGray;
44 |
45 | .#{$prefixCls}-text, .fa {
46 | color: $darkGray;
47 | }
48 |
49 | // primary 变透明
50 | &.primary {
51 | opacity: .5;
52 | }
53 |
54 | // ghost
55 | &.ghost {
56 | border-color: $darkGray;
57 | opacity: .5;
58 |
59 | .#{$prefixCls}-text, .fa {
60 | color: $darkGray;
61 | }
62 | }
63 | }
64 |
65 | // primary按钮 绿底白字
66 | &.primary {
67 | background-color: $viColor;
68 | border: none;
69 |
70 | &.#{$prefixCls}-active {
71 | background-color: $darkViColor;
72 | }
73 |
74 | .#{$prefixCls}-text, .fa {
75 | color: #fff;
76 | }
77 | }
78 | // 幽灵按钮 底色透明
79 | &.ghost {
80 | background-color: transparent;
81 | border-color: $viColor;
82 |
83 | &.#{$prefixCls}-active {
84 | background-color: $viColor;
85 | .#{$prefixCls}-text, .fa {
86 | color: #fff;
87 | }
88 | }
89 |
90 | .#{$prefixCls}-text, .fa {
91 | color: $viColor;
92 | }
93 | }
94 |
95 | &.#{$prefixCls}-active {
96 | background-color: $lightGray;
97 | }
98 |
99 | .#{$prefixCls}-text {
100 | display: inline-block;
101 | color: #000;
102 | }
103 |
104 | .fa {
105 | display: inline-block;
106 | margin-right: 10px;
107 | color: #000;
108 | }
109 | }
--------------------------------------------------------------------------------
/src/routes/Feedback/containers/Toast/ToastPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/18.
3 | */
4 | import React from 'react'
5 | import ListTitle from 'components/DataDisplay/ListTitle/'
6 | import Button from 'components/DataEntry/Button/components/Button'
7 | import Toast from 'components/Feedback/Toast/'
8 | import Tools from 'components/Tools/Tools'
9 |
10 | const ToastPage = () => {
11 | const commonInfo = () => {
12 | Toast.info('普通的Toast我普通的摇!!', true);
13 | };
14 | const commonSuccess = () => {
15 | Toast.success('操作成功', false, 'fa-check');
16 | };
17 | const commonError = () => {
18 | Toast.error('有错误!!', false, undefined, ()=>{console.log('callback');});
19 | };
20 | const commonToast = () => {
21 | Toast.info('欢迎来到本直播间');
22 | };
23 | const successToast = () => {
24 | Toast.success('操作成功!', false, 'fa-check');
25 | };
26 | const errorToast = () => {
27 | Toast.error('操作失败!', false, 'fa-times');
28 | };
29 | const warningToast = () => {
30 | Toast.warning('警告:手机2s后爆炸', false, 'fa-exclamation-triangle');
31 | };
32 | const loadingToast = () => {
33 | Toast.loading();
34 | const timer = setTimeout(()=>{
35 | Toast.hide();
36 | clearTimeout(timer);
37 | }, 3000);
38 | };
39 |
40 | return (
41 |
42 |
43 | {Tools.linkTo('/')}} />
44 | Toast
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | )
63 | };
64 |
65 | export default ToastPage
--------------------------------------------------------------------------------
/src/routes/DataDisplay/containers/Tooltip/TooltipPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/13.
3 | */
4 | import React from 'react'
5 | import List from 'components/DataDisplay/List/'
6 | import Switch from 'components/DataEntry/Switch/'
7 | import ListTitle from 'components/DataDisplay/ListTitle/'
8 | import Tooltip from 'components/DataDisplay/Tooltip/'
9 | import Button from 'components/DataEntry/Button/'
10 | import Tools from 'components/Tools/Tools'
11 | const Item = List.Item;
12 |
13 | const TooltipPage = () => {
14 |
15 | const switchTooltipRight =
16 |
17 | ;
18 |
19 | const switchTooltipLeft =
20 |
21 | ;
22 |
23 | const switchTooltipAuto =
24 |
25 | ;
26 |
27 | return (
28 |
29 |
30 | {Tools.linkTo('/')}} />
31 | Tooltip
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 提示文字}
42 | trigger='click'
43 | >
44 |
45 |
46 | 长按提示文字}
48 | trigger='long-press'
49 | >
50 |
51 |
52 |
53 |
54 |
55 | -
56 | 显示在左侧
57 |
58 | -
59 | {switchTooltipRight}
60 |
61 | -
62 | 自动计算
63 |
64 |
65 |
66 | )
67 | };
68 |
69 | export default TooltipPage
--------------------------------------------------------------------------------
/src/components/Form/components/Checkbox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/27.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import '../style/form.scss'
7 |
8 | const Checkbox = (props) => {
9 | const {required, labelName, value, options, readOnly, onChange} = props;
10 |
11 | const itemDOM = [];
12 |
13 | function handleChange (targetValue) {
14 | if(value.indexOf(targetValue) !== -1){
15 | value.splice(value.indexOf(targetValue), 1);
16 | onChange({value: value});
17 | return;
18 | }
19 |
20 | value.push(targetValue);
21 | onChange({value: value});
22 | }
23 |
24 | options.map((item, i)=>{
25 | itemDOM.push(
26 | {readOnly || item.disabled ? empty() : handleChange(item.value)}}
30 | >
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
{item.label}
40 |
41 | );
42 | });
43 |
44 | return (
45 |
46 |
47 |
48 |
{labelName}
49 |
50 |
51 | {itemDOM}
52 |
53 |
54 | )
55 | };
56 |
57 | function empty() {}
58 |
59 | Checkbox.PropTypes = {
60 | required: React.PropTypes.bool,
61 | labelName: React.PropTypes.string.isRequired,
62 | options: React.PropTypes.array.isRequired,
63 | value: React.PropTypes.array,
64 | readOnly: React.PropTypes.bool,
65 | onChange: React.PropTypes.func
66 | };
67 |
68 | Checkbox.defaultProps = {
69 | required: false,
70 | value: [],
71 | readOnly: false,
72 | onChange: empty
73 | };
74 |
75 | export default Checkbox;
--------------------------------------------------------------------------------
/src/containers/DataDisplay/TagPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/6/22.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import Tools from '../../components/Tools/Tools'
7 | import Tag from '../../components/DataDisplay/Tag/'
8 |
9 | const handleTagClick = (item) => {
10 | console.log(item);
11 | };
12 |
13 | const tags = [
14 | {content: 'Basic', selected: false, closeable: true, onClick: handleTagClick, onClose: handleTagClick},
15 | {content: 'Basic', selected: true, closeable: false, onClick: handleTagClick},
16 | {content: 'Pink', selected: false, closeable: false, style: {color: '#f5317f', background: '#fdd8e7', borderColor: '#fdd8e7'}},
17 | {content: 带icon的Tag, selected: false, closeable: false, style: {color: '#f04134', background: '#fcdbd9', borderColor: '#fcdbd9'}},
18 | {content: 'Disabled', selected: false, closeable: false, disabled: true},
19 | {content: 'Orange', selected: false, closeable: false, disabled: true, style: {color: '#f56a00', background: '#fde3cf', borderColor: '#fde3cf'}},
20 | {content: 'Green', selected: false, closeable: false, disabled: true, style: {color: '#00a854', background: '#cfefdf', borderColor: '#cfefdf'}},
21 | {content: 'Cyan', selected: false, closeable: false, disabled: true, style: {color: '#00a2ae', background: '#cfedf0', borderColor: '#cfedf0'}},
22 | {content: 'Blue', selected: false, closeable: false, disabled: true, style: {color: '#108ee9', background: '#d2eafb', borderColor: '#d2eafb'}},
23 | {content: 'Purple', selected: false, closeable: false, disabled: true, style: {color: '#7265e6', background: '#e4e2fa', borderColor: '#e4e2fa'}},
24 | ];
25 |
26 | const TagPage = () => {
27 | const getTagsDOM = () => {
28 | let result = [];
29 |
30 | tags.map((item, index)=>{
31 | result.push();
32 | });
33 |
34 | return result;
35 | };
36 |
37 | const tagsDOM = getTagsDOM();
38 |
39 | return (
40 |
41 |
42 | {Tools.linkTo('/index')}} />
43 | Tag
44 |
45 |
46 |
47 |
48 | {tagsDOM}
49 |
50 |
51 | );
52 | };
53 |
54 | export default TagPage;
--------------------------------------------------------------------------------
/src/containers/Form/DateTimePage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/26.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import {DateTime} from '../../components/Form/'
7 | import Tools from '../../components/Tools/Tools'
8 | import moment from 'moment'
9 |
10 | class DateTimePage extends React.Component {
11 | constructor (props) {
12 | super(props);
13 | this.state = {
14 | value1: {required: true, labelName: '日期', value: moment()},
15 | value2: {required: false, labelName: '日期时间', value: moment(), kind: 'datetime'},
16 | value3: {required: false, labelName: '日期时间', value: undefined, kind: 'datetime'},
17 | value4: {readOnly: true, required: false, labelName: '日期时间', value: moment(), kind: 'datetime'},
18 | value5: {readOnly: true, required: false, labelName: '日期时间', value: moment(), kind: 'datetime'},
19 | };
20 | }
21 | handleChange (type, value) {
22 | this.setState((previousState)=>{
23 | previousState[type] = Object.assign(previousState[type], value);
24 | return {...previousState};
25 | });
26 | }
27 | render () {
28 | const {value1, value2, value3, value4, value5} = this.state;
29 |
30 | return (
31 |
32 |
33 | {Tools.linkTo('/index')}} />
34 | DateTime
35 |
36 |
37 |
38 |
39 |
43 |
47 |
51 |
52 |
53 |
54 |
57 |
60 |
61 |
62 | )
63 | }
64 | }
65 |
66 | export default DateTimePage
--------------------------------------------------------------------------------
/src/routes/DataDisplay/containers/Carousel/CarouselPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/10.
3 | */
4 | import React from 'react'
5 | import ListTitle from 'components/DataDisplay/ListTitle/'
6 | import Carousel from 'components/DataDisplay/Carousel/'
7 | import Tools from 'components/Tools/Tools'
8 | import StarBucks from '../../../../static/img/star-bucks.jpg'
9 |
10 | const CarouselPage = () => {
11 | const carouselData = [
12 | {content: 'Figure1', style: {'textAlign': 'center', 'fontSize': '1rem', 'lineHeight': '4rem', 'color': '#fff', 'background': '#26a69a'}},
13 | {content: 'Figure2', style: {'textAlign': 'center', 'fontSize': '1rem', 'lineHeight': '4rem', 'color': '#fff', 'background': '#ff751f'}},
14 | {content: Figure3
},
15 | {content:
}
16 | ];
17 |
18 | return (
19 |
20 |
21 | {Tools.linkTo('/')}} />
22 | Carousel
23 |
24 |
25 |
26 |
27 |
32 |
33 |
34 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
53 |
54 |
55 |
56 | {console.log('afterChange index is ' + nextIndex)}}
61 | />
62 |
63 |
64 | )
65 | };
66 |
67 | export default CarouselPage
--------------------------------------------------------------------------------
/src/routes/DataDisplay/containers/Badge/BadgePage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/20.
3 | */
4 | import React from 'react'
5 | import Badge from 'components/DataDisplay/Badge'
6 | import List from 'components/DataDisplay/List'
7 | import Tools from 'components/Tools/Tools'
8 |
9 | const Item = List.Item;
10 |
11 | const BadgePage = () => {
12 | return (
13 |
14 |
15 | {Tools.linkTo('/')}} />
16 | Badge
17 |
18 |
19 |
20 | } arrow="horizontal">文字
21 | } arrow="horizontal">数字
22 | } arrow="horizontal">数字超过上限
23 | -
24 | 徽章在左侧
25 |
26 | -
27 | 自定义徽章
28 |
36 |
42 |
48 |
49 |
50 |
51 |
52 | -
55 |
56 |
57 | }
58 | >
59 | dot形式 右上角点表示
60 |
61 |
62 | }>
63 | 右侧角落显示
64 |
65 |
66 |
67 | );
68 | };
69 |
70 | export default BadgePage;
--------------------------------------------------------------------------------
/src/components/DataDisplay/Tooltip/style/tooltip.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-tooltip';
6 |
7 | // Tooltip
8 | .#{$prefixCls} {
9 | display: block;
10 | //position: relative;
11 |
12 | &-title {
13 | position: absolute;
14 | max-width: 40vw;
15 | padding: 20px 40px;
16 | background: #000;
17 | border: 2px solid #000;
18 | border-radius: 10px;
19 | font-size: 32px;
20 | color: #fff;
21 |
22 | .fa {
23 | display: inline-block;
24 | margin-right: 10px;
25 | }
26 |
27 | &.hidden {
28 | display: none;
29 | }
30 |
31 | &.top {
32 | transform: translate3D(0, -10px, 0);
33 | transform-origin: center bottom;
34 | animation: turnUp .3s;
35 |
36 | &:after {
37 | left: 50%;
38 | top: 100%;
39 | border-top: 12px solid #000;
40 | border-left: 12px solid transparent;
41 | border-right: 12px solid transparent;
42 | border-bottom: 12px solid transparent;
43 | transform: translate3D(-50%, 0, 0);
44 | }
45 | }
46 |
47 | &.left {
48 | transform: translate3D(-10px, 0, 0);
49 | transform-origin: right center;
50 | animation: turnLeft .3s;
51 |
52 | &:after {
53 | left: 100%;
54 | top: 50%;
55 | border-left: 12px solid #000;
56 | border-top: 12px solid transparent;
57 | border-right: 12px solid transparent;
58 | border-bottom: 12px solid transparent;
59 | transform: translate3D(0, -50%, 0);
60 | }
61 | }
62 |
63 | &.right {
64 | transform: translate3D(10px, 0, 0);
65 | transform-origin: left center;
66 | animation: turnRight .3s;
67 |
68 | &:after {
69 | right: 100%;
70 | top: 50%;
71 | border-right: 12px solid #000;
72 | border-top: 12px solid transparent;
73 | border-bottom: 12px solid transparent;
74 | border-left: 12px solid transparent;
75 | transform: translate3D(0, -50%, 0);
76 | }
77 | }
78 |
79 | &.bottom {
80 | transform: translate3D(0, 10px, 0);
81 | transform-origin: center top;
82 | animation: turnBottom .3s;
83 |
84 | &:after {
85 | left: 50%;
86 | bottom: 100%;
87 | border-bottom: 12px solid #000;
88 | border-top: 12px solid transparent;
89 | border-right: 12px solid transparent;
90 | border-left: 12px solid transparent;
91 | transform: translate3D(0, -50%, 0);
92 | }
93 | }
94 |
95 | &:after {
96 | content: '';
97 | position: absolute;
98 | width: 0;
99 | height: 0;
100 | z-index: 50;
101 | }
102 | }
103 | }
104 |
105 |
--------------------------------------------------------------------------------
/src/components/Form/style/form.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-form-line';
6 | $formPrefixCls: 'zby-form-box';
7 |
8 | textarea {
9 | box-sizing: border-box;
10 | }
11 |
12 | .#{$formPrefixCls} {
13 | margin-bottom: 30px;
14 | background: #fff;
15 | }
16 |
17 | .#{$prefixCls} {
18 | overflow: hidden;
19 | border-bottom: #eee solid 2px;
20 |
21 | &.error {
22 | border-bottom-color: red;
23 |
24 | .title {
25 | .label-name {
26 | color: red;
27 | }
28 | }
29 | .content {
30 | input {
31 | color: red;
32 | }
33 | }
34 | }
35 |
36 | .title {
37 | position: relative;
38 | padding: 0;
39 | width: 230px;
40 | float: left;
41 | left: 0;
42 | top: 0;
43 |
44 | // 必填符号
45 | .fa-asterisk {
46 | display: inline-block;
47 | width: 40px;
48 | font-size: 26px;
49 | line-height: 100px;
50 | text-align: center;
51 | color: red;
52 | visibility: hidden;
53 | margin: 0;
54 |
55 | &.required {
56 | visibility: visible;
57 | }
58 | }
59 |
60 | .label-name {
61 | display: inline-block;
62 | max-width: 200px;
63 | height: 100px;
64 | color: #999;
65 | font-size: 32px;
66 | line-height: 100px;
67 | overflow: hidden;
68 | text-overflow: ellipsis;
69 | white-space: nowrap;
70 | vertical-align: top;
71 | }
72 | }
73 |
74 | .content {
75 | box-sizing: border-box;
76 | margin-left: 230px;
77 |
78 | &.has-feedback {
79 | position: relative;
80 |
81 | input {
82 | padding-right: 10px;
83 | }
84 |
85 | .fa {
86 | position: absolute;
87 | top: 50%;
88 | right: 60px;
89 | margin-top: -20px;
90 | font-size: 40px;
91 |
92 | &.fa-check-circle {
93 | color: $viColor;
94 | }
95 |
96 | &.fa-exclamation-circle {
97 | color: #ffbf00;
98 | }
99 |
100 | &.fa-times-circle {
101 | color: #f04134;
102 | }
103 |
104 | &.fa-circle-o-notch {
105 | color: #108ee9;
106 | }
107 | }
108 | }
109 |
110 | input {
111 | width: 100%;
112 | padding: 30px 0 30px 20px;
113 | font-size: 32px;
114 | line-height: 40px;
115 | color: #333;
116 | border: none;
117 |
118 | box-sizing: border-box;
119 | }
120 |
121 | .input-readonly {
122 | padding-left: 20px;
123 | font-size: 32px;
124 | line-height: 100px;
125 | color: #999;
126 | }
127 |
128 | .connection {
129 | float: left;
130 | overflow: hidden;
131 | box-sizing: border-box;
132 | }
133 | }
134 | }
135 |
136 |
--------------------------------------------------------------------------------
/src/components/Feedback/Toast/components/Notice.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/6/14.
3 | */
4 |
5 | // Notice是Toast最底层组件
6 | // 每个黑色的小框框其实都是一个Notice
7 | // Notice核心就是组件初始化的时候 生成一个定时器
8 | // 根据输入的时间 加载一个动画 然后执行输入的回调
9 | // Notice的显示和隐藏收到父组件Notification的绝对控制
10 | import React from 'react'
11 | import classNames from 'classnames'
12 | import '../style/notice.scss'
13 |
14 | function empty() {}
15 |
16 | class Notice extends React.Component {
17 | constructor (props) {
18 | super(props);
19 | this.state = {
20 | shouldClose: false, // 是否开启关闭动画
21 | }
22 | }
23 | componentDidMount () {
24 | if(this.props.duration > 0){
25 | this.closeTimer = setTimeout(() => {
26 | this.close();
27 | }, this.props.duration - 300); // 减掉消失动画300毫秒
28 | }
29 | }
30 | componentWillUnmount () {
31 | // 当有意外关闭的时候 清掉定时器
32 | this.clearCloseTimer();
33 | }
34 | clearCloseTimer () {
35 | if (this.closeTimer) {
36 | clearTimeout(this.closeTimer);
37 | this.closeTimer = null;
38 | }
39 | }
40 | close () {
41 | // 关闭的时候 应该先清掉倒数定时器
42 | // 然后开启过场动画
43 | // 等待动画结束 执行回调
44 | this.clearCloseTimer();
45 | const _this = this;
46 | _this.setState({shouldClose: true});
47 | this.timer = setTimeout(()=>{
48 | if(this.props.onClose){
49 | this.props.onClose();
50 | }
51 | clearTimeout(_this.timer);
52 | }, 300);
53 | }
54 | render () {
55 | const {shouldClose} = this.state;
56 | const {prefixCls, type, iconClass, content} = this.props;
57 |
58 | return (
59 |
68 | {iconClass ?
: null}
69 |
{content}
70 |
71 | )
72 | }
73 | }
74 |
75 | Notice.propTypes = {
76 | duration: React.PropTypes.number.isRequired, // Notice显示时间
77 | prefixCls: React.PropTypes.string, // 前缀class
78 | type: React.PropTypes.oneOf(['info', 'success', 'warning', 'error']), // notice类型
79 | iconClass: React.PropTypes.string, // icon的class
80 | content: React.PropTypes.any, // Notice显示的内容
81 | onClose: React.PropTypes.func // 显示结束回调
82 | };
83 |
84 | Notice.defaultProps = {
85 | prefixCls: 'zby-notice',
86 | duration: 3000,
87 | onClose: empty
88 | };
89 |
90 | export default Notice
--------------------------------------------------------------------------------
/src/components/DataDisplay/Tooltip/util/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/16.
3 | */
4 | export const getTipPosition = (elementPosition, titlePosition, direct) => {
5 | const result = {};
6 |
7 | if(elementPosition === undefined || titlePosition === undefined) return result;
8 |
9 | const {tipWidth, tipHeight} = titlePosition;
10 | const {width, height, x, y} = elementPosition;
11 | const clineWidth = document.body.clientWidth;
12 | const clineHeight = document.body.clientHeight;
13 |
14 | // 算出tip 在上边的中心点的位置
15 | const centerPositionTop = {
16 | left: x + width / 2 - tipWidth / 2,
17 | top: y - tipHeight
18 | };
19 |
20 | // 显示在左边是否合适
21 | const centerPositionLeft = {
22 | left: x - tipWidth,
23 | top: y + (height / 2) - (tipHeight / 2)
24 | };
25 |
26 | // 判断显示在右边
27 | const centerPositionRight = {
28 | left: x + width,
29 | top: y + (height / 2) - (tipHeight / 2)
30 | };
31 |
32 | // 显示在下边
33 | const centerPositionBottom = {
34 | left: x + width / 2 - tipWidth / 2,
35 | top: y + height
36 | };
37 |
38 | // 是否有指令
39 | if(direct === 'top'){
40 | return {direction: direct, position: centerPositionTop};
41 | } else if (direct === 'left') {
42 | return {direction: direct, position: centerPositionLeft};
43 | } else if (direct === 'right') {
44 | return {direction: direct, position: centerPositionRight};
45 | } else if (direct === 'bottom') {
46 | return {direction: direct, position: centerPositionRight};
47 | }
48 |
49 | // 自动计算
50 | // 判断显示在上边是否合适
51 | if(centerPositionTop.top >= 10 && // 上
52 | centerPositionTop.left >= 10 && // 左
53 | (clineWidth - (centerPositionTop.left + tipWidth)) >=10 ){ // 右
54 |
55 | return {direction: 'top', position: centerPositionTop};
56 | }
57 |
58 |
59 | if(centerPositionLeft.top >= 10 && // 上
60 | centerPositionLeft.left >= 10 && // 左
61 | (clineHeight - (centerPositionLeft.top + tipHeight)) >=10 ){ // 下
62 |
63 | return {direction: 'left', position: centerPositionLeft};
64 | }
65 |
66 |
67 | if(centerPositionRight.top >= 10 && // 上
68 | centerPositionRight.left + tipWidth >= 10 && // 右
69 | (clineHeight - (centerPositionRight.top + tipHeight)) >=10 ){ // 下
70 |
71 | return {direction: 'right', position: centerPositionRight};
72 | }
73 |
74 | return {direction: 'bottom', position: centerPositionBottom};
75 | };
76 |
77 | export const getTitleDOMPosition = (dom) => {
78 | const cloneDOM = dom.cloneNode(true);
79 |
80 | cloneDOM.style.display = 'block';
81 | cloneDOM.style.left = '-1000px';
82 | cloneDOM.style.top = '-1000px';
83 |
84 | document.body.appendChild(cloneDOM);
85 |
86 | const result = {
87 | tipWidth: cloneDOM.offsetWidth,
88 | tipHeight: cloneDOM.offsetHeight
89 | };
90 |
91 | cloneDOM.remove();
92 |
93 | return result;
94 | };
--------------------------------------------------------------------------------
/src/containers/Form/FormPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/8/1.
3 | */
4 | import React from 'react'
5 | import {Form} from '../../components/Form/'
6 | import Tools from '../../components/Tools/Tools'
7 | import Button from '../../components/DataEntry/Button'
8 | import Toast from '../../components/Feedback/Toast/components/Toast'
9 | import moment from 'moment'
10 |
11 | class FormPage extends React.Component {
12 | constructor (props) {
13 | super(props);
14 | this.state = {
15 | formData: [
16 | {type: 'group', id: 'customGroup', name: '自定义表单', children: [
17 | {type: 'input', id: 'username', labelName: '姓名', value: 'Aus', require: true, max: 10, min: 2},
18 | {type: 'input', id: 'password', labelName: '密码', value: 'asd', kind: 'password', require: true, min: 4},
19 | {type: 'input', id: 're-password', labelName: '重复密码', value: 'as', kind: 'password', require: true, min: 4},
20 | {type: 'select', id: 'gender', labelName: '性别', value: '1', data: [{label: '男', value: '1'}, {label: '女', value: '2'}, {label: '外星人', value: '3'}]},
21 | {type: 'date-time', id: 'datetime', labelName: '出生日期', value: moment()},
22 | {type: 'input', id: 'email', labelName: '邮箱', value: undefined, kind: 'email', placeholder: '请输入邮箱'},
23 | {type: 'input', id: 'phone', labelName: '手机号', value: undefined, kind: 'phone', placeholder: '请输入手机号'},
24 | {type: 'switch', id: 'message', labelName: '开启消息推送', value: true},
25 | {type: 'checkbox', id: 'specialty', labelName: '擅长语言', value: ['JS'], options: [{label: 'JS', value: 'JS'}, {label: 'JAVA', value: 'JAVA'}, {label: 'PHP', value: 'PHP'}, {label: 'C++', value: 'C++'}]},
26 | ]}
27 | ]
28 | };
29 | this.handleSubmit = this.handleSubmit.bind(this);
30 | }
31 | handleSubmit () {
32 | const validateResult = this.refs.form.validate();
33 |
34 | if(validateResult.length === 0){
35 | Toast.success('提交成功!', 3000, 'fa-check', false);
36 | return;
37 | }
38 | Toast.error('提交失败!', 3000, 'fa-times', false);
39 | }
40 | render () {
41 | const {formData} = this.state;
42 |
43 | return (
44 |
45 |
46 | {Tools.linkTo('/index')}} />
47 | Form
48 |
49 |
50 |
61 | )
62 | }
63 | }
64 |
65 | export default FormPage
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/PickerView/PickerViewPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/20.
3 | */
4 | import React from 'react'
5 | import ListTitle from 'components/DataDisplay/ListTitle/'
6 | import PickerView from 'components/DataEntry/PickerView/'
7 | import Tools from 'components/Tools/Tools'
8 |
9 | class PickerViewPage extends React.Component {
10 | constructor (props) {
11 | super(props);
12 | this.state = {
13 | value1: ['fruit', 'watermelon'],
14 | value2: ['一', '4', '貮']
15 | };
16 | this.handleChange = this.handleChange.bind(this);
17 | }
18 | handleChange (newValue) {
19 | console.log(newValue);
20 | }
21 | render () {
22 | const {value1, value2} = this.state;
23 | const array1 = [
24 | {label: '水果', value: 'fruit', children: [
25 | {label: '苹果', value: 'apple'},
26 | {label: '香蕉', value: 'banana'},
27 | {label: '橘子', value: 'orange'},
28 | {label: '西瓜', value: 'watermelon'}
29 | ]},
30 | {label: '蔬菜', value: 'vegetables', children: [
31 | {label: '番茄', value: 'tomato'},
32 | {label: '土豆', value: 'potato'},
33 | {label: '白菜', value: 'cabbage'}
34 | ]},
35 | {label: '动物', value: 'animal', children: [
36 | {label: '皮皮虾', value: 'shrimp'},
37 | {label: '象拔蚌', value: 'clam'},
38 | {label: '骚猪', value: 'pdd'}
39 | ]},
40 | ];
41 | const array2 = [
42 | [
43 | {label: '一', value: '一'},
44 | {label: '二', value: '二'},
45 | {label: '三', value: '三'}
46 | ],
47 | [
48 | {label: '1', value: '1'},
49 | {label: '2', value: '2'},
50 | {label: '3', value: '3'},
51 | {label: '4', value: '4'}
52 | ],
53 | [
54 | {label: '壹', value: '壹'},
55 | {label: '貮', value: '貮'},
56 | {label: '叁', value: '叁'}
57 | ]
58 | ];
59 |
60 | return (
61 |
62 |
63 | {Tools.linkTo('/')}} />
64 | PickerView
65 |
66 |
67 |
68 |
74 |
75 |
76 |
83 |
84 | );
85 | }
86 | }
87 |
88 | export default PickerViewPage;
--------------------------------------------------------------------------------
/src/components/DataEntry/Figure/style/figure.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base';
4 |
5 | $prefixCls: 'zby-figure';
6 |
7 | .#{$prefixCls}-with-preview {
8 | position: relative;
9 | display: inline-block;
10 | margin-right: 15px;
11 | margin-bottom: 15px;
12 |
13 | .#{$prefixCls}-preview-box {
14 | box-sizing: border-box;
15 | position: relative;
16 | width: 160px;
17 | height: 160px;
18 | border-radius: 5px;
19 | border: 2px solid $darkGray;
20 | background: rgba(0,0,0, .7);
21 | animation: scaleIn .3s;
22 | vertical-align: top;
23 |
24 | transition: all .3s;
25 | transform-origin: left top;
26 |
27 | &.loading, &.error {
28 | &:before {
29 | content: '';
30 | position: absolute;
31 | left: 0;
32 | right: 0;
33 | top: 0;
34 | bottom: 0;
35 | background: rgba(0,0,0, .7);
36 | z-index: 10;
37 | }
38 | }
39 |
40 | &.loaded {
41 | .progress-text {
42 | display: none;
43 | }
44 | }
45 |
46 | &.error {
47 | border-color: red;
48 | }
49 |
50 | &.deleted {
51 | margin: 0;
52 | padding: 0;
53 | width: 0;
54 | height: 0;
55 | opacity: 0;
56 | border: none;
57 | animation: scaleOut .3s;
58 | }
59 |
60 | .img-box {
61 | width: 100%;
62 | height: 100%;
63 | overflow: hidden;
64 |
65 | img {
66 | height: 100%;
67 | margin-top: 50%;
68 | margin-left: 50%;
69 | transform: translate(-50%, -50%);
70 | }
71 | }
72 |
73 | .uploading {
74 | width: 200px;
75 | height: 100%;
76 | text-align: center;
77 |
78 | .fa {
79 | font-size: 80px;
80 | line-height: 176px;
81 | color: #999;
82 | }
83 | }
84 |
85 | .progress-text {
86 | font-size: 32px;
87 | color: #fff;
88 | position: absolute;
89 | left: 50%;
90 | top: 50%;
91 |
92 | transform: translate(-50%, -50%);
93 | z-index: 10;
94 |
95 | .fa {
96 | font-size: 48px;
97 | }
98 | }
99 |
100 | .close {
101 | position: absolute;
102 | right: -16px;
103 | top: -16px;
104 | width: 32px;
105 | height: 32px;
106 | background: #fff;
107 | border-radius: 50%;
108 | font-size: 32px;
109 | line-height: 32px;
110 | text-align: center;
111 | color: $viColor;
112 | z-index: 10;
113 | }
114 | }
115 | }
116 |
117 | // 预览
118 | .#{$prefixCls}-preview-container {
119 | position: fixed;
120 | left: 0;
121 | top: 0;
122 | right: 0;
123 | bottom: 0;
124 | background: rgba(0, 0, 0, 0.7);
125 | overflow: hidden;
126 | z-index: 200;
127 |
128 | transition: all .3s;
129 |
130 | &.scale {
131 | position: fixed;
132 | }
133 |
134 | img {
135 | position: absolute;
136 | left: 50%;
137 | top: 50%;
138 | width: 90%;
139 | transform: translate(-50%, -50%);
140 | }
141 | }
--------------------------------------------------------------------------------
/src/components/DataDisplay/List/components/Item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/4/1.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import Touchable from 'rc-touchable'
7 | import '../style/item.scss'
8 |
9 | const Item = (props) => {
10 | const {prefixCls, className, activeClassName, thumb, extra, subtitle, arrow, wrap, disabled, onClick, onLongPress, children, ...resProps} = props;
11 |
12 | return (
13 |
19 |
20 | {thumb ? typeof thumb === 'string' ?
: thumb : null}
21 | {typeof children === 'object' ?
22 |
23 | {children}
24 |
25 | :
26 |
27 | {children}
28 | {subtitle ?
{subtitle}
: null}
29 |
30 | }
31 | {extra ?
{extra}
: null}
32 | {arrow ?
33 |
{arrow !== 'empty' ?
34 |
40 | :
41 | null}
42 |
43 | :
44 | null
45 | }
46 |
47 |
48 | )
49 | };
50 |
51 | // List中的item 组件
52 | Item.PropTypes = {
53 | prefixCls: React.PropTypes.string, // 前缀class
54 | className: React.PropTypes.string, // 自定义class
55 | activeClassName: React.PropTypes.string, // 点击效果class
56 | thumb: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), // 缩略图
57 | extra: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), // 右侧的内容
58 | subtitle: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), // 列表项的副标题
59 | arrow: React.PropTypes.oneOf(['horizontal', 'up', 'down', 'empty']), // 列表项的箭头 枚举
60 | wrap: React.PropTypes.bool, // 超出文字是否会被隐藏
61 | disabled: React.PropTypes.bool, // 列表项不可点击
62 | onClick: React.PropTypes.func, // 列表项点击回调事件
63 | onLongPress: React.PropTypes.func, // 长按回调事件
64 | };
65 |
66 | Item.defaultProps = {
67 | prefixCls: 'zby-item',
68 | activeClassName: 'zby-item-active',
69 | wrap: true,
70 | disabled: false
71 | };
72 |
73 | export default Item
--------------------------------------------------------------------------------
/src/containers/Form/DateRangePage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/25.
3 | */
4 | import React from 'react'
5 | import ListTitle from '../../components/DataDisplay/ListTitle/'
6 | import {DateRange} from '../../components/Form/'
7 | import Tools from '../../components/Tools/Tools'
8 | import moment from 'moment'
9 |
10 | class DataRangePage extends React.Component {
11 | constructor (props) {
12 | super(props);
13 | this.state = {
14 | value1: {required: true, startLabelName: '开始时间啊', startValue: moment([2017, 6, 27]), endLabelName: '结束时间啊', endValue: moment([2017, 6, 27]), rangeLabelName: '时长'},
15 | value2: {required: false, startLabelName: 'start', startValue: moment([2017, 6, 27, 19, 20]), endLabelName: 'end', endValue: moment([2017, 6, 27, 19, 30]), rangeLabelName: 'range', kind: 'datetime'},
16 | value3: {readOnly: true, required: false, startLabelName: '开始时间啊', startValue: moment([2017, 6, 27]), endLabelName: '结束时间啊', endValue: moment([2017, 6, 27]), rangeLabelName: '时长'},
17 | value4: {readOnly: true, required: false, startLabelName: 'start', startValue: moment([2017, 6, 27, 19, 20]), endLabelName: 'end', endValue: moment([2017, 6, 27, 19, 30]), rangeLabelName: 'range', kind: 'datetime'},
18 | value5: {required: true, startLabelName: '自定义开始', startValue: moment([2017, 6, 27]), endLabelName: '自定义结束', endValue: moment([2017, 6, 27]), rangeLabelName: '自定义时长'},
19 | };
20 | }
21 | handleChange (type, value) {
22 | this.setState((previousState)=>{
23 | previousState[type] = Object.assign(previousState[type], value);
24 | return {...previousState};
25 | });
26 | }
27 | render () {
28 | const {value1, value2, value3, value4, value5} = this.state;
29 |
30 | return (
31 |
32 |
33 | {Tools.linkTo('/index')}} />
34 | DateRange
35 |
36 |
37 |
38 |
39 |
43 |
47 |
48 |
49 |
50 |
53 |
56 |
57 |
58 |
59 | (`${d}天${h}时${m}分`)}
62 | onChange={this.handleChange.bind(this, 'value5')}
63 | />
64 |
65 |
66 | )
67 | }
68 | }
69 |
70 | export default DataRangePage
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/Switch/SwitchPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/17.
3 | */
4 | import React from 'react'
5 | import List from 'components/DataDisplay/List/'
6 | import Switch from 'components/DataEntry/Switch/'
7 | import Tools from 'components/Tools/Tools'
8 |
9 | const Item = List.Item;
10 |
11 | class SwitchPage extends React.Component {
12 | constructor (props) {
13 | super(props);
14 | this.state = {
15 | switch1: true,
16 | switch2: true,
17 | }
18 | }
19 | handleChange (id, checked) {
20 | console.log('切换选中');
21 | let state = {};
22 | state[id] = checked;
23 | this.setState(state);
24 | }
25 | render () {
26 | return (
27 |
28 |
29 | {Tools.linkTo('/')}} />
30 | Switch
31 |
32 |
33 |
34 | - }>
35 | 不受控组件
36 |
37 | }>
38 | {`受控组件:${this.state.switch1 ? '开启' : '关闭'}`}
39 |
40 |
41 |
42 |
43 | }>
44 | 不受控组件
45 |
46 | }
51 | >
52 | {`受控组件:${this.state.switch2 ? '开启' : '关闭'}`}
53 |
54 |
55 |
56 |
57 | }>iOS关闭状态
58 | }>iOS开启状态
59 | }
63 | >
64 | 安卓关闭状态
65 |
66 | }
70 | >
71 | 安卓开启状态
72 |
73 |
74 |
75 |
76 | }>iOS关闭状态
77 | }
81 | >
82 | 安卓关闭状态
83 |
84 |
85 |
86 | )
87 | }
88 | }
89 |
90 | export default SwitchPage
--------------------------------------------------------------------------------
/src/components/DataEntry/Switch/style/index.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import '../../../../static/sass/base.scss';
4 |
5 | $prefixCls: 'zby-switch';
6 |
7 | // Switch
8 | .#{$prefixCls} {
9 | position: relative;
10 | display: inline-block;
11 | margin: 8px 0;
12 | width: 120px;
13 | border-radius: 30px;
14 | vertical-align: middle;
15 | // 清除浏览器默认样式
16 | appearance: none;
17 | transition: all .3s;
18 | z-index: 2;
19 |
20 | &.iOS {
21 | height: 60px;
22 | border: 2px solid #e5e5e5;
23 | box-shadow: 0 2px 5px rgba(0,0,0,.3) inset;
24 |
25 | &:after {
26 | width: 60px;
27 | height: 60px;
28 | left: 1px;
29 | top: 1px;
30 | background-color: #fff;
31 | box-shadow: 0 8px 10px rgba(0,0,0,.3);
32 | }
33 |
34 | &.focus {
35 | &:after {
36 | width: 70px;
37 | }
38 |
39 | &.on {
40 | &:after {
41 | transform: translateX(68%);
42 | }
43 | }
44 | }
45 |
46 | &.disabled {
47 | opacity: .3;
48 | }
49 | }
50 |
51 | &.android {
52 | height: 60px;
53 | background-color: $viColor;
54 |
55 | &:after {
56 | width: 52px;
57 | height: 52px;
58 | left: 4px;
59 | top: 4px;
60 | background-color: #fff;
61 | }
62 |
63 | &.focus {
64 | &:after {
65 | width: 60px;
66 | }
67 |
68 | &.on {
69 | &:after {
70 | transform: translateX(85%);
71 | }
72 | }
73 | }
74 |
75 | &.disabled {
76 | &.on, &.off {
77 | background-color: $lightGray;
78 | &:after {
79 | background-color: $darkGray;
80 | }
81 | }
82 | }
83 |
84 | .attachedTextFalse {
85 | color: #fff;
86 | opacity: 1;
87 | }
88 | }
89 |
90 | // 关闭时候的样式
91 | &.off {
92 | &.iOS {
93 | background-color: #fff;
94 | }
95 |
96 | &.android {
97 | background-color: $darkGray;
98 | }
99 |
100 | &:after {
101 | transform: translateX(0%);
102 | }
103 |
104 | .attachedTextFalse {
105 | opacity: .8;
106 | transform: translateX(0);
107 | }
108 | }
109 |
110 | // 开的样式
111 | &.on {
112 | &.iOS {
113 | background-color: #4dd865;
114 |
115 | &:after {
116 | transform: translateX(95%);
117 | }
118 | }
119 |
120 | &.android {
121 | background-color: $viColor;
122 |
123 | &:after {
124 | transform: translateX(112%);
125 | }
126 | }
127 |
128 | .attachedTextTrue {
129 | opacity: .8;
130 | transform: translateX(0);
131 | }
132 | }
133 |
134 | //
135 | &:after {
136 | content: '';
137 | position: absolute;
138 | border-radius: 30px;
139 | z-index: 3;
140 | transition: all .3s;
141 | }
142 |
143 | // 开关文字
144 | .attachedTextTrue,
145 | .attachedTextFalse {
146 | position: absolute;
147 | top: 0;
148 | bottom: 0;
149 | width: 50%;
150 | font-size: 26px;
151 | text-align: center;
152 | line-height: 60px;
153 | opacity: 0;
154 |
155 | transition: all .3s;
156 | }
157 |
158 | .attachedTextTrue {
159 | left: 0;
160 | color: #fff;
161 | transform: translate3D(-100%, 0, 0);
162 | }
163 |
164 | .attachedTextFalse {
165 | right: 0;
166 | color: $textGray;
167 | transform: translate3D(100%, 0, 0);
168 | }
169 | }
--------------------------------------------------------------------------------
/src/routes/Form/containers/Input/InputPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/28.
3 | */
4 | import React from 'react'
5 | import ListTitle from 'components/DataDisplay/ListTitle/'
6 | import {Input} from 'components/Form/'
7 | import Tools from 'components/Tools/Tools'
8 |
9 | class InputPage extends React.Component {
10 | constructor (props) {
11 | super(props);
12 | this.state = {
13 | value1: 'abc',
14 | value2: 'ABC',
15 | value3: '一二三',
16 | value4: 'asd',
17 | value5: 33368,
18 | value6: 18686868686,
19 | value7: '123@1.com',
20 | value8: '获取焦点的时候Tooltip',
21 | };
22 | }
23 | handleChange (type, value) {
24 | this.setState({[type]: value.value});
25 | }
26 | render () {
27 | const {value1, value2, value3, value4, value5, value6, value7, value8} = this.state;
28 |
29 | return (
30 |
84 | )
85 | }
86 | }
87 |
88 | export default InputPage
--------------------------------------------------------------------------------
/src/components/DataEntry/Switch/components/Switch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/4/11.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import Touchable from 'rc-touchable'
7 | import '../style/index.scss'
8 |
9 | // 滑动开关组件
10 | class Switch extends React.Component {
11 | constructor (props) {
12 | super(props);
13 |
14 | let checked = true;
15 |
16 | // 如果传入checked 说明是受控组件
17 | if('checked' in props && props.checked !== undefined){
18 | checked = !!props.checked;
19 | } else {
20 | // 否则不受控
21 | checked = !!props.defaultChecked;
22 | }
23 |
24 | this.state = {
25 | checked: checked, // 是否是受控组件
26 | focus: false // 是否按住
27 | };
28 |
29 | this.toggle = this.toggle.bind(this);
30 | }
31 | handleTouch (id) {
32 | if('start' === id){
33 | this.setState({focus: true});
34 | return;
35 | }
36 |
37 | this.setState({focus: false});
38 | }
39 | toggle () {
40 | const checked = !(this.props.checked !== undefined ? this.props.checked : this.state.checked);
41 | this.setChecked(checked);
42 | }
43 | setChecked (checked) {
44 | if (!('checked' in this.props && this.props.checked !== undefined)) {
45 | this.setState({
46 | checked,
47 | });
48 | }
49 |
50 | this.props.onChange(checked);
51 | }
52 | getClassName () {
53 | const checked = this.props.checked !== undefined ? this.props.checked : this.state.checked;
54 | const {prefixCls, theme, disabled} = this.props;
55 | const {focus} = this.state;
56 |
57 | return classNames([prefixCls, {
58 | 'iOS': theme === 'iOS',
59 | 'android': theme === 'android',
60 | 'on' : checked,
61 | 'off' : !checked,
62 | 'focus': focus,
63 | 'disabled': disabled
64 | }]);
65 | }
66 | getAttachedDOM () {
67 | const {attachedText} = this.props;
68 |
69 | if(!attachedText || attachedText.length !== 2) return;
70 |
71 | return [
72 | {attachedText[0]},
73 | {attachedText[1]}
74 | ];
75 |
76 | }
77 | render () {
78 | const className = this.getClassName();
79 | const attachedDOM = this.getAttachedDOM();
80 |
81 | return (
82 |
86 |
91 | {attachedDOM}
92 |
93 |
94 | )
95 | }
96 | }
97 |
98 | Switch.propTypes = {
99 | prefixCls: React.PropTypes.string, // 前缀class
100 | theme: React.PropTypes.oneOf(['iOS', 'android']), // 主题 枚举 iOS风格和Android风格
101 | checked: React.PropTypes.bool,
102 | defaultChecked: React.PropTypes.bool,
103 | attachedText: React.PropTypes.array,
104 | disabled: React.PropTypes.bool,
105 | onChange: React.PropTypes.func,
106 | };
107 |
108 | Switch.defaultProps = {
109 | prefixCls: 'zby-switch',
110 | theme: 'iOS',
111 | defaultChecked: true,
112 | onChange: empty,
113 | };
114 |
115 | function empty() {}
116 |
117 | export default Switch
--------------------------------------------------------------------------------
/src/components/Gesture/Touchable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/4/6.
3 | */
4 | import React from 'react'
5 |
6 | // 根据原生触摸事件 封装手势事件
7 | class Touchable extends React.Component {
8 | constructor (props) {
9 | super(props);
10 | this.state = {
11 | startX: 0, // touchstart时 记录下初始位置 X Y
12 | startY: 0,
13 | startTimeStamp: 0, // 按下去的一瞬间的时刻
14 | longTapTimer: null // 如果是长按事件 需要setTimeout判断
15 | }
16 | }
17 | handleTouchStart (event) {
18 | // 如果由于长按事件监听 则清除
19 | let longTapTimer = this.state.longTapTimer;
20 |
21 | if(longTapTimer){
22 | clearTimeout(longTapTimer);
23 | this.setState({
24 | longTapTimer: null
25 | });
26 | }
27 | // 记录下初始位置
28 | // 取第一个手指
29 | const touch = event.touches[0];
30 | // 设置一个新的长按监听
31 | let newLongTapTimer = setTimeout(() => {
32 | // 执行tap回调函数
33 | //let onTap = this.props.onTap;
34 | //if(onTap && typeof onTap == "function" ) onTap();
35 | }, 800 );
36 |
37 | this.setState({
38 | startX: touch.clientX,
39 | startY: touch.clientY,
40 | startTimeStamp: event.timeStamp,
41 | longTapTimer: newLongTapTimer
42 | });
43 |
44 | event.persist();
45 | }
46 | handleTouchMove (event) {
47 | // 记录下当前移动时刻的位置
48 | const touch = event.touches[0], currentX = touch.clientX, currentY = touch.clientY;
49 | let {startX, startY, longTapTimer} = this.state;
50 | const distanceX = Math.abs(startX - currentX), distanceY = Math.abs(startY - currentY);
51 |
52 | // 如果当前位置相对于start位置有轻微移动 结束长按事件监听
53 | if((distanceX > 5 || distanceY > 5) && longTapTimer ){
54 | clearTimeout(longTapTimer);
55 | this.setState({
56 | longTapTimer: null
57 | });
58 | }
59 | }
60 | handleTouchEnd (event) {
61 | // 记录手指离开时的位置
62 | const touch = event.changedTouches[0];
63 | const endX = touch.clientX;
64 | const endY = touch.clientY;
65 | const endTimeStamp = event.timeStamp;
66 | let longTapTimer = this.state.longTapTimer;
67 |
68 | if(longTapTimer){
69 | clearTimeout(longTapTimer);
70 | this.setState({
71 | longTapTimer: null
72 | });
73 | }
74 |
75 | // 判断其是什么事件
76 | const touchType = this._GetEventType(endX, endY, endTimeStamp);
77 |
78 | switch (touchType) {
79 | case 'tap':
80 | // 执行tap回调函数
81 | let onTap = this.props.onTap;
82 | if(onTap && typeof onTap == 'function' ) onTap();
83 | break;
84 | }
85 |
86 | event.persist();
87 | }
88 | _GetEventType (endX, endY, endTimeStamp) {
89 | const {startX, startY, startTimeStamp} = this.state;
90 | const distanceX = Math.abs(startX - endX);
91 | const distanceY = Math.abs(startY - endY);
92 | const timeDiff = endTimeStamp - startTimeStamp;
93 |
94 | // 允许手指轻微滑动 按压时间小于500ms
95 | if( distanceX < 6 && distanceY < 6 && timeDiff < 500){
96 | return 'tap';
97 | }
98 | }
99 | render () {
100 | return (
101 |
107 | {this.props.children}
108 |
109 | )
110 | }
111 | }
112 |
113 | export default Touchable
--------------------------------------------------------------------------------
/src/components/Form/components/Input.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/17.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import Icon from 'component-font-awesome'
7 | import FormLineHOC from './FormLineHOC'
8 |
9 | const feedbackIconMap = {
10 | success: 'check-circle',
11 | warning: 'exclamation-circle',
12 | wrong: 'times-circle',
13 | loading: 'circle-o-notch fa-spin'
14 | };
15 |
16 | class Input extends React.Component {
17 | constructor (props) {
18 | super(props);
19 | this.state = {
20 | stateValue: ''
21 | };
22 | this.handleChange = this.handleChange.bind(this);
23 | }
24 | componentDidMount () {
25 | const {controlled, value} = this.props;
26 |
27 | if(!controlled){
28 | this.setState({stateValue: value});
29 | }
30 | }
31 | handleChange (e) {
32 | const value = e.target.value;
33 | const {controlled, onChange} = this.props;
34 |
35 | if(controlled){
36 | onChange({value: value});
37 | return;
38 | }
39 |
40 | this.setState({stateValue: value});
41 | }
42 | render () {
43 | const {stateValue} = this.state;
44 | const {value, readOnly, placeHolder, controlled, kind, feedbackIcon} = this.props;
45 |
46 | let inputType;
47 |
48 | switch (kind) {
49 | case 'number':
50 | inputType = 'number';
51 | break;
52 | case 'phone':
53 | inputType = 'number';
54 | break;
55 | case 'password':
56 | inputType = 'password';
57 | break;
58 | default:
59 | inputType = 'text';
60 | break;
61 | }
62 |
63 | return (
64 |
65 | {readOnly ?
66 |
{value ? value : placeHolder}
67 | : controlled ?
68 |
72 | :
73 |
78 | }
79 | {!readOnly && feedbackIcon ?
: ''}
80 |
81 | );
82 | }
83 | }
84 |
85 | function empty() {}
86 |
87 | Input.PropTypes = {
88 | labelName: React.PropTypes.string.isRequired,
89 | prefixCls: React.PropTypes.string, // 前缀class
90 | required: React.PropTypes.bool, // 是否必填
91 | readOnly: React.PropTypes.bool, // 是否只读
92 | controlled: React.PropTypes.bool, // 是否受控组件
93 | value: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]),
94 | placeHolder: React.PropTypes.string,
95 | kind: React.PropTypes.oneOf(['password', 'phone', 'email', 'number', 'text']), // 字段类型
96 | errorText: React.PropTypes.string, // 报错信息
97 | feedbackIcon: React.PropTypes.oneOf(['success', 'warning', 'error', 'loading', '']),
98 | onChange: React.PropTypes.func,
99 | };
100 |
101 | Input.defaultProps = {
102 | prefixCls: 'zby-form-line',
103 | required: false,
104 | readOnly: false,
105 | controlled: true,
106 | value: '',
107 | placeHolder: '',
108 | kind: 'text',
109 | errorText: '',
110 | feedbackIcon: '',
111 | onChange: empty
112 | };
113 |
114 | export default FormLineHOC(Input);
--------------------------------------------------------------------------------
/src/components/Feedback/Toast/components/Notification.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/6/14.
3 | */
4 |
5 | // Notification是Notice父组件,容器
6 | // 是动态插入和删除DOM节点的核心
7 | // 同时也向上暴露给Toast重写改变自己的方法
8 | import React from 'react'
9 | import ReactDOM from 'react-dom'
10 | import Notice from './Notice'
11 | import '../style/notification.scss'
12 |
13 | class Notification extends React.Component {
14 | constructor (props) {
15 | super(props);
16 | this.state = {
17 | notices: [], // 存储当前有的notices
18 | hasMask: true, // 是否显示蒙版
19 | }
20 | }
21 | add (notice) {
22 | // 添加notice
23 | // 创造一个不重复的key
24 | const {notices} = this.state;
25 | const key = notice.key ? notice.key : notice.key = getUuid();
26 | const mask = notice.mask ? notice.mask : false;
27 | const temp = notices.filter((item) => item.key === key).length;
28 |
29 | if(!temp){
30 | // 不存在重复的 添加
31 | notices.push(notice);
32 | this.setState({
33 | notices: notices,
34 | hasMask: mask
35 | });
36 | }
37 | }
38 | remove (key) {
39 | // 根据key删除对应
40 | this.setState(previousState => ({notices: previousState.notices.filter(notice => notice.key !== key)}));
41 | }
42 | getNoticeDOM () {
43 | const _this = this;
44 | const {notices} = this.state;
45 | const result = [];
46 |
47 | notices.map((notice)=>{
48 | // 每个Notice onClose的时候 删除掉notices中对应key的notice
49 | const closeCallback = () => {
50 | _this.remove(notice.key);
51 | // 如果有用户传入的onClose 执行
52 | if(notice.onClose) notice.onClose();
53 | };
54 |
55 | result.push(
56 |
60 | );
61 | });
62 |
63 | return result;
64 | }
65 | getMaskDOM () {
66 | const {notices, hasMask} = this.state;
67 | // notices为空的时候 不显示蒙版
68 | // 始终只有一个蒙版
69 | if(notices.length > 0 && hasMask === true) return ;
70 | }
71 | render () {
72 | const {prefixCls} = this.props;
73 | const noticesDOM = this.getNoticeDOM();
74 | const maskDOM = this.getMaskDOM();
75 |
76 | return (
77 |
78 | {maskDOM}
79 |
80 | {noticesDOM}
81 |
82 |
83 | )
84 | }
85 | }
86 |
87 | // 统计notice总数 防止重复
88 | let noticeNumber = 0;
89 |
90 | // 生成唯一的id
91 | const getUuid = () => {
92 | return 'notification-' + new Date().getTime() + '-' + noticeNumber++;
93 | };
94 |
95 | // Notification增加一个重写方法
96 | // 该方法方便Notification组件动态添加到页面中和重写
97 | Notification.reWrite = function (properties) {
98 | const { ...props } = properties || {};
99 |
100 | let div;
101 |
102 | div = document.createElement('div');
103 | document.body.appendChild(div);
104 |
105 | const notification = ReactDOM.render(, div);
106 |
107 | return {
108 | notice(noticeProps) {
109 | notification.add(noticeProps);
110 | },
111 | removeNotice(key) {
112 | notification.remove(key);
113 | },
114 | destroy() {
115 | ReactDOM.unmountComponentAtNode(div);
116 | document.body.removeChild(div);
117 | },
118 | component: notification
119 | }
120 | };
121 |
122 | Notification.propTypes = {
123 | prefixCls: React.PropTypes.string, // 组件class前缀
124 | };
125 |
126 | Notification.defaultProps = {
127 | prefixCls: 'zby-notification',
128 | };
129 |
130 | export default Notification
--------------------------------------------------------------------------------
/src/static/sass/base.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | // 公共函数
4 |
5 | // 基本颜色属性等
6 | $viColor: #00b4a5;
7 | $darkViColor: #009486;
8 | $linearViColor: linear-gradient(to right, #31D5A1, #00B4A5);
9 | $lightGray: #ddd;
10 | $darkGray: #bbb;
11 | $textGray: #888;
12 |
13 | // px2rem并没有自动实现该功能 还是手动加上吧
14 | @mixin font-dpr($font-size){
15 | font-size: $font-size;
16 |
17 | [data-dpr="2"] & {
18 | font-size: $font-size * 2;
19 | }
20 |
21 | [data-dpr="3"] & {
22 | font-size: $font-size * 3;
23 | }
24 | }
25 |
26 | @mixin border-top () {
27 | border-top: 2px solid $lightGray;
28 | }
29 |
30 | @mixin border-bottom () {
31 | border-bottom: 2px solid $lightGray;
32 | }
33 |
34 | @mixin list-item-bottom-line ($fromLeft: 40px) {
35 | content: '';
36 | display: block;
37 | position: absolute;
38 | left: $fromLeft;
39 | bottom: 0;
40 | width: 100%;
41 | @include border-bottom();
42 | }
43 |
44 | // 公共动画库
45 | // 爆炸进入
46 | @keyframes BombIn {
47 | 0% {
48 | transform: translate(-50%, -50%, 0) scale(0, 0);
49 | -webkit-transform: translate(-50%, -50%, 0) scale(0, 0);
50 | }
51 | 50% {
52 | transform: translate(-50%, -50%, 0) scale(1.2, 1.2);
53 | -webkit-transform: translate(-50%, -50%) scale(1.2, 1.2);
54 | }
55 | 100% {
56 | transform: translate(-50%, -50%) scale(1, 1);
57 | -webkit-transform: translate(-50%, -50%) scale(1, 1);
58 | }
59 | }
60 |
61 | // 翻转
62 | @keyframes Flop {
63 | 0% {
64 | transform: translate(-50%, -50%) rotateY(90deg);
65 | -webkit-transform: translate(-50%, -50%) rotateY(90deg);
66 | }
67 | 33% {
68 | transform: translate(-50%, -50%) rotateY(0deg);
69 | -webkit-transform: translate(-50%, -50%) rotateY(0deg);
70 | }
71 | 67% {
72 | transform: translate(-50%, -50%) rotateY(-30deg);
73 | -webkit-transform: translate(-50%, -50%) rotateY(-30deg);
74 | }
75 | 100% {
76 | transform: translate(-50%, -50%) rotateY(0deg);
77 | -webkit-transform: translate(-50%, -50%) rotateY(0deg);
78 | }
79 | }
80 |
81 | // 从底部滑入
82 | @keyframes slideInFromBottom {
83 | 0% {
84 | transform: translate(-50%, 0%);
85 | -webkit-transform: translate(-50%, 0%);
86 | opacity: 0;
87 | }
88 | 100% {
89 | transform: translate(-50%, -50%);
90 | -webkit-transform: translate(-50%, -50%);
91 | opacity: 1;
92 | }
93 | }
94 |
95 |
96 | // 摇晃
97 | @keyframes shake {
98 | from, to {
99 | -webkit-transform: translate(-50%, -50%);
100 | transform: translate(-50%, -50%);
101 | }
102 |
103 | 15%, 45%, 75% {
104 | -webkit-transform: translate(-60%, -50%);
105 | transform: translate(-60%, -50%);
106 | }
107 |
108 | 30%, 60%, 90% {
109 | -webkit-transform: translate(-40%, -50%);
110 | transform: translate(-40%, -50%);
111 | }
112 | }
113 |
114 | // 0-160
115 | @keyframes scaleIn {
116 | 0% {
117 | width: 0;
118 | height: 0;
119 | }
120 | 100% {
121 | width: 160px;
122 | height: 160px;
123 | }
124 | }
125 |
126 | // 160-0
127 | @keyframes scaleOut {
128 | 0% {
129 | width: 160px;
130 | height: 160px;
131 | opacity: 1;
132 | }
133 | 100% {
134 | width: 0;
135 | height: 0;
136 | opacity: 0;
137 | }
138 | }
139 |
140 | //
141 | @keyframes turnUp {
142 | 0% {
143 | transform: translate3D(0, -10px, 0) rotateX(90deg);
144 | }
145 | 100% {
146 | transform: translate3D(0, -10px, 0) rotateX(0);
147 | }
148 | }
149 |
150 | @keyframes turnLeft {
151 | 0% {
152 | transform: translate3D(-10px, 0, 0) rotateY(90deg);
153 | }
154 | 100% {
155 | transform: translate3D(-10px, 0, 0) rotateY(0);
156 | }
157 | }
158 |
159 | @keyframes turnRight {
160 | 0% {
161 | transform: translate3D(10px, 0, 0) rotateY(90deg);
162 | }
163 | 100% {
164 | transform: translate3D(10px, 0, 0) rotateY(0);
165 | }
166 | }
167 |
168 | @keyframes turnBottom {
169 | 0% {
170 | transform: translate3D(0, 10px, 0) rotateX(90deg);
171 | }
172 | 100% {
173 | transform: translate3D(0, 10px, 0) rotateX(0);
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/DatePicker/DatePickerPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/7.
3 | */
4 | import React from 'react'
5 | import ListTitle from 'components/DataDisplay/ListTitle/'
6 | import List from 'components/DataDisplay/List/'
7 | import DatePicker from 'components/DataEntry/DataPicker/'
8 | import Tools from 'components/Tools/Tools'
9 | import moment from 'moment'
10 |
11 | const Item = List.Item;
12 |
13 | class DatePickerPage extends React.Component {
14 | constructor (props) {
15 | super(props);
16 | this.state = {
17 | dateValue: moment(),
18 | timeValue: moment('12:10', 'HH:mm'),
19 | datetimeValue: moment(),
20 | yearValue: moment(),
21 | monthValue: moment()
22 | }
23 | }
24 | handleChange (type, newValue) {
25 | console.log('value change');
26 | console.log(newValue);
27 |
28 | const obj = {
29 | 'date': 'dateValue',
30 | 'time': 'timeValue',
31 | 'datetime': 'datetimeValue',
32 | 'year': 'yearValue',
33 | 'month': 'monthValue',
34 | };
35 |
36 | this.setState({[obj[type]]: newValue});
37 | }
38 | render () {
39 | const {dateValue, timeValue, datetimeValue, yearValue, monthValue} = this.state;
40 |
41 | return (
42 |
43 |
44 | {Tools.linkTo('/')}} />
45 | DataPicker
46 |
47 |
48 |
49 |
50 |
51 |
59 | - 日期选择
60 |
61 |
70 | - 时间选择
71 |
72 |
80 | - 日期时间选择
81 |
82 |
90 | - 年份选择
91 |
92 |
100 | - 月份选择
101 |
102 |
103 |
104 | )
105 | }
106 | }
107 |
108 | export default DatePickerPage
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-component",
3 | "version": "1.0.0",
4 | "description": "react 组件集合",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/Aus0049/react-component.git"
9 | },
10 | "keywords": [
11 | "react"
12 | ],
13 | "author": "Aus",
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/Aus0049/react-component/issues"
17 | },
18 | "homepage": "https://github.com/Aus0049/react-component#readme",
19 | "scripts": {
20 | "clean": "rimraf dist",
21 | "compile": "better-npm-run compile",
22 | "lint": "eslint src/components/** src/containers/** src/layout/** src/routes/**",
23 | "lint:fix": "npm run lint -- --fix",
24 | "start": "better-npm-run start",
25 | "dev": "better-npm-run dev",
26 | "deploy": "better-npm-run deploy",
27 | "deploy:dev": "better-npm-run deploy:dev",
28 | "deploy:prod": "better-npm-run deploy:prod",
29 | "online": "better-npm-run online"
30 | },
31 | "betterScripts": {
32 | "compile": {
33 | "command": "node bin/compile",
34 | "env": {
35 | "DEBUG": "app:*"
36 | }
37 | },
38 | "dev": {
39 | "command": "nodemon --exec node bin/server --ignore dist --ignore coverage --ignore tests --ignore src",
40 | "env": {
41 | "NODE_ENV": "development",
42 | "DEBUG": "app:*"
43 | }
44 | },
45 | "deploy": {
46 | "command": "npm run clean && npm run compile",
47 | "env": {
48 | "DEBUG": "app:*"
49 | }
50 | },
51 | "deploy:dev": {
52 | "command": "npm run deploy",
53 | "env": {
54 | "NODE_ENV": "development",
55 | "DEBUG": "app:*"
56 | }
57 | },
58 | "deploy:prod": {
59 | "command": "npm run deploy",
60 | "env": {
61 | "NODE_ENV": "production",
62 | "DEBUG": "app:*"
63 | }
64 | },
65 | "start": {
66 | "command": "node bin/server",
67 | "env": {
68 | "DEBUG": "app:*"
69 | }
70 | },
71 | "online": {
72 | "command": "node bin/online",
73 | "env": {
74 | "NODE_ENV": "production",
75 | "PORT": 3000,
76 | "DEBUG": "app:*"
77 | }
78 | }
79 | },
80 | "dependencies": {
81 | "babel-loader": "^6.2.5",
82 | "babel-plugin-import": "^1.1.0",
83 | "babel-plugin-transform-runtime": "^6.15.0",
84 | "babel-preset-es2015": "^6.14.0",
85 | "babel-preset-react": "^6.11.1",
86 | "babel-preset-stage-0": "^6.3.13",
87 | "babel-runtime": "^6.11.6",
88 | "better-npm-run": "0.0.11",
89 | "classnames": "^2.2.5",
90 | "component-font-awesome": "0.0.4",
91 | "create-react-class": "^15.5.3",
92 | "css-loader": "^0.25.0",
93 | "cssnano": "^3.7.4",
94 | "debug": "^2.2.0",
95 | "exif-js": "^2.3.0",
96 | "extract-text-webpack-plugin": "^1.0.0",
97 | "file-loader": "^0.9.0",
98 | "font-awesome": "^4.7.0",
99 | "fs-extra": "^0.30.0",
100 | "hammerjs": "^2.0.8",
101 | "html-webpack-plugin": "^2.22.0",
102 | "imports-loader": "^0.6.5",
103 | "ip": "^1.1.2",
104 | "json-loader": "^0.5.4",
105 | "moment": "^2.18.1",
106 | "node-sass": "^3.13.1",
107 | "normalize.css": "^4.1.1",
108 | "postcss-loader": "^0.13.0",
109 | "postcss-plugin-px2rem": "^0.7.0",
110 | "postcss-px2rem": "^0.3.0",
111 | "postcss-pxtorem": "^4.0.0",
112 | "rc-touchable": "^1.0.13",
113 | "react": "^15.4.2",
114 | "react-addons": "^0.9.1-deprecated",
115 | "react-addons-css-transition-group": "^15.4.2",
116 | "react-dom": "^15.0.0",
117 | "react-redux": "^4.4.5",
118 | "react-router": "^2.8.0",
119 | "redux": "^3.6.0",
120 | "rimraf": "^2.5.4",
121 | "sass-loader": "^4.0.0",
122 | "style-loader": "^0.13.1",
123 | "url-loader": "^0.5.6",
124 | "webpack": "^1.15.0",
125 | "yargs": "^5.0.0",
126 | "zscroller": "^0.3.1"
127 | },
128 | "devDependencies": {
129 | "autoprefixer": "^7.1.2",
130 | "babel-core": "^6.17.0",
131 | "babel-eslint": "^8.0.1",
132 | "connect-history-api-fallback": "^1.3.0",
133 | "eslint": "^4.7.2",
134 | "eslint-loader": "^1.9.0",
135 | "eslint-plugin-babel": "^4.1.2",
136 | "eslint-plugin-react": "^7.4.0",
137 | "express": "^4.14.0",
138 | "node-sass": "^3.13.0",
139 | "nodemon": "^1.10.2",
140 | "postcss-loader": "^0.13.0",
141 | "react-addons-test-utils": "^15.0.0",
142 | "redbox-react": "^1.2.10",
143 | "webpack-dev-middleware": "^1.6.1",
144 | "webpack-hot-middleware": "^2.12.2"
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React 组件库
5 |
6 |
7 |
8 |
9 |
10 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | /* eslint key-spacing:0 spaced-comment:0 */
2 | const path = require('path')
3 | const debug = require('debug')('app:config')
4 | const argv = require('yargs').argv
5 | const ip = require('ip')
6 |
7 | debug('Creating default configuration.')
8 |
9 | // ========================================================
10 | // Default Configuration
11 | // ========================================================
12 | const config = {
13 | env: process.env.NODE_ENV || 'development',
14 |
15 | // ----------------------------------
16 | // Project Structure
17 | // ----------------------------------
18 | path_base: path.resolve(__dirname, '..'),
19 | components: path.resolve(__dirname, '../src/components'),
20 | static_style: path.resolve(__dirname, '../src/static/sass'),
21 | dir_client: 'src',
22 | dir_dist: 'dist',
23 | dir_server: 'server',
24 | dir_test: 'tests',
25 |
26 | // ----------------------------------
27 | // Server Configuration
28 | // ----------------------------------
29 | // server_host : 'localhost' || ip.address(), // use string 'localhost' to prevent exposure on local network
30 | server_host: ip.address(),
31 | server_port: process.env.PORT || 3000,
32 |
33 | // ----------------------------------
34 | // Compiler Configuration
35 | // ----------------------------------
36 | compiler_babel: {
37 | cacheDirectory: true,
38 | plugins: ['transform-runtime'],
39 | presets: ['es2015', 'react', 'stage-0']
40 | },
41 | compiler_devtool: 'source-map',
42 | compiler_hash_type: 'hash',
43 | compiler_fail_on_warning: false,
44 | compiler_quiet: false,
45 | compiler_public_path: '/',
46 | compiler_stats: {
47 | chunks: false,
48 | chunkModules: false,
49 | colors: true
50 | },
51 | compiler_vendors: [
52 | 'react',
53 | 'react-redux',
54 | 'react-router',
55 | 'redux'
56 | ],
57 |
58 | // ----------------------------------
59 | // Test Configuration
60 | // ----------------------------------
61 | coverage_reporters: [
62 | {type: 'text-summary'},
63 | {type: 'lcov', dir: 'coverage'}
64 | ]
65 | }
66 |
67 | /************************************************
68 | -------------------------------------------------
69 |
70 | All Internal Configuration Below
71 | Edit at Your Own Risk
72 |
73 | -------------------------------------------------
74 | ************************************************/
75 |
76 | // ------------------------------------
77 | // Environment
78 | // ------------------------------------
79 | // N.B.: globals added here must _also_ be added to .eslintrc
80 | config.globals = {
81 | 'process.env': {
82 | 'NODE_ENV': JSON.stringify(config.env)
83 | },
84 | 'NODE_ENV': config.env,
85 | '__DEV__': config.env === 'development',
86 | '__PROD__': config.env === 'production',
87 | '__TEST__': config.env === 'test',
88 | '__COVERAGE__': !argv.watch && config.env === 'test',
89 | '__BASENAME__': JSON.stringify(process.env.BASENAME || '')
90 | }
91 |
92 | // ------------------------------------
93 | // Validate Vendor Dependencies
94 | // ------------------------------------
95 | const pkg = require('../package.json')
96 |
97 | config.compiler_vendors = config.compiler_vendors
98 | .filter((dep) => {
99 | if (pkg.dependencies[dep]) return true
100 |
101 | debug(
102 | `Package "${dep}" was not found as an npm dependency in package.json; ` +
103 | `it won't be included in the webpack vendor bundle.
104 | Consider removing it from compiler_vendors in ~/config/index.js`
105 | )
106 | })
107 |
108 | // ------------------------------------
109 | // Utilities
110 | // ------------------------------------
111 | function base() {
112 | const args = [config.path_base].concat([].slice.call(arguments))
113 | return path.resolve.apply(path, args)
114 | }
115 |
116 | config.utils_paths = {
117 | base: base,
118 | client: base.bind(null, config.dir_client),
119 | dist: base.bind(null, config.dir_dist)
120 | }
121 |
122 | // ========================================================
123 | // Environment Configuration
124 | // ========================================================
125 | debug(`Looking for environment overrides for NODE_ENV "${config.env}".`)
126 | const environments = require('./environments')
127 | const overrides = environments[config.env]
128 | if (overrides) {
129 | debug('Found overrides, applying to default configuration.')
130 | Object.assign(config, overrides(config))
131 | } else {
132 | debug('No environment overrides found, defaults will be used.')
133 | }
134 |
135 | module.exports = config
136 |
--------------------------------------------------------------------------------
/src/routes/DataEntry/containers/Picker/PickerPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/7.
3 | */
4 | import React from 'react'
5 | import ListTitle from 'components/DataDisplay/ListTitle/'
6 | import List from 'components/DataDisplay/List/'
7 | import Picker from 'components/DataEntry/Picker/'
8 | import Tools from 'components/Tools/Tools'
9 |
10 | const Item = List.Item;
11 |
12 | class PickerPage extends React.Component {
13 | constructor (props) {
14 | super(props);
15 | this.state = {
16 | areaValue: ['辽宁省', '本溪市', '桓仁满族自治县'],
17 | numberValue: ['一', '4', '貮']
18 | };
19 | this.handleChange = this.handleChange.bind(this);
20 | this.handlePickerChange = this.handlePickerChange.bind(this);
21 | this.handleNumberChange = this.handleNumberChange.bind(this);
22 | }
23 | handleChange (newValue) {
24 | console.log('value change');
25 | console.log(newValue);
26 | this.setState({
27 | areaValue: newValue
28 | });
29 | }
30 | handlePickerChange (newValue) {
31 | console.log('picker change');
32 | console.log(newValue);
33 | }
34 | handleNumberChange (newValue) {
35 | console.log('number change');
36 | console.log(newValue);
37 | this.setState({
38 | numberValue: newValue
39 | });
40 | }
41 | render () {
42 | let {areaValue, numberValue} = this.state;
43 | const areaArray = [
44 | {label: '北京市', value: '北京市', children: [
45 | {label: '北京市', value: '北京市', children: [
46 | {label: '朝阳区', value: '朝阳区'},
47 | {label: '海淀区', value: '朝阳区'},
48 | {label: '东城区', value: '朝阳区'},
49 | {label: '西城区', value: '朝阳区'}
50 | ]}
51 | ]},
52 | {label: '辽宁省', value: '辽宁省', children: [
53 | {label: '沈阳市', value: '沈阳市', children: [
54 | {label: '沈河区', value: '沈河区'},
55 | {label: '浑南区', value: '浑南区'},
56 | {label: '沈北新区', value: '沈北新区'},
57 | ]},
58 | {label: '本溪市', value: '本溪市', children: [
59 | {label: '溪湖区', value: '溪湖区'},
60 | {label: '东明区', value: '东明区'},
61 | {label: '桓仁满族自治县', value: '桓仁满族自治县'},
62 | ]}
63 | ]},
64 | {label: '云南省', value: '云南省', children: [
65 | {label: '昆明市', value: '昆明市', children:[
66 | {label: '五华区', value: '五华区'},
67 | {label: '官渡区', value: '官渡区'},
68 | {label: '呈贡区', value: '呈贡区'},
69 | ]}
70 | ]},
71 | ];
72 |
73 | const numberArray = [
74 | [
75 | {label: '一', value: '一'},
76 | {label: '二', value: '二'},
77 | {label: '三', value: '三'}
78 | ],
79 | [
80 | {label: '1', value: '1'},
81 | {label: '2', value: '2'},
82 | {label: '3', value: '3'},
83 | {label: '4', value: '4'}
84 | ],
85 | [
86 | {label: '壹', value: '壹'},
87 | {label: '貮', value: '貮'},
88 | {label: '叁', value: '叁'}
89 | ]
90 | ];
91 |
92 | return (
93 |
94 |
95 | {Tools.linkTo('/')}} />
96 | Picker
97 |
98 |
99 |
100 |
101 |
102 |
110 | - 级联选择
111 |
112 |
119 | - 不级联选择
120 |
121 |
122 |
123 | )
124 | }
125 | }
126 |
127 | export default PickerPage
--------------------------------------------------------------------------------
/src/components/DataEntry/PickerView/components/PickerColumn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/5/25.
3 | */
4 | import React from 'react'
5 | import ZScroller from 'zscroller'
6 | import classNames from 'classnames'
7 | import '../style/picker-column.scss'
8 |
9 | // picker-view 中的列
10 | class PickerColumn extends React.Component {
11 | constructor (props) {
12 | super(props);
13 | this.state = {};
14 | }
15 | componentDidMount () {
16 | // getBoundingClientRect js原生方法
17 | // 根据变量判断dom是否渲染完毕
18 | this.itemHeight = this.refs.indicator.getBoundingClientRect().height;
19 | if(this.itemHeight !== 0){
20 | // 绑定事件
21 | this.bindScrollEvent();
22 | // 列表滚到对应位置
23 | this.scrollToPosition();
24 | return;
25 | }
26 | // TODO 这里有个问题 必须要等到渲染结束才能绑定事件 不然获取元素高度有bug 待优化
27 | window.setTimeout(()=>{
28 | // 绑定事件
29 | this.bindScrollEvent();
30 | // 列表滚到对应位置
31 | this.scrollToPosition();
32 | }, 100);
33 | }
34 | componentDidUpdate() {
35 | this.zscroller.reflow();
36 | this.scrollToPosition();
37 | }
38 | componentWillUnmount() {
39 | this.zscroller.destroy();
40 | }
41 | bindScrollEvent () {
42 | // 绑定滚动的事件
43 | const content = this.refs.content;
44 | // getBoundingClientRect js原生方法
45 | this.itemHeight = this.refs.indicator.getBoundingClientRect().height;
46 |
47 | // 最后还是用了何一鸣的zscroll插件
48 | // 但是这个插件并没有太多的文档介绍 gg
49 | // 插件demo地址:http://yiminghe.me/zscroller/examples/demo.html
50 | const t = this;
51 | this.zscroller = new ZScroller(content, {
52 | scrollbars: false,
53 | scrollingX: false,
54 | snapping: true, // 滚动结束之后 滑动对应的位置
55 | penetrationDeceleration: .1,
56 | minVelocityToKeepDecelerating: 0.5,
57 | scrollingComplete () {
58 | // 滚动结束 回调
59 | t.scrollingComplete();
60 | }
61 | });
62 |
63 | // 设置每个格子的高度 这样滚动结束 自动滚到对应格子上
64 | // 单位必须是px 所以要动态取一下
65 | this.zscroller.scroller.setSnapSize(0, this.itemHeight);
66 | }
67 | scrollingComplete () {
68 | // 滚动结束 判断当前选中值
69 | const { top } = this.zscroller.scroller.getValues();
70 | const {data, value, index, onValueChange} = this.props;
71 |
72 | let currentIndex = top / this.itemHeight;
73 | const floor = Math.floor(currentIndex);
74 | if (currentIndex - floor > 0.5) {
75 | currentIndex = floor + 1;
76 | } else {
77 | currentIndex = floor;
78 | }
79 |
80 | let selectedValue;
81 |
82 | if(data[currentIndex]) {
83 | selectedValue = data[currentIndex].value;
84 | }
85 |
86 | if(selectedValue && selectedValue !== value){
87 | // 值发生变化 通知父组件
88 | onValueChange(selectedValue, index);
89 | }
90 | }
91 | scrollToPosition () {
92 | // 滚动到选中的位置
93 | const {data, value} = this.props;
94 |
95 | for(let i = 0; i < data.length; i++){
96 | if(data[i].value === value){
97 | this.selectByIndex(i);
98 | return;
99 | }
100 | }
101 |
102 | this.selectByIndex(0);
103 | }
104 | selectByIndex (index) {
105 | // 滚动到index对应的位置
106 | const top = this.itemHeight * index;
107 |
108 | this.zscroller.scroller.scrollTo(0, top);
109 | }
110 | getCols () {
111 | // 根据value 和 index 获取到对应的data
112 | const {data, value, index, prefixCls} = this.props;
113 |
114 | return data.map((item, i)=>({data[i].label}
));
115 | }
116 | render () {
117 | const {prefixCls} = this.props;
118 | const cols = this.getCols();
119 |
120 | return (
121 |
122 |
123 |
124 |
125 |
126 | {cols}
127 |
128 |
129 |
130 | )
131 | }
132 | }
133 |
134 | function empty() {}
135 |
136 | PickerColumn.propTypes = {
137 | prefixCls: React.PropTypes.string, // 前缀class
138 | index: React.PropTypes.number.isRequired,
139 | data: React.PropTypes.array.isRequired,
140 | value: React.PropTypes.string,
141 | onValueChange: React.PropTypes.func
142 | };
143 |
144 | PickerColumn.defaultProps = {
145 | prefixCls: 'zby-picker-column',
146 | value: '',
147 | onValueChange: empty
148 | };
149 |
150 | export default PickerColumn;
--------------------------------------------------------------------------------
/src/components/DataDisplay/Tooltip/components/Tooltip.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/11/13.
3 | */
4 | import React from 'react'
5 | import ReactDOM from 'react-dom'
6 | import classNames from 'classnames'
7 | import Hammer from 'hammerjs'
8 | import StateComponent from './StateComponent'
9 | import {getTipPosition, getTitleDOMPosition} from '../util/'
10 | import '../style/tooltip.scss'
11 |
12 | let totalZIndex = 100;
13 |
14 | class Tooltip extends React.Component {
15 | constructor (props) {
16 | super(props);
17 | this.state = {
18 | isShow: false, // 是否显示
19 | };
20 | }
21 | componentDidMount () {
22 | setTimeout(()=>{
23 | this.createTipDOM();
24 | if(this.props.show === null) this.bindTouchEvent();
25 | }, 10);
26 | }
27 | componentWillUnmount () {
28 | // 销毁
29 | this.tipContainerDOM.remove();
30 | this.tipDOM.remove();
31 | }
32 | bindTouchEvent () {
33 | // 绑定事件
34 | const {trigger} = this.props;
35 |
36 | if(!this.childDOM) return;
37 |
38 | const cloneChildDOM = this.childDOM;
39 | const cloneChildDOMHammer = new Hammer(cloneChildDOM);
40 |
41 | switch (trigger) {
42 | case 'touch': {
43 | cloneChildDOM.addEventListener('touchstart', ()=>{
44 | this.setState({isShow: true});
45 | }, false);
46 | cloneChildDOM.addEventListener('touchend', ()=>{
47 | this.setState({isShow: false});
48 | }, false);
49 | break;
50 | }
51 | case 'click': {
52 | cloneChildDOMHammer.on('tap', ()=>{
53 | this.setState({isShow: !this.state.isShow});
54 | });
55 | break;
56 | }
57 | case 'long-press': {
58 | cloneChildDOMHammer.on('press', ()=>{
59 | this.setState({isShow: !this.state.isShow});
60 | });
61 | break;
62 | }
63 | default: break;
64 | }
65 | }
66 | createTipDOM () {
67 | // 创建
68 | const {prefixCls} = this.props;
69 | // 创建div
70 | const tipContainerDOM = document.createElement('div');
71 | tipContainerDOM.className = prefixCls;
72 | tipContainerDOM.style.zIndex = totalZIndex++;
73 |
74 | this.tipContainerDOM = tipContainerDOM;
75 | document.body.appendChild(tipContainerDOM);
76 |
77 | // 获取child真实位置
78 | this.childDOM = ReactDOM.findDOMNode(this.child);
79 |
80 | this.updateTipDOM();
81 |
82 | // 隐藏的dom是获取不到宽高的
83 | // 克隆dom 将其插入到页面中 然后remove
84 | this.titleDOMPosition = getTitleDOMPosition(this.tipDOM);
85 | }
86 | updateTipDOM () {
87 | const {prefixCls, show, title, direct} = this.props;
88 | const {isShow} = this.state;
89 | const shouldShow = show !== null ? show : isShow;
90 |
91 | // 获取child真实位置
92 | this.childDOMPostion = this.childDOM.getBoundingClientRect();
93 |
94 | // 这块位置根据屏幕动态计算出来
95 | const {direction, position} = getTipPosition(this.childDOMPostion, this.titleDOMPosition, direct);
96 |
97 | const tipContentDOM =
98 |
102 | {title}
103 |
;
104 |
105 | this.tipDOM = ReactDOM.render(tipContentDOM, this.tipContainerDOM);
106 | }
107 | render () {
108 | // 第一次之后render
109 | if(this.cloneChild) {
110 | this.updateTipDOM();
111 | return this.child = r} component={this.cloneChild} />;
112 | }
113 |
114 | // 第一次render
115 | // 组件挂载之前 clone 传入的children
116 | // 只能有一个children
117 | const cloneChildren = React.cloneElement(!this.props.children ? null :
118 | React.Children.only(this.props.children));
119 |
120 | // 因为props不可修改 clone children 然后修改某些属性
121 | this.cloneChild = React.cloneElement(cloneChildren);
122 |
123 | return this.child = r} component={this.cloneChild} />;
124 | }
125 | }
126 |
127 | Tooltip.propTypes = {
128 | title: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.node]).isRequired, // 显示文字
129 | prefixCls: React.PropTypes.string, // 前缀class
130 | trigger: React.PropTypes.oneOf(['touch', 'click', 'long-press']), // 触发方式
131 | show: React.PropTypes.oneOf([null, true, false]), // 是否显示
132 | direct: React.PropTypes.oneOf(['left', 'right', 'right', 'auto']), // 显示方向
133 | children: React.PropTypes.node, // 被tip的节点
134 | };
135 |
136 | Tooltip.defaultProps = {
137 | prefixCls: 'zby-tooltip',
138 | trigger: 'touch',
139 | show: null,
140 | direct: 'auto',
141 | children:
142 | };
143 |
144 | export default Tooltip
145 |
--------------------------------------------------------------------------------
/src/components/DataEntry/PickerView/components/PickerView.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/5/24.
3 | */
4 | import React from 'react'
5 | import PickerColumn from './PickerColumn'
6 | import '../style/picker-view.scss'
7 |
8 | // 递归寻找value
9 | function getNewValue (tree, oldValue, newValue, deep) {
10 | // 遍历tree
11 | let has;
12 |
13 | tree.map((item, i)=>{
14 | if(item.value === oldValue[deep]) {
15 | newValue.push(item.value);
16 | has = i;
17 | }
18 | });
19 |
20 | if(has === undefined) {
21 | has = 0;
22 | newValue.push(tree[has].value);
23 | }
24 |
25 | if(tree[has].children) getNewValue(tree[has].children, oldValue, newValue, deep+1);
26 |
27 | return newValue;
28 | }
29 |
30 | // 根据value找索引
31 | function getColumnsData (tree, value, hasFind, deep) {
32 | // 遍历tree
33 | let has;
34 | const array = [];
35 |
36 | tree.map((item, i)=>{
37 | array.push({label: item.label, value: item.value});
38 | if(item.value === value[deep]) has = i;
39 | });
40 |
41 | // 判断有没有找到
42 | // 没找到return
43 | // 找到了 没有下一集 也return
44 | // 有下一级 则递归
45 | if(has === undefined) return hasFind;
46 |
47 | hasFind.push(array);
48 |
49 | if(tree[has].children) getColumnsData(tree[has].children, value, hasFind, deep+1);
50 |
51 | return hasFind;
52 | }
53 |
54 |
55 | // 选择器组件
56 | class PickerView extends React.Component {
57 | constructor (props) {
58 | super(props);
59 | this.state = {
60 | defaultSelectedValue: [] // 默认选择的值
61 | };
62 | this.handleValueChange = this.handleValueChange.bind(this);
63 | }
64 | componentDidMount () {
65 | // picker view 当做一个非受控组件
66 | const {value, controlled} = this.props;
67 | if (!controlled) this.setState({defaultSelectedValue: value});
68 | }
69 | handleValueChange (newValue, index) {
70 | // 子组件column发生变化的回调函数
71 | // 每次值发生变化 都要判断整个值数组的新值
72 | const {defaultSelectedValue} = this.state;
73 | const {data, cascade, controlled, value, onChange} = this.props;
74 |
75 | if(controlled){
76 | // 也要算一下正确的值
77 | const oldValue = value.slice();
78 | oldValue[index] = newValue;
79 |
80 | if(cascade) {
81 | onChange(getNewValue(data, oldValue, [], 0));
82 | } else {
83 | onChange(oldValue);
84 | }
85 |
86 | return;
87 | }
88 |
89 | const oldValue = defaultSelectedValue.slice();
90 | oldValue[index] = newValue;
91 |
92 | if(cascade){
93 | // 如果级联的情况下
94 | const newState = getNewValue(data, oldValue, [], 0);
95 |
96 | this.setState({defaultSelectedValue: newState});
97 |
98 | onChange(newState);
99 | } else {
100 | // 不级联 单纯改对应数据
101 | if(!controlled){
102 | this.setState({defaultSelectedValue: oldValue});
103 | }
104 |
105 | onChange(oldValue);
106 | }
107 | }
108 | getColumns () {
109 | const {col, data, cascade, value, controlled} = this.props;
110 | const {defaultSelectedValue} = this.state;
111 | const result = [];
112 |
113 | if(controlled){
114 | if(value.length === 0) return;
115 | } else {
116 | if(defaultSelectedValue.length === 0) return;
117 | }
118 |
119 | let array;
120 |
121 | if(cascade){
122 | if(controlled){
123 | array = getColumnsData(data, value, [], 0);
124 | } else {
125 | array = getColumnsData(data, defaultSelectedValue, [], 0);
126 | }
127 | } else {
128 | array = data;
129 | }
130 |
131 | for(let i = 0; i < col; i++){
132 | result.push(
133 |
140 | );
141 | }
142 |
143 | return result;
144 | }
145 | render () {
146 | const {prefixCls} = this.props;
147 | const columns = this.getColumns();
148 |
149 | return (
150 |
151 | {columns}
152 |
153 | )
154 | }
155 | }
156 |
157 | PickerView.propTypes = {
158 | prefixCls: React.PropTypes.string, // 前缀class
159 | col: React.PropTypes.number,
160 | data: React.PropTypes.array,
161 | value: React.PropTypes.array,
162 | cascade: React.PropTypes.bool,
163 | controlled: React.PropTypes.bool, // 是否受控
164 | onChange: React.PropTypes.func
165 | };
166 |
167 | PickerView.defaultProps = {
168 | prefixCls: 'zby-picker-view',
169 | col: 1,
170 | cascade: true,
171 | controlled: false,
172 | };
173 |
174 | export default PickerView
--------------------------------------------------------------------------------
/src/components/Form/components/DateRange.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/24.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import DatePicker from '../../DataEntry/DataPicker/'
7 | import List from '../../DataDisplay/List/'
8 | import moment from 'moment'
9 | import '../style/form.scss'
10 |
11 | const DateRange = (props) => {
12 | const {required, startLabelName, endLabelName, startValue, endValue, readOnly, rangeLabelName, kind, format, onChange} = props;
13 | const datetimeDOM = [];
14 | let startValueText = , endValueText = ;
15 |
16 | if(startValue){
17 | if(kind === 'date'){
18 | startValueText = startValue.format('YYYY-MM-DD');
19 | } else {
20 | startValueText = startValue.format('YYYY-MM-DD HH:mm');
21 | }
22 | }
23 |
24 | if(endValue){
25 | if(kind === 'date'){
26 | endValueText = endValue.format('YYYY-MM-DD');
27 | } else {
28 | endValueText = endValue.format('YYYY-MM-DD HH:mm');
29 | }
30 | }
31 |
32 | function handleChange(from, value) {
33 | const data = {startValue: startValue, endValue: endValue};
34 | data[from] = value;
35 |
36 | onChange(data);
37 | }
38 |
39 | if(startValue && endValue){
40 | // 时间差毫秒数
41 | const diff = endValue.diff(startValue);
42 | // 天数 做两位小数处理
43 | const days = Number.parseInt(endValue.diff(startValue, 'days', true) * 100) / 100;
44 | // 小时 做两位小数处理
45 | const hours = Number.parseInt(diff / (1000 * 60 * 60));
46 | // 分钟 做两位小数处理
47 | const minutes = Number.parseInt((diff % (1000 * 60 * 60)) / (1000 * 60) * 100) / 100;
48 |
49 | let text = `${hours} 小时 ${minutes} 分钟 (${days} 天)`;
50 |
51 | if(format){
52 | text = format(days, hours, minutes);
53 | }
54 |
55 | datetimeDOM.push(text);
56 | }
57 |
58 | return (
59 |
60 |
61 |
62 |
63 |
64 |
{startLabelName}
65 |
66 |
67 | {readOnly ?
68 |
{startValueText}
69 | :
70 |
76 | {startValueText}
77 |
78 | }
79 |
80 |
81 |
82 |
83 |
84 |
85 |
{endLabelName}
86 |
87 |
88 | {readOnly ?
89 |
{endValueText}
90 | :
91 |
97 | {endValueText}
98 |
99 | }
100 |
101 |
102 |
103 |
104 |
105 |
106 |
{rangeLabelName}
107 |
108 |
109 |
{datetimeDOM}
110 |
111 |
112 |
113 |
114 | )
115 | };
116 |
117 | function empty() {}
118 |
119 | DateRange.PropTypes = {
120 | required: React.PropTypes.bool,
121 | startLabelName: React.PropTypes.string.isRequired,
122 | startValue: React.PropTypes.instanceOf(moment),
123 | endLabelName: React.PropTypes.string.isRequired,
124 | endValue: React.PropTypes.instanceOf(moment),
125 | rangeLabelName: React.PropTypes.string.isRequired,
126 | readOnly: React.PropTypes.bool,
127 | kind: React.PropTypes.oneOf(['date', 'datetime']),
128 | format: React.PropTypes.func,
129 | onChange: React.PropTypes.func
130 | };
131 |
132 | DateRange.defaultProps = {
133 | required: false,
134 | readOnly: false,
135 | kind: 'date',
136 | onChange: empty
137 | };
138 |
139 | export default DateRange;
--------------------------------------------------------------------------------
/src/components/DataEntry/Figure/components/Figure.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/7/6.
3 | */
4 | import React from 'react'
5 | import classNames from 'classnames'
6 | import '../style/figure.scss'
7 |
8 | // Figure就是每个图片的容器 以及实现预览的容器
9 | class Figure extends React.Component {
10 | constructor(props) {
11 | super(props);
12 | this.state = {};
13 | this.handlePreview = this.handlePreview.bind(this);
14 | this.handleDelete = this.handleDelete.bind(this);
15 | this.handleReUpload = this.handleReUpload.bind(this);
16 | this.handleClosePreview = this.handleClosePreview.bind(this);
17 | }
18 | componentWillUnmount () {
19 | const {id} = this.props;
20 | const mask = document.getElementById('preview-' + id);
21 |
22 | if(mask) mask.remove();
23 | }
24 | handlePreview () {
25 | const {prefixCls, id, imgUrl, dataUrl, canPreview} = this.props;
26 | const src = imgUrl ? imgUrl : dataUrl;
27 | // 打开预览
28 | if(!canPreview) return;
29 |
30 | // 动态插入dom
31 | const img = document.createElement('img');
32 | img.src = src;
33 | img.onclick = this.handleClosePreview;
34 | const mask = document.createElement('div');
35 | mask.id = 'preview-' + id;
36 | mask.className = `${prefixCls}-preview-container`;
37 | mask.onclick = this.handleClosePreview;
38 | mask.appendChild(img);
39 |
40 | document.body.appendChild(mask);
41 | }
42 | handleDelete (e) {
43 | const {id, onDelete} = this.props;
44 | e.stopPropagation();
45 | document.getElementById(id).className += ' deleted';
46 | const timer = setTimeout(() => {
47 | clearTimeout(timer);
48 | onDelete(id);
49 | }, 300);
50 | }
51 | handleReUpload () {
52 | const {id, onError} = this.props;
53 | onError(id);
54 | }
55 | handleClosePreview (e) {
56 | const {id} = this.props;
57 | document.getElementById('preview-' + id).remove();
58 | e.stopPropagation();
59 | }
60 | getPreviewBoxDOM () {
61 | const {prefixCls, id, status, imgUrl, dataUrl, canDelete} = this.props;
62 | const src = imgUrl ? imgUrl : dataUrl;
63 |
64 | switch (status) {
65 | case 1: {
66 | // 上传中
67 | return (
68 |
73 |
74 |
75 | {canDelete ?
: null}
76 |
77 | );
78 | }
79 | case 2: {
80 | // 上传成功
81 | return (
82 |
87 |
88 |
89 | {canDelete ?
: null}
90 |
91 | );
92 | }
93 | case 3: {
94 | // 上传失败
95 | return (
96 |
101 |
102 |
103 | {canDelete ?
: null}
104 |
105 | );
106 | }
107 | default:
108 | break;
109 | }
110 | }
111 | render() {
112 | const {prefixCls} = this.props;
113 | const previewBoxDOM = this.getPreviewBoxDOM();
114 |
115 | return (
116 |
117 | {previewBoxDOM}
118 |
119 | );
120 | }
121 | }
122 |
123 | function empty() {}
124 |
125 | Figure.propTypes = {
126 | id: React.PropTypes.string.isRequired, // 图片的id
127 | status: React.PropTypes.oneOf([1,2,3]).isRequired, // 此图片上传的状态 1:上传中,2:上传成功,3:上传失败
128 | prefixCls: React.PropTypes.string, // class前缀
129 | canPreview: React.PropTypes.bool, // 是否使用预览功能
130 | canDelete: React.PropTypes.bool, // 是否可以删除
131 | dataUrl: React.PropTypes.string, // 图片的base64编码
132 | imgUrl: React.PropTypes.string, // 图片的路径
133 | onDelete: React.PropTypes.func, // 删除的回调
134 | onError: React.PropTypes.func, // 上传失败的回调
135 | };
136 |
137 | Figure.defaultProps = {
138 | prefixCls: 'zby-figure',
139 | canPreview: true,
140 | canDelete: true,
141 | dataUrl: '',
142 | imgUrl: '',
143 | onDelete: empty,
144 | onError: empty
145 | };
146 |
147 | export default Figure;
--------------------------------------------------------------------------------
/src/routes/Home/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Aus on 2017/10/13.
3 | */
4 | import React from 'react'
5 | import List from '../../components/DataDisplay/List/'
6 | import Tools from '../../components/Tools/Tools'
7 |
8 | const Item = List.Item;
9 |
10 | const Home = () => {
11 | return (
12 |
13 |
React 组件库
14 |
15 | - {Tools.linkTo('/data-entry/button')}}
18 | >
19 | 按钮
20 |
21 | - {Tools.linkTo('/data-entry/switch')}}
24 | >
25 | 滑动按钮
26 |
27 | - {Tools.linkTo('/data-entry/date-picker')}}
30 | >
31 | 日期选择器
32 |
33 | - {Tools.linkTo('/data-entry/picker')}}
36 | >
37 | 选择器
38 |
39 | - {Tools.linkTo('/data-entry/picker-view')}}
42 | >
43 | 选择器
44 |
45 | - {Tools.linkTo('/data-entry/uploader')}}
48 | >
49 | 上传预览
50 |
51 |
52 |
53 | - {Tools.linkTo('/data-display/list')}}
56 | >
57 | 列表项
58 |
59 | - {Tools.linkTo('/data-display/carousel')}}
62 | >
63 | 轮播图
64 |
65 | - {Tools.linkTo('/data-display/tooltip')}}
68 | >
69 | 文字提示
70 |
71 | - {Tools.linkTo('/data-display/badge')}}
74 | >
75 | 徽章
76 |
77 | {/*- {Tools.linkTo('/data-display/tag')}}*/}
80 | {/*>标签*/}
81 | {/*
*/}
82 |
83 |
84 |
85 | - {Tools.linkTo('/feedback/toast')}}
88 | >
89 | 提示
90 |
91 |
92 |
93 |
94 | - {Tools.linkTo('/form/input')}}
97 | >
98 | 单行文本
99 |
100 |
101 | {/**/}
102 | {/**/}
103 |
104 | {/*- {Tools.linkTo('/form/textarea')}}*/}
107 | {/*>多行文本*/}
108 | {/*
*/}
109 | {/*- {Tools.linkTo('/form/number')}}*/}
112 | {/*>数值*/}
113 | {/*
*/}
114 | {/*- {Tools.linkTo('/form/switch')}}*/}
117 | {/*>开关*/}
118 | {/*
*/}
119 | {/*- {Tools.linkTo('/form/date-range')}}*/}
122 | {/*>日期区间*/}
123 | {/*
*/}
124 | {/*- {Tools.linkTo('/form/date-time')}}*/}
127 | {/*>日期时刻*/}
128 | {/*
*/}
129 | {/*- {Tools.linkTo('/form/select')}}*/}
132 | {/*>单选框*/}
133 | {/*
*/}
134 | {/*- {Tools.linkTo('/form/checkbox')}}*/}
137 | {/*>复选框*/}
138 | {/*
*/}
139 | {/*- {Tools.linkTo('/form/form')}}*/}
142 | {/*>表单*/}
143 | {/*
*/}
144 | {/*
*/}
145 |
146 | )
147 | };
148 |
149 | export default Home
--------------------------------------------------------------------------------