├── .DS_Store ├── .babelrc ├── .gitignore ├── README.md ├── assets ├── .DS_Store ├── iconfonts │ ├── .DS_Store │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.js │ ├── iconfont.svg │ ├── iconfont.ttf │ └── iconfont.woff └── scale_default.png ├── bin └── dev-server.js ├── dist ├── 29933c03dca9629dd8bfef50bec5005f.png ├── 3f8467b01369f58b0a67ac2ed08c8f3a.eot ├── 571a48ecabb3c98f28e5982d3f811af8.svg ├── ab85a97f51f177206f0635614657e685.ttf ├── bundle.js └── index.html ├── index.html ├── libs ├── .DS_Store ├── Button │ ├── index.js │ └── index.less ├── Checkbox │ ├── index.js │ └── index.less ├── ContentEditable │ ├── index.js │ └── index.less ├── Dialog │ ├── index.js │ └── index.less ├── DragSort │ ├── example.js │ ├── index.js │ └── index.less ├── Dropdown │ ├── index.js │ └── index.less ├── Input │ ├── index.js │ └── index.less ├── Radio │ ├── index.js │ └── index.less └── Shake │ ├── index.js │ └── index.less ├── package-lock.json ├── package.json ├── react-questionnair.png ├── src ├── .DS_Store ├── Questionnair │ ├── index.js │ └── index.less ├── QuestionnairAnswer │ ├── index.js │ └── index.less ├── QuestionnairContent │ ├── index.js │ └── index.less ├── QuestionnairEditor │ ├── index.js │ └── index.less ├── QuestionnairSiderbar │ ├── index.js │ └── index.less ├── index.js └── main.js ├── utils └── utils.js └── webpack └── webpack.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "react", 5 | "stage-1" 6 | ], 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | npm-debug.log* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-questionnair 2 | 3 | 基于react开发的自定义问卷调查表。目前只支持单选题、下拉题、多选题、单行文本题、多行文本题、填空题六种基本题型。支持拖拽排序功能。 4 | 5 | ## 预览 6 | ![preview](react-questionnair.png) 7 | 8 | ## 安装 9 | 10 | npm install react-questionnair 11 | 12 | ## Demo 开发 13 | 14 | ```shell 15 | $ git clone https://github.com/pandly/react-questionnair.git 16 | $ cd react-questionnair 17 | $ npm install 18 | $ npm run dev 19 | 20 | ``` 21 | [demo地址](https://pandly.github.io/react-questionnair/dist/) 22 | 23 | ## 使用 24 | 25 | ```shell 26 | import React from 'react' 27 | import Questionnnair from 'react-questionnair' 28 | 29 | //编辑题目 30 | //渲染题目 31 | //渲染答案 32 | 33 | ``` 34 | 35 | ## APIs 36 | 37 | | 属性 | 描述 | 类型 | 默认值 | 38 | | --------- | :------------------------------ | :------: | :----: | 39 | | editor | 编辑器数据结构 | array | | 40 | | acitveAnswer | 编辑器组件为true时可以进行答案填写 | boolean | false | 41 | 42 | 43 | | 事件 | 描述 | 参数 | 44 | | --------- | :------------------------------ | :------: | 45 | | onDrag | 当拖拽题目时会触发该事件(包括题目栏拖拽) | array | 46 | | onConfirm | 当确认编辑题目时会触发该事件 | array | 47 | | onCopy | 当拷贝题目时会触发该事件 | array | 48 | | onRemove | 当移除题目时会触发该事件 | array | 49 | | onSign | 当标记问卷时会触发该事件 | array | 50 | | onSaveTitle | 当问卷题目失焦时会触发该事件 | array | 51 | 52 | ## 数据结构 53 | 54 | ``` 55 | //可供选择的type类型 56 | const type = { 57 | radio: '单选题', 58 | dropdown: '下拉题', 59 | checkbox: '多选题', 60 | text: '单行文本题', 61 | textarea: '多行文本题', 62 | input: '填空题' 63 | } 64 | 65 | //react-questionnair按照如下的数据结构约定一个编辑器,开发时可以按照如下的数据结构约定好 66 | const editor = { 67 | questionId: uuid(), //id 68 | type: type, //类型,根据类型渲染出相应的题型 69 | title: '', //题目 70 | required: false, //是否必填 71 | remark: false, //是否有备注 72 | remarkText: '', //备注内容 73 | options: ['选项', '选项'], //选项(只有radio,checkbox,select有,其余尽量给个空数组) 74 | rows: 1, //选项占的行数 75 | textareaHeight: 3, //多行文本高度 76 | maxLength: 50, //单行文本限制的字数 77 | otherOption: false, //是否有其他选项 78 | otherOptionForwards: '其他', //”其他“项文本(前) 79 | otherOptionBackwards: '', //”其他“项文本(后) 80 | completionForwards: '题目:', //填空题文本(前) 81 | completionBackwards: '', //填空题文本(后) 82 | isEditor: true, //编辑状态还是已编辑状态 83 | isFirst: true, //是否是新创建的 84 | editorShake: '' 85 | } 86 | 87 | ``` 88 | 89 | ## 注意事项 90 | 91 | * 题目编辑器都是循环``渲染出来的,react-questionnair的设计思路就是将编辑器数组状态提升, 92 | 每次进行编辑器修改时(触发API事件),都会反映在编辑器数组中,然后再重新渲染整个问卷表。与后端交互的话把最新的题目数组返回给后端保存就行。 93 | 94 | * 其他模块需要编辑好的题目话,根据相应的id去后端取相应的题目``渲染在页面上。 95 | 96 | * 填写完题目以后再根据``组件渲染答案。 97 | -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/assets/.DS_Store -------------------------------------------------------------------------------- /assets/iconfonts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/assets/iconfonts/.DS_Store -------------------------------------------------------------------------------- /assets/iconfonts/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('iconfont.eot?t=1528702461354'); /* IE9*/ 4 | src: url('iconfont.eot?t=1528702461354#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'), 6 | url('iconfont.ttf?t=1528702461354') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1528702461354#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-daisuifang-icon-green:before { content: "\e601"; } 19 | 20 | .icon-yisuifang-icon-green:before { content: "\e602"; } 21 | 22 | .icon-zhankai:before { content: "\e604"; } 23 | 24 | .icon-xiaoxi:before { content: "\e605"; } 25 | 26 | .icon-shouqi:before { content: "\e606"; } 27 | 28 | .icon-suifangguanliicon:before { content: "\e607"; } 29 | 30 | .icon-huanzheguanliicon:before { content: "\e608"; } 31 | 32 | .icon-suifangmobanicon:before { content: "\e609"; } 33 | 34 | .icon-dengpao:before { content: "\e60a"; } 35 | 36 | .icon-chachaicon:before { content: "\e60b"; cursor: pointer; margin-left: 10px; color: #979797;} 37 | 38 | .icon-fangdajingicon:before { content: "\e60c"; } 39 | 40 | .icon-jinggaotanhaoicon:before { content: "\e60d"; } 41 | 42 | .icon-shanchuicon:before { content: "\e60e"; } 43 | 44 | .icon-fuzhiicon:before { content: "\e60f"; } 45 | 46 | .icon-tianjiaicon:before { content: "\e610"; } 47 | 48 | .icon-information:before { content: "\e612"; } 49 | 50 | .icon-baocunchenggong:before { content: "\e613"; } 51 | 52 | .icon-danxuanicon:before { content: "\e614"; } 53 | 54 | .icon-duohangicon:before { content: "\e615"; } 55 | 56 | .icon-bi:before { content: "\e616"; } 57 | 58 | .icon-baocunzhong:before { content: "\e617"; } 59 | 60 | .icon-hongselajixiang:before { content: "\e618"; } 61 | 62 | .icon-lansezantingshiyong:before { content: "\e619"; } 63 | 64 | .icon-duoxuan-icon:before { content: "\e61a"; } 65 | 66 | .icon-guaduan_icon:before { content: "\e61b"; } 67 | 68 | .icon-jinggaochacha:before { content: "\e61c"; } 69 | 70 | .icon-querenbodaicon:before { content: "\e61d"; } 71 | 72 | .icon-sanjiaoxingjinggao:before { content: "\e61e"; } 73 | 74 | .icon-shanchuwenzichacha:before { content: "\e61f"; } 75 | 76 | .icon-suifangjihuaicon:before { content: "\e620"; } 77 | 78 | .icon-rili:before { content: "\e621"; } 79 | 80 | .icon-tiankongtiicon:before { content: "\e622"; } 81 | 82 | .icon-xialaicon:before { content: "\e623"; } 83 | 84 | .icon-tianjialiebiao_icon:before { content: "\e625"; } 85 | 86 | .icon-green_guanbiyulan:before { content: "\e626"; } 87 | 88 | .icon-green_phone:before { content: "\e629"; } 89 | 90 | .icon-grey_bianji:before { content: "\e62c"; } 91 | 92 | .icon-grey_shanchu:before { content: "\e62d"; } 93 | 94 | .icon-grey_fuzhi:before { content: "\e62e"; } 95 | 96 | .icon-Q-icon:before { content: "\e62f"; color: #06AEA6;} 97 | 98 | .icon-grey_yanjing:before { content: "\e630"; } 99 | 100 | .icon-white_jinggao:before { content: "\e631"; } 101 | 102 | .icon-red_phone:before { content: "\e632"; } 103 | 104 | .icon-suifangyuqi-icon-color:before { content: "\e634"; } 105 | 106 | .icon-yisuifang-icon-color:before { content: "\e638"; } 107 | 108 | .icon-daisuifangicon-color:before { content: "\e633"; } 109 | 110 | .icon-shijianweidao-icon-color:before { content: "\e636"; } 111 | 112 | .icon-tongyongbiaotiicon:before { content: "\e611"; } 113 | 114 | .icon-xuanxiangicon:before { content: "\e63b"; color: #cdcdcd;} 115 | 116 | .icon-zhongxinfabuicon:before { content: "\e63c"; } 117 | 118 | .icon-xinjianxuanxiangicon:before { content: "\e63d"; margin: 0 10px;} 119 | 120 | .icon-jieshu:before { content: "\e600"; } 121 | 122 | .icon-danhangicon:before { content: "\e603"; } 123 | 124 | -------------------------------------------------------------------------------- /assets/iconfonts/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/assets/iconfonts/iconfont.eot -------------------------------------------------------------------------------- /assets/iconfonts/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /assets/iconfonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/assets/iconfonts/iconfont.ttf -------------------------------------------------------------------------------- /assets/iconfonts/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/assets/iconfonts/iconfont.woff -------------------------------------------------------------------------------- /assets/scale_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/assets/scale_default.png -------------------------------------------------------------------------------- /bin/dev-server.js: -------------------------------------------------------------------------------- 1 | /* 2 | * webpack-dev-server是一个小型的静态文件服务器,为webpack打包的资源文件提供Web服务 3 | */ 4 | const WebpackDevServer = require('webpack-dev-server'); 5 | const config = require('../webpack/webpack.config'); 6 | const webpack = require('webpack'); 7 | const path = require('path'); 8 | const compiler = webpack(config); 9 | const port = 9090; 10 | 11 | const server = new WebpackDevServer(compiler, { 12 | contentBase: path.resolve(__dirname, '../dist'), //默认会以根文件夹提供本地服务器,这里指定文件夹 13 | historyApiFallback: true, //在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html 14 | port: port, //如果省略,默认8080 15 | publicPath: "/", 16 | inline: true, // 自动刷新 17 | hot: true, // 开启热模块替换 18 | }); 19 | 20 | console.log('> Starting dev server...') 21 | server.listen(port, 'localhost', function(err) { 22 | if (err) throw err 23 | }) -------------------------------------------------------------------------------- /dist/29933c03dca9629dd8bfef50bec5005f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/dist/29933c03dca9629dd8bfef50bec5005f.png -------------------------------------------------------------------------------- /dist/3f8467b01369f58b0a67ac2ed08c8f3a.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/dist/3f8467b01369f58b0a67ac2ed08c8f3a.eot -------------------------------------------------------------------------------- /dist/ab85a97f51f177206f0635614657e685.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/dist/ab85a97f51f177206f0635614657e685.ttf -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | react-questionnair 6 | 18 | 19 | 20 |
21 | 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | react-questionnair 6 | 18 | 19 | 20 |
21 | 22 | -------------------------------------------------------------------------------- /libs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandly/react-questionnair/6a7f3c7a563826b59fcb6f7b55ac7b9971a2c0f7/libs/.DS_Store -------------------------------------------------------------------------------- /libs/Button/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './index.less'; 3 | 4 | class Button extends React.PureComponent { 5 | static defaultProps = { 6 | disabled: false, 7 | type: '', 8 | size: '', 9 | } 10 | 11 | handleClick = (e) => { 12 | const { 13 | onClick, 14 | } = this.props; 15 | if (onClick) { 16 | onClick(); 17 | }; 18 | } 19 | 20 | render() { 21 | const { 22 | type, 23 | size, 24 | disabled, 25 | children, 26 | onClick, 27 | ...otherProps, 28 | } = this.props; 29 | const buttonType = type ? `wowjoy-button__${type}` : ''; 30 | const buttonSize = size ? `wowjoy-button__${size}` : ''; 31 | return ( 32 | 38 | ); 39 | } 40 | } 41 | 42 | export default Button; -------------------------------------------------------------------------------- /libs/Button/index.less: -------------------------------------------------------------------------------- 1 | :global{ 2 | .wowjoy-button { 3 | padding: 6px 20px; 4 | border: 1px solid #06aea6; 5 | margin: 0 10px; 6 | outline: none; 7 | cursor: pointer; 8 | font-size: 14px 9 | } 10 | .wowjoy-button__primary { 11 | background: #06aea6; 12 | color: #fff; 13 | } 14 | .wowjoy-button__normal { 15 | background: #fff; 16 | color: #06aea6; 17 | } 18 | .wowjoy-button__mini { 19 | padding: 4px; 20 | font-size: 12px; 21 | } 22 | .wowjoy-button__small { 23 | padding: 7px 9px; 24 | font-size: 12px; 25 | } 26 | .wowjoy-button__large { 27 | padding: 11px 19px; 28 | font-size: 16px; 29 | } 30 | }; -------------------------------------------------------------------------------- /libs/Checkbox/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './index.less'; 3 | 4 | class Checkbox extends React.PureComponent { 5 | static defaultProps = { 6 | value: '', 7 | name: '', 8 | } 9 | 10 | handleChange = (e) => { 11 | const { 12 | onChange, 13 | index, 14 | } = this.props; 15 | if (onChange) { 16 | onChange(e, index); 17 | }; 18 | } 19 | 20 | render() { 21 | const { 22 | defaultChecked, 23 | value, 24 | name, 25 | index, 26 | label, 27 | style, 28 | } = this.props; 29 | return ( 30 | 42 | ); 43 | } 44 | } 45 | 46 | export default Checkbox; -------------------------------------------------------------------------------- /libs/Checkbox/index.less: -------------------------------------------------------------------------------- 1 | :global { 2 | .wowjoy-checkbox { 3 | cursor: pointer; 4 | display: inline-block; 5 | } 6 | input[type='checkbox']:checked { 7 | &+.wowjoy-checkbox__inner { 8 | border-color: #06aea6; 9 | &:before { 10 | content: '\2713'; 11 | color: #06aea6; 12 | position: absolute; 13 | top: 50%; 14 | left: 50%; 15 | transform: translate(-50%, -50%); 16 | } 17 | } 18 | } 19 | .wowjoy-checkbox__inner { 20 | position: relative; 21 | display: inline-block; 22 | width: 16px; 23 | height: 16px; 24 | background: #fff; 25 | border: 1px solid #DBDBDB; 26 | vertical-align: sub; 27 | margin-right: 5px; 28 | } 29 | .wowjoy-checkbox__text { 30 | display: inline-block; 31 | max-width: 80%; 32 | vertical-align: top; 33 | word-break: break-all; 34 | } 35 | } -------------------------------------------------------------------------------- /libs/ContentEditable/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './index.less'; 3 | 4 | const stripNbsp = str => str.replace(/ |\u202F|\u00A0/g, ' '); 5 | 6 | export default class ContentEditable extends React.Component { 7 | shouldComponentUpdate(nextProps) { 8 | let { 9 | props, 10 | htmlEl 11 | } = this; 12 | if (JSON.stringify(this.props.style) === JSON.stringify(nextProps.style)) { 13 | return false; 14 | }; 15 | // We need not rerender if the change of props simply reflects the user's edits. 16 | // Rerendering in this case would make the cursor/caret jump 17 | 18 | // Rerender if there is no element yet... (somehow?) 19 | if (!htmlEl) { 20 | return true; 21 | }; 22 | // ...or if html really changed... (programmatically, not by user edit) 23 | if ( 24 | stripNbsp(nextProps.html) !== stripNbsp(htmlEl.innerHTML) && 25 | nextProps.html !== props.html 26 | ) { 27 | return true; 28 | }; 29 | let optional = ['style', 'className', 'disabled', 'tagName']; 30 | // Handle additional properties 31 | return optional.some(name => props[name] !== nextProps[name]); 32 | } 33 | 34 | componentDidUpdate() { 35 | if (this.htmlEl && this.props.html !== this.htmlEl.innerHTML) { 36 | // Perhaps React (whose VDOM gets outdated because we often prevent 37 | // rerendering) did not update the DOM. So we update it manually now. 38 | this.htmlEl.innerHTML = this.props.html; 39 | }; 40 | } 41 | 42 | emitChange = (evt) => { 43 | if (!this.htmlEl) return; 44 | var name = evt.target.dataset.name; 45 | var html = this.htmlEl.innerHTML; 46 | if (this.props.onChange && html !== this.lastHtml) { 47 | // Clone event with Object.assign to avoid 48 | // "Cannot assign to read only property 'target' of object" 49 | var evt = Object.assign({}, evt, { 50 | target: { 51 | value: html, 52 | name: name, 53 | }, 54 | }); 55 | this.props.onChange(evt); 56 | } 57 | this.lastHtml = html; 58 | } 59 | 60 | render() { 61 | var { 62 | tagName, 63 | name, 64 | html, 65 | style, 66 | onKeyPress, 67 | ...otherProps, 68 | } = this.props; 69 | 70 | return ( 71 | // React.createElement( 72 | // tagName || 'div', 73 | // { 74 | // ...props, 75 | // ref: (e) => this.htmlEl = e, 76 | // onInput: this.emitChange, 77 | // onBlur: this.props.onBlur || this.emitChange, 78 | // contentEditable: !this.props.disabled, 79 | // dangerouslySetInnerHTML: {__html: html} 80 | // }, 81 | // this.props.children); 82 |
this.htmlEl = e} 87 | onInput={this.emitChange} 88 | onKeyPress={onKeyPress} 89 | //onBlur={this.props.onBlur || this.emitChange} 90 | contentEditable={!this.props.disabled} 91 | dangerouslySetInnerHTML={{__html: html}}> 92 | {this.props.children} 93 |
94 | ); 95 | } 96 | } -------------------------------------------------------------------------------- /libs/ContentEditable/index.less: -------------------------------------------------------------------------------- 1 | :global { 2 | .contentEditable { 3 | outline: none; 4 | line-height: 36px; 5 | } 6 | } -------------------------------------------------------------------------------- /libs/Dialog/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from 'libs/Button'; 3 | import './index.less' 4 | 5 | class Dialog extends React.PureComponent { 6 | state = { 7 | visible: this.props.visible, 8 | } 9 | 10 | componentWillReceiveProps(nextProps) { 11 | if (nextProps.visible !== this.props.visible) { 12 | this.setState({ 13 | visible: nextProps.visible, 14 | }); 15 | }; 16 | } 17 | 18 | confirm = () => { 19 | const { 20 | onConfirm, 21 | } = this.props; 22 | if (onConfirm) { 23 | onConfirm(); 24 | }; 25 | } 26 | 27 | cancel = () => { 28 | const { 29 | onCancel, 30 | } = this.props; 31 | if (onCancel) { 32 | onCancel(); 33 | }; 34 | } 35 | 36 | render() { 37 | const { 38 | title, 39 | children, 40 | onCancel, 41 | onConfirm, 42 | } = this.props; 43 | const { 44 | visible, 45 | } = this.state; 46 | const fade = visible ? 'wowjoy-dialog__fadeIn' : ''; 47 | return ( 48 |
49 |
50 |
51 | {title} 52 |
53 |
54 | {children} 55 |
56 |
57 | 58 | 59 |
60 |
61 |
62 | ); 63 | } 64 | } 65 | 66 | export default Dialog; -------------------------------------------------------------------------------- /libs/Dialog/index.less: -------------------------------------------------------------------------------- 1 | :global { 2 | .wowjoy-dialog { 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | right: 0; 7 | bottom: 0; 8 | background: rgba(0,0,0,0.30); 9 | //visibility: hidden; 10 | display: none; 11 | //transition: all .3s; 12 | z-index: 10; 13 | } 14 | .wowjoy-dialog__fadeIn { 15 | display: block; 16 | } 17 | .wowjoy-dialog__inner { 18 | background: #FFFFFF; 19 | box-shadow: 0 0 4px 0 rgba(0,0,0,0.20); 20 | border-radius: 3px; 21 | padding: 10px 20px; 22 | width: 40%; 23 | max-width: 500px; 24 | min-width: 300px; 25 | position: absolute; 26 | top: 50%; 27 | left: 50%; 28 | transform: translate(-50%, -50%); 29 | //opacity: 0; 30 | //transition: all .3s; 31 | } 32 | .wowjoy-dialog__slideDown { 33 | opacity: 1; 34 | transform: translateY(-50%); 35 | } 36 | .wowjoy-dialog__header { 37 | margin-bottom: 15px; 38 | font-family: PingFangSC-Medium; 39 | font-size: 16px; 40 | color: #333333; 41 | } 42 | .wowjoy-dialog__footer { 43 | height: 40px; 44 | width: 100%; 45 | display: flex; 46 | align-items: center; 47 | justify-content: center; 48 | } 49 | }; -------------------------------------------------------------------------------- /libs/DragSort/example.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DragSort from './index.js'; 3 | 4 | export default class DragSortExample extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | list: [{ 9 | name: 'title' 10 | }, { 11 | name: 'name' 12 | }, { 13 | name: 'code' 14 | }, { 15 | name: 'email' 16 | }], 17 | curMoveItem: null, 18 | index: '', 19 | dragged: false, 20 | } 21 | } 22 | 23 | handleDragMove = (data, from, to) => { 24 | this.setState({ 25 | curMoveItem: to, 26 | list: data, 27 | index: null, 28 | }); 29 | } 30 | 31 | handleDragEnd = (index) => { 32 | this.setState({ 33 | curMoveItem: null, 34 | dragged: false, 35 | index, 36 | }); 37 | } 38 | 39 | enter = (index) => { 40 | if (this.state.index !== null) { 41 | this.setState({ 42 | index, 43 | }); 44 | }; 45 | } 46 | 47 | leave = () => { 48 | if (this.state.index !== null) { 49 | this.setState({ 50 | index: '', 51 | }); 52 | }; 53 | } 54 | 55 | render() { 56 | const { 57 | dragged, 58 | } = this.state; 59 | const el = this.state.list.map((item, index) => { 60 | return ( 61 |
69 |
{item.name}
70 |
71 | ); 72 | }); 73 | return ( 74 |
75 |
    76 | 81 | {el} 82 | 83 |
84 |
85 | ); 86 | } 87 | } -------------------------------------------------------------------------------- /libs/DragSort/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | let curDragIndex = null; 4 | 5 | export default function DragSort(props) { 6 | let container = props.children; 7 | let draggable = props.draggable; 8 | 9 | function onChange(from, to) { 10 | let curValue = props.data; 11 | let newValue = arrMove(curValue, from, to); 12 | if (typeof props.onDragMove === 'function') { 13 | return props.onDragMove(newValue, from, to); 14 | } 15 | } 16 | return ( 17 |
18 | {container.map((item, index)=>{ 19 | if(React.isValidElement(item)){ 20 | return React.cloneElement(item, { 21 | draggable, 22 | //开始拖动元素时触发此事件 23 | onDragStart(){ 24 | curDragIndex = index; 25 | }, 26 | /* 27 | * 当被拖动的对象进入其容器范围内时触发此事件 28 | * 在自身拖动时也会触发该事件 29 | */ 30 | onDragEnter() { 31 | onChange(curDragIndex, index); 32 | curDragIndex = index; 33 | }, 34 | /* 35 | * 当被拖动的对象在另一对象容器范围内拖动时触发此事件 36 | * 在拖动元素时,每隔350毫秒会触发onDragOver事件 37 | */ 38 | onDragOver(e) { 39 | /* 40 | * 默认情况下,数据/元素不能放置到其他元素中。如果要实现该功能,我们需要 41 | * 防止元素的默认处理方法,我们可以通过调用event.preventDefault()方法来实现onDragOver事件 42 | */ 43 | e.preventDefault(); 44 | }, 45 | //完成元素拖动后触发 46 | onDragEnd(){ 47 | curDragIndex = null; 48 | if(typeof props.onDragEnd === 'function'){ 49 | props.onDragEnd(index); 50 | }; 51 | }, 52 | }) 53 | } 54 | return item; 55 | })} 56 |
57 | ); 58 | } 59 | 60 | function arrMove(arr, fromIndex, toIndex) { 61 | if (fromIndex !== toIndex) { 62 | arr = arr.concat(); 63 | let item = arr.splice(fromIndex, 1)[0]; 64 | arr.splice(toIndex, 0, item); 65 | }; 66 | return arr; 67 | } -------------------------------------------------------------------------------- /libs/DragSort/index.less: -------------------------------------------------------------------------------- 1 | :global { 2 | .item{ 3 | height: 35px; 4 | margin:10px; 5 | // background-color: #eee; 6 | // &:hover { 7 | // background-color: red; 8 | // } 9 | } 10 | .item-notdrag { 11 | height: 35px; 12 | margin:10px; 13 | background-color: #eee; 14 | 15 | } 16 | .inner { 17 | height: 100%; 18 | } 19 | .item.active{ 20 | //height: 35px; 21 | //margin:10px; 22 | opacity: 0; 23 | //background-color: #eee !important; 24 | } 25 | }; -------------------------------------------------------------------------------- /libs/Dropdown/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './index.less'; 3 | 4 | class Select extends React.PureComponent { 5 | 6 | handleChange = (e) => { 7 | const { 8 | onChange, 9 | } = this.props; 10 | if (onChange) { 11 | onChange(e); 12 | }; 13 | } 14 | 15 | render() { 16 | const { 17 | name, 18 | value, 19 | options, 20 | } = this.props; 21 | return ( 22 |
23 | 33 |
34 | ); 35 | } 36 | } 37 | 38 | export default Select; -------------------------------------------------------------------------------- /libs/Dropdown/index.less: -------------------------------------------------------------------------------- 1 | :global{ 2 | .wowjoy-select { 3 | display: inline-block; 4 | } 5 | }; -------------------------------------------------------------------------------- /libs/Input/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './index.less' 3 | 4 | class Input extends React.PureComponent { 5 | static defaultProps = { 6 | disabled: false, 7 | type: 'text', 8 | } 9 | 10 | handleChange = (e) => { 11 | const { 12 | onChange, 13 | index, 14 | } = this.props; 15 | if (onChange) { 16 | onChange(e, index); 17 | }; 18 | } 19 | 20 | handleBlur = (e) => { 21 | const { 22 | onBlur, 23 | index, 24 | } = this.props; 25 | if (onBlur) { 26 | onBlur(e, index); 27 | }; 28 | } 29 | 30 | fixControlledValue = (value) => { 31 | if (typeof value === 'undefined' || value === null) { 32 | return ''; 33 | }; 34 | return value; 35 | } 36 | 37 | render() { 38 | const { 39 | type, 40 | name, 41 | width, 42 | margin, 43 | style, 44 | maxLength, 45 | rows, 46 | disabled, 47 | ...otherProps 48 | } = this.props; 49 | /* 50 | * defaultValue只会在第一次渲染有效 51 | * defaultValue和value尽量不共存,如果共存的话value将会覆盖defaultValue 52 | * 在共存的情况下,如果value值为undefined或者null,会被defaultValue覆盖 53 | */ 54 | if ('value' in otherProps) { 55 | otherProps.value = this.fixControlledValue(otherProps.value); 56 | delete otherProps.defaultValue; 57 | }; 58 | return ( 59 |
62 | {type === 'textarea' ? ( 63 |