narro 6 years ago
parent
commit
1b1ab25660

+ 4
- 0
CHANGELOG.md View File

1
 # CHANGELOG
1
 # CHANGELOG
2
 
2
 
3
+## 0.5.6
4
+- [x] Editor增加maxLength属性
5
+- [x] RenderText增加换行支持 
6
+
3
 ## 0.5.5
7
 ## 0.5.5
4
 
8
 
5
 - [x] 增加删除评论、回复功能
9
 - [x] 增加删除评论、回复功能

+ 2
- 2
README.md View File

2
 
2
 
3
 通用评论系统及编辑器
3
 通用评论系统及编辑器
4
 
4
 
5
-**`version 0.5.5`**
5
+**`version 0.5.6`**
6
 
6
 
7
 ```js
7
 ```js
8
 import Comment, { Editor, RenderText } from 'comment';
8
 import Comment, { Editor, RenderText } from 'comment';
53
 | closeUploadWhenBlur | boolean                         |               | false    | 当 upload 失去焦点(鼠标点击非 Upload 的区域)的时候,是否自动关闭 Popover                        |
53
 | closeUploadWhenBlur | boolean                         |               | false    | 当 upload 失去焦点(鼠标点击非 Upload 的区域)的时候,是否自动关闭 Popover                        |
54
 | showError           | boolean                         | true          | false    | 是否使用Antd的Message组件提示错误信息, 主要是上传图片出错的情况                                   |
54
 | showError           | boolean                         | true          | false    | 是否使用Antd的Message组件提示错误信息, 主要是上传图片出错的情况                                   |
55
 | onError             | function(msg,{response})        |               | false    | 错误回调, 出错了会被调用, 主要是上传图片出错的情况                                                |
55
 | onError             | function(msg,{response})        |               | false    | 错误回调, 出错了会被调用, 主要是上传图片出错的情况                                                |
56
-
56
+| maxLength           | number                          | 140           | false    | 限制最大输入字数                                                                                  |
57
 
57
 
58
 
58
 
59
 
59
 

+ 8
- 4
lib/App.js View File

126
     key: "errorHandler",
126
     key: "errorHandler",
127
     value: function errorHandler(error) {
127
     value: function errorHandler(error) {
128
       if (error.response && error.response.data && error.response.data.msg) {
128
       if (error.response && error.response.data && error.response.data.msg) {
129
-        this.error(_lang2.default[error.response.data.msg] || _constant.ERROR_DEFAULT, { response: error.response });
129
+        this.error(_lang2.default[error.response.data.msg] || _constant.ERROR_DEFAULT, {
130
+          response: error.response
131
+        });
130
         return;
132
         return;
131
       }
133
       }
132
-      this.error(_lang2.default[error.message] || _constant.ERROR_DEFAULT, { response: error.response });
134
+      this.error(_lang2.default[error.message] || _constant.ERROR_DEFAULT, {
135
+        response: error.response
136
+      });
133
     }
137
     }
134
 
138
 
135
     /**
139
     /**
373
 
377
 
374
     /**
378
     /**
375
      * 删除回复
379
      * 删除回复
376
-     * @param {*} replyId 
377
-     * @param {*} commentId 
380
+     * @param {*} replyId
381
+     * @param {*} commentId
378
      */
382
      */
379
 
383
 
380
   }, {
384
   }, {

+ 1
- 1
lib/App.js.map
File diff suppressed because it is too large
View File


+ 8
- 5
lib/components/ContentItem/index.js View File

209
             ) : null,
209
             ) : null,
210
             app.userId === content.user_id && _react2.default.createElement(
210
             app.userId === content.user_id && _react2.default.createElement(
211
               _popconfirm2.default,
211
               _popconfirm2.default,
212
-              { title: "\u786E\u5B9A\u8981\u5220\u9664\u5417?", onConfirm: function onConfirm() {
212
+              {
213
+                title: "\u786E\u5B9A\u8981\u5220\u9664\u5417?",
214
+                onConfirm: function onConfirm() {
213
                   if (replyId) {
215
                   if (replyId) {
214
                     app.sDeleteReply(content.id, commentId);
216
                     app.sDeleteReply(content.id, commentId);
215
                     return;
217
                     return;
216
                   }
218
                   }
217
                   app.sDeleteComment(content.id);
219
                   app.sDeleteComment(content.id);
218
-                }, okText: "\u786E\u5B9A", cancelText: "\u53D6\u6D88" },
220
+                },
221
+                okText: "\u786E\u5B9A",
222
+                cancelText: "\u53D6\u6D88"
223
+              },
219
               _react2.default.createElement(
224
               _react2.default.createElement(
220
                 "a",
225
                 "a",
221
-                {
222
-                  className: "comment-item-bottom-right"
223
-                },
226
+                { className: "comment-item-bottom-right" },
224
                 "\xA0 \u5220\u9664"
227
                 "\xA0 \u5220\u9664"
225
               )
228
               )
226
             ),
229
             ),

+ 1
- 1
lib/components/ContentItem/index.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
lib/components/Editor/Upload.js.map
File diff suppressed because it is too large
View File


+ 67
- 45
lib/components/Editor/index.css View File

1
+.comment-editor-toolbar {
2
+  text-align: right;
3
+  padding: 0 20px 10px 0;
4
+}
5
+
6
+.comment-editor-toolbar-error {
7
+  color: red;
8
+}
9
+
1
 .comment-editor {
10
 .comment-editor {
2
-  box-sizing: border-box;
3
-  margin: 0;
4
-  padding: 0;
5
-  width: 100%;
6
-  max-width: 100%;
7
-  list-style: none;
8
-  position: relative;
9
-  display: block;
10
-  font-size: 14px;
11
-  line-height: 1.5;
12
-  color: rgba(0, 0, 0, 0.65);
13
-  background-color: #fff;
14
-  background-image: none;
15
-  border: 1px solid #d9d9d9;
16
-  border-radius: 4px;
17
-  transition: all 0.3s, height 0s;
11
+    box-sizing: border-box;
12
+    margin: 0;
13
+    padding: 0;
14
+    width: 100%;
15
+    max-width: 100%;
16
+    list-style: none;
17
+    position: relative;
18
+    display: block;
19
+    font-size: 14px;
20
+    line-height: 1.5;
21
+    color: rgba(0, 0, 0, 0.65);
22
+    background-color: #fff;
23
+    background-image: none;
24
+    border: 1px solid #d9d9d9;
25
+    border-radius: 4px;
26
+    transition: all 0.3s, height 0s;
18
 }
27
 }
28
+
19
 .comment-editor textarea.ant-input {
29
 .comment-editor textarea.ant-input {
20
-  border: none;
21
-  border-bottom: 1px solid #eee;
22
-  border-bottom-right-radius: 0;
23
-  border-bottom-left-radius: 0;
30
+    border: none;
31
+    border-bottom: 1px solid #eee;
32
+    border-bottom-right-radius: 0;
33
+    border-bottom-left-radius: 0;
24
 }
34
 }
35
+
25
 .comment-editor textarea.ant-input:hover {
36
 .comment-editor textarea.ant-input:hover {
26
-  border: none;
27
-  border-bottom: 1px solid #eee;
37
+    border: none;
38
+    border-bottom: 1px solid #eee;
28
 }
39
 }
40
+
29
 .comment-editor textarea.ant-input:focus {
41
 .comment-editor textarea.ant-input:focus {
30
-  box-shadow: none;
31
-  border-bottom: 1px solid #eee;
42
+    box-shadow: none;
43
+    border-bottom: 1px solid #eee;
32
 }
44
 }
45
+
33
 .comment-editor [contentEditable="true"]:empty:not(:focus):before {
46
 .comment-editor [contentEditable="true"]:empty:not(:focus):before {
34
-  content: attr(data-text);
35
-  color: #bfbfbf;
47
+    content: attr(data-text);
48
+    color: #bfbfbf;
36
 }
49
 }
50
+
37
 .comment-editor:focus,
51
 .comment-editor:focus,
38
 .comment-editor:hover {
52
 .comment-editor:hover {
39
-  border-color: #40a9ff;
40
-  outline: 0;
41
-  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
53
+    border-color: #40a9ff;
54
+    outline: 0;
55
+    box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
42
 }
56
 }
57
+
43
 .comment-toolbar {
58
 .comment-toolbar {
44
-  display: inline-block;
45
-  width: 100%;
46
-  margin: 5px 0 0 0;
59
+    display: inline-block;
60
+    width: 100%;
61
+    margin: 5px 0 0 0;
47
 }
62
 }
63
+
48
 .comment-toolbar .comment-toolbar-icon {
64
 .comment-toolbar .comment-toolbar-icon {
49
-  font-size: 23px;
50
-  cursor: pointer;
51
-  transition: color 0.3s;
65
+    font-size: 23px;
66
+    cursor: pointer;
67
+    transition: color 0.3s;
52
 }
68
 }
69
+
53
 .comment-toolbar .comment-toolbar-icon:hover {
70
 .comment-toolbar .comment-toolbar-icon:hover {
54
-  color: #40a9ff;
71
+    color: #40a9ff;
55
 }
72
 }
73
+
56
 .comment-toolbar-left {
74
 .comment-toolbar-left {
57
-  float: left;
58
-  margin: 8px 11px;
75
+    float: left;
76
+    margin: 8px 11px;
59
 }
77
 }
78
+
60
 .comment-toolbar-right {
79
 .comment-toolbar-right {
61
-  float: right;
62
-  margin: 5px 11px;
80
+    float: right;
81
+    margin: 5px 11px;
63
 }
82
 }
64
 
83
 
65
 .comment-emoji-popover .ant-popover-inner-content {
84
 .comment-emoji-popover .ant-popover-inner-content {
66
-  padding: 12px 16px 20px 16px;
85
+    padding: 12px 16px 20px 16px;
67
 }
86
 }
87
+
68
 .comment-emoji-popover .ant-carousel .slick-dots {
88
 .comment-emoji-popover .ant-carousel .slick-dots {
69
-  bottom: -10px;
89
+    bottom: -10px;
70
 }
90
 }
91
+
71
 .comment-emoji-popover .ant-carousel .slick-dots li.slick-active button {
92
 .comment-emoji-popover .ant-carousel .slick-dots li.slick-active button {
72
-  background-color: #7b868a;
93
+    background-color: #7b868a;
73
 }
94
 }
95
+
74
 .comment-emoji-popover .ant-carousel .slick-dots li button {
96
 .comment-emoji-popover .ant-carousel .slick-dots li button {
75
-  background-color: #a2aeb5;
76
-}
97
+    background-color: #a2aeb5;
98
+}

+ 147
- 112
lib/components/Editor/index.js View File

16
 
16
 
17
 var _icon2 = _interopRequireDefault(_icon);
17
 var _icon2 = _interopRequireDefault(_icon);
18
 
18
 
19
+var _message2 = require("antd/es/message");
20
+
21
+var _message3 = _interopRequireDefault(_message2);
22
+
19
 var _input = require("antd/es/input");
23
 var _input = require("antd/es/input");
20
 
24
 
21
 var _input2 = _interopRequireDefault(_input);
25
 var _input2 = _interopRequireDefault(_input);
28
 
32
 
29
 require("antd/es/icon/style/css");
33
 require("antd/es/icon/style/css");
30
 
34
 
35
+require("antd/es/message/style/css");
36
+
31
 require("antd/es/input/style/css");
37
 require("antd/es/input/style/css");
32
 
38
 
33
 var _react = require("react");
39
 var _react = require("react");
38
 
44
 
39
 var _propTypes2 = _interopRequireDefault(_propTypes);
45
 var _propTypes2 = _interopRequireDefault(_propTypes);
40
 
46
 
47
+var _classnames = require("classnames");
48
+
49
+var _classnames2 = _interopRequireDefault(_classnames);
50
+
41
 var _constant = require("../../constant");
51
 var _constant = require("../../constant");
42
 
52
 
43
 var _helper = require("../../helper");
53
 var _helper = require("../../helper");
187
     value: function handleSubmit() {
197
     value: function handleSubmit() {
188
       var _this2 = this;
198
       var _this2 = this;
189
 
199
 
200
+      var maxLength = this.props.maxLength;
190
       var _state = this.state,
201
       var _state = this.state,
191
           value = _state.value,
202
           value = _state.value,
192
           fileMap = _state.fileMap,
203
           fileMap = _state.fileMap,
193
           fileList = _state.fileList;
204
           fileList = _state.fileList;
194
 
205
 
206
+      if (value.length > maxLength) {
207
+        _message3.default.error("\u5B57\u6570\u4E0D\u5F97\u8D85\u8FC7" + maxLength + "\u5B57");
208
+        return;
209
+      }
195
       var files = [];
210
       var files = [];
196
       if (fileList.length) {
211
       if (fileList.length) {
197
         fileList.forEach(function (item) {
212
         fileList.forEach(function (item) {
249
           btnDisabled = _props.btnDisabled,
264
           btnDisabled = _props.btnDisabled,
250
           button = _props.button,
265
           button = _props.button,
251
           emojiToolIcon = _props.emojiToolIcon,
266
           emojiToolIcon = _props.emojiToolIcon,
252
-          imageToolIcon = _props.imageToolIcon;
267
+          imageToolIcon = _props.imageToolIcon,
268
+          maxLength = _props.maxLength;
253
 
269
 
254
       var handleSubmit = this.handleSubmit;
270
       var handleSubmit = this.handleSubmit;
255
       var disabledSubmit = btnDisabled || !this.props.value && !this.state.value && !this.state.fileList.length;
271
       var disabledSubmit = btnDisabled || !this.props.value && !this.state.value && !this.state.fileList.length;
272
+      var inputValue = value || this.state.value;
256
       return _react2.default.createElement(
273
       return _react2.default.createElement(
257
         "div",
274
         "div",
258
-        { className: "comment-editor" },
259
-        _react2.default.createElement(TextArea, {
260
-          value: value || this.state.value,
261
-          onChange: function onChange(e) {
262
-            return _this3.handleChange(e.target.value);
263
-          },
264
-          rows: rows,
265
-          placeholder: placeholder
266
-        }),
275
+        null,
276
+        _react2.default.createElement(
277
+          "div",
278
+          { className: (0, _classnames2.default)({
279
+              "comment-editor-toolbar": true,
280
+              "comment-editor-toolbar-error": inputValue.length > maxLength
281
+            }) },
282
+          "\u5DF2\u8F93\u5165 ",
283
+          inputValue.length,
284
+          " / ",
285
+          maxLength,
286
+          " \u5B57"
287
+        ),
267
         _react2.default.createElement(
288
         _react2.default.createElement(
268
           "div",
289
           "div",
269
-          { className: "comment-toolbar" },
290
+          { className: "comment-editor" },
291
+          _react2.default.createElement(TextArea, {
292
+            value: inputValue,
293
+            onChange: function onChange(e) {
294
+              return _this3.handleChange(e.target.value);
295
+            },
296
+            rows: rows,
297
+            placeholder: placeholder
298
+          }),
270
           _react2.default.createElement(
299
           _react2.default.createElement(
271
             "div",
300
             "div",
272
-            { className: "comment-toolbar-left" },
273
-            showEmoji && _react2.default.createElement(
274
-              _popover2.default,
275
-              {
276
-                trigger: "click",
277
-                placement: "bottomLeft",
278
-                autoAdjustOverflow: false,
279
-                content: _react2.default.createElement(
280
-                  "div",
281
-                  { style: { width: 200 } },
282
-                  _react2.default.createElement(_Emoji2.default, { onClick: this.handleClickEmoji })
283
-                ),
284
-                overlayClassName: "comment-emoji-popover"
285
-              },
286
-              emojiToolIcon || _react2.default.createElement(_icon2.default, { type: "smile-o", className: "comment-toolbar-icon" })
287
-            ),
288
-            showUpload ? _react2.default.createElement(
289
-              _popover2.default,
290
-              {
291
-                trigger: "click",
292
-                visible: this.state.showUpload,
293
-                overlayStyle: { zIndex: 999 },
294
-                onVisibleChange: closeUploadWhenBlur ? function (visible) {
295
-                  _this3.handleShowUpload(visible);
296
-                } : null,
297
-                content: _react2.default.createElement(
298
-                  "div",
299
-                  {
300
-                    style: {
301
-                      width: 112 * maxUpload,
302
-                      minHeight: 100,
303
-                      margin: "0 auto"
304
-                    }
305
-                  },
306
-                  _react2.default.createElement(_Upload2.default, {
307
-                    onChangeFileList: this.handleChangeFileList,
308
-                    onUpload: this.handleUpload,
309
-                    maxUpload: maxUpload,
310
-                    fileList: this.state.fileList,
311
-                    showError: this.props.showError,
312
-                    onError: this.props.onError
313
-                  })
314
-                ),
315
-                placement: "bottomLeft",
316
-                title: _react2.default.createElement(
317
-                  "div",
318
-                  { style: { margin: "5px auto" } },
319
-                  _react2.default.createElement(
320
-                    "span",
321
-                    null,
322
-                    "\u4E0A\u4F20\u56FE\u7247",
323
-                    maxUpload >= 2 ? _react2.default.createElement(
324
-                      "span",
325
-                      { style: { color: "#666", fontWeight: 400 } },
326
-                      "(\u60A8\u8FD8\u80FD\u4E0A\u4F20",
327
-                      maxUpload - this.state.fileList.length,
328
-                      "\u5F20\u56FE\u7247)"
329
-                    ) : null
301
+            { className: "comment-toolbar" },
302
+            _react2.default.createElement(
303
+              "div",
304
+              { className: "comment-toolbar-left" },
305
+              showEmoji && _react2.default.createElement(
306
+                _popover2.default,
307
+                {
308
+                  trigger: "click",
309
+                  placement: "bottomLeft",
310
+                  autoAdjustOverflow: false,
311
+                  content: _react2.default.createElement(
312
+                    "div",
313
+                    { style: { width: 200 } },
314
+                    _react2.default.createElement(_Emoji2.default, { onClick: this.handleClickEmoji })
330
                   ),
315
                   ),
331
-                  _react2.default.createElement(_icon2.default, {
332
-                    type: "close",
333
-                    onClick: function onClick() {
334
-                      return _this3.handleShowUpload(false);
316
+                  overlayClassName: "comment-emoji-popover"
317
+                },
318
+                emojiToolIcon || _react2.default.createElement(_icon2.default, { type: "smile-o", className: "comment-toolbar-icon" })
319
+              ),
320
+              showUpload ? _react2.default.createElement(
321
+                _popover2.default,
322
+                {
323
+                  trigger: "click",
324
+                  visible: this.state.showUpload,
325
+                  overlayStyle: { zIndex: 999 },
326
+                  onVisibleChange: closeUploadWhenBlur ? function (visible) {
327
+                    _this3.handleShowUpload(visible);
328
+                  } : null,
329
+                  content: _react2.default.createElement(
330
+                    "div",
331
+                    {
332
+                      style: {
333
+                        width: 112 * maxUpload,
334
+                        minHeight: 100,
335
+                        margin: "0 auto"
336
+                      }
335
                     },
337
                     },
336
-                    style: {
337
-                      float: "right",
338
-                      cursor: "pointer",
339
-                      marginTop: 4
340
-                    }
341
-                  })
342
-                )
343
-              },
344
-              imageToolIcon ? _react2.default.cloneElement(imageToolIcon, {
345
-                onClick: function onClick() {
346
-                  return _this3.handleShowUpload(true);
347
-                }
348
-              }) : _react2.default.createElement(_icon2.default, {
349
-                type: "picture",
350
-                className: "comment-toolbar-icon",
351
-                style: { marginLeft: 10 },
352
-                onClick: function onClick() {
353
-                  return _this3.handleShowUpload(true);
354
-                }
355
-              })
356
-            ) : null
357
-          ),
358
-          _react2.default.createElement(
359
-            "div",
360
-            { className: "comment-toolbar-right" },
361
-            button ? _react2.default.cloneElement(button, {
362
-              onClick: button.props.onClick || handleSubmit
363
-            }) : _react2.default.createElement(
364
-              _button2.default,
365
-              {
366
-                onClick: function onClick() {
367
-                  return _this3.handleSubmit();
338
+                    _react2.default.createElement(_Upload2.default, {
339
+                      onChangeFileList: this.handleChangeFileList,
340
+                      onUpload: this.handleUpload,
341
+                      maxUpload: maxUpload,
342
+                      fileList: this.state.fileList,
343
+                      showError: this.props.showError,
344
+                      onError: this.props.onError
345
+                    })
346
+                  ),
347
+                  placement: "bottomLeft",
348
+                  title: _react2.default.createElement(
349
+                    "div",
350
+                    { style: { margin: "5px auto" } },
351
+                    _react2.default.createElement(
352
+                      "span",
353
+                      null,
354
+                      "\u4E0A\u4F20\u56FE\u7247",
355
+                      maxUpload >= 2 ? _react2.default.createElement(
356
+                        "span",
357
+                        { style: { color: "#666", fontWeight: 400 } },
358
+                        "(\u60A8\u8FD8\u80FD\u4E0A\u4F20",
359
+                        maxUpload - this.state.fileList.length,
360
+                        "\u5F20\u56FE\u7247)"
361
+                      ) : null
362
+                    ),
363
+                    _react2.default.createElement(_icon2.default, {
364
+                      type: "close",
365
+                      onClick: function onClick() {
366
+                        return _this3.handleShowUpload(false);
367
+                      },
368
+                      style: {
369
+                        float: "right",
370
+                        cursor: "pointer",
371
+                        marginTop: 4
372
+                      }
373
+                    })
374
+                  )
375
+                },
376
+                imageToolIcon ? _react2.default.cloneElement(imageToolIcon, {
377
+                  onClick: function onClick() {
378
+                    return _this3.handleShowUpload(true);
379
+                  }
380
+                }) : _react2.default.createElement(_icon2.default, {
381
+                  type: "picture",
382
+                  className: "comment-toolbar-icon",
383
+                  style: { marginLeft: 10 },
384
+                  onClick: function onClick() {
385
+                    return _this3.handleShowUpload(true);
386
+                  }
387
+                })
388
+              ) : null
389
+            ),
390
+            _react2.default.createElement(
391
+              "div",
392
+              { className: "comment-toolbar-right" },
393
+              button ? _react2.default.cloneElement(button, {
394
+                onClick: button.props.onClick || handleSubmit
395
+              }) : _react2.default.createElement(
396
+                _button2.default,
397
+                {
398
+                  onClick: function onClick() {
399
+                    return _this3.handleSubmit();
400
+                  },
401
+                  type: "primary",
402
+                  loading: btnLoading,
403
+                  disabled: disabledSubmit
368
                 },
404
                 },
369
-                type: "primary",
370
-                loading: btnLoading,
371
-                disabled: disabledSubmit
372
-              },
373
-              btnSubmitText
405
+                btnSubmitText
406
+              )
374
             )
407
             )
375
           )
408
           )
376
         )
409
         )
399
   emojiToolIcon: _propTypes2.default.node,
432
   emojiToolIcon: _propTypes2.default.node,
400
   imageToolIcon: _propTypes2.default.node,
433
   imageToolIcon: _propTypes2.default.node,
401
   showError: _propTypes2.default.bool,
434
   showError: _propTypes2.default.bool,
402
-  onError: _propTypes2.default.func
435
+  onError: _propTypes2.default.func,
436
+  maxLength: _propTypes2.default.number
403
 };
437
 };
404
 
438
 
405
 Editor.defaultProps = {
439
 Editor.defaultProps = {
412
   btnSubmitText: "发表",
446
   btnSubmitText: "发表",
413
   btnLoading: false,
447
   btnLoading: false,
414
   btnDisabled: false,
448
   btnDisabled: false,
415
-  showError: true
449
+  showError: true,
450
+  maxLength: 140
416
 };
451
 };
417
 
452
 
418
 exports.default = Editor;
453
 exports.default = Editor;

+ 1
- 1
lib/components/Editor/index.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
lib/helper.js View File

76
     }
76
     }
77
     var value = emojiObejct[src] ? emojiObejct[src].value : src;
77
     var value = emojiObejct[src] ? emojiObejct[src].value : src;
78
     return "<img src=\"" + _emoji.prefixUrl + value + "." + _emoji.ext + "\" alt=\"" + value + "\" />";
78
     return "<img src=\"" + _emoji.prefixUrl + value + "." + _emoji.ext + "\" alt=\"" + value + "\" />";
79
-  });
79
+  }).replace(/\n/g, '<br />');
80
 }
80
 }
81
 //# sourceMappingURL=helper.js.map
81
 //# sourceMappingURL=helper.js.map

+ 1
- 1
lib/helper.js.map View File

1
-{"version":3,"sources":["../src/helper.js"],"names":["isFunction","isUrl","arrayToObject","htmlEncode","renderContent","emojiObejct","emoji","functionToCheck","toString","call","userInput","regexp","res","match","array","keyField","reduce","obj","item","str","replace","i","charCodeAt","content","onClick","newContent","indexOf","IMAGE_SPLIT","split","pop","join","REGEXP","a","b","src","slice","value","prefixUrl","ext"],"mappings":";;;;;QAKgBA,U,GAAAA,U;QAMAC,K,GAAAA,K;QAYAC,a,GAAAA,a;QAYAC,U,GAAAA,U;QAYAC,a,GAAAA,a;;AA/ChB;;AACA;;;;;;AAEA,IAAMC,cAAcH,cAAcI,eAAd,EAAqB,OAArB,CAApB;;AAEO,SAASN,UAAT,CAAoBO,eAApB,EAAqC;AAC1C,SACEA,mBAAmB,GAAGC,QAAH,CAAYC,IAAZ,CAAiBF,eAAjB,MAAsC,mBAD3D;AAGD;;AAEM,SAASN,KAAT,CAAeS,SAAf,EAA0B;AAC/B,MAAMC,SAAS,kGAAf;AACA,MAAIC,MAAMF,UAAUG,KAAV,CAAgBF,MAAhB,CAAV;AACA,MAAIC,QAAQ,IAAZ,EAAkB,OAAO,KAAP,CAAlB,KACK,OAAO,IAAP;AACN;;AAED;;;;;AAKO,SAASV,aAAT,CAAuBY,KAAvB,EAA8BC,QAA9B,EAAwC;AAC7C,SAAOD,MAAME,MAAN,CAAa,UAACC,GAAD,EAAMC,IAAN,EAAe;AACjCD,QAAIC,KAAKH,QAAL,CAAJ,IAAsBG,IAAtB;AACA,WAAOD,GAAP;AACD,GAHM,EAGJ,EAHI,CAAP;AAID;;AAED;;;;;AAKO,SAASd,UAAT,CAAoBgB,GAApB,EAAyB;AAC9B,MAAI,CAACA,GAAL,EAAU,OAAO,EAAP;AACV,SAAOA,IAAIC,OAAJ,CAAY,SAAZ,EAAuB,UAASC,CAAT,EAAY;AACxC,WAAO,OAAOA,EAAEC,UAAF,CAAa,CAAb,CAAP,GAAyB,GAAhC;AACD,GAFM,CAAP;AAGD;;AAED;;;;;AAKO,SAASlB,aAAT,CAAuBmB,OAAvB,EAAgCC,OAAhC,EAAyC;AAC9C,MAAIC,aAAaF,OAAjB;AACA,MAAIE,WAAWC,OAAX,CAAmBC,qBAAnB,MAAoC,CAAC,CAAzC,EAA4C;AAC1CF,iBAAaA,WAAWG,KAAX,CAAiBD,qBAAjB,CAAb;AACAF,eAAWI,GAAX;AACAJ,iBAAaA,WAAWK,IAAX,CAAgB,EAAhB,CAAb;AACD;;AAED,SAAO3B,WAAWsB,UAAX,EAAuBL,OAAvB,CAA+BW,gBAA/B,EAAuC,UAASC,CAAT,EAAYC,CAAZ,EAAe;AAC3D,QAAMC,MAAMF,EAAEG,KAAF,CAAQ,CAAR,EAAW,CAAC,CAAZ,CAAZ;;AAEA;AACA;AACA,QAAIlC,MAAMiC,GAAN,CAAJ,EAAgB;AACd,kCAAyBA,GAAzB,iBAAsCA,GAAtC;AACD;AACD,QAAME,QAAQ/B,YAAY6B,GAAZ,IAAmB7B,YAAY6B,GAAZ,EAAiBE,KAApC,GAA4CF,GAA1D;AACA,2BAAoBG,gBAApB,GAAgCD,KAAhC,SAAyCE,UAAzC,iBAAsDF,KAAtD;AACD,GAVM,CAAP;AAWD","file":"helper.js","sourcesContent":["import { REGEXP, IMAGE_SPLIT } from \"./constant\";\r\nimport emoji, { prefixUrl, ext } from \"./emoji\";\r\n\r\nconst emojiObejct = arrayToObject(emoji, \"title\");\r\n\r\nexport function isFunction(functionToCheck) {\r\n  return (\r\n    functionToCheck && {}.toString.call(functionToCheck) === \"[object Function]\"\r\n  );\r\n}\r\n\r\nexport function isUrl(userInput) {\r\n  const regexp = /(http(s)?:\\/\\/.)?(www\\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g;\r\n  var res = userInput.match(regexp);\r\n  if (res === null) return false;\r\n  else return true;\r\n}\r\n\r\n/**\r\n * 将对象数组转换为对象\r\n * @param {array} array Array of Objects\r\n * @param {string} keyField string\r\n */\r\nexport function arrayToObject(array, keyField) {\r\n  return array.reduce((obj, item) => {\r\n    obj[item[keyField]] = item;\r\n    return obj;\r\n  }, {});\r\n}\r\n\r\n/**\r\n * HTML 编码\r\n * 将 < > 等字符串进行编码\r\n * @param {string} str 文本\r\n */\r\nexport function htmlEncode(str) {\r\n  if (!str) return \"\";\r\n  return str.replace(/[<>]/gim, function(i) {\r\n    return \"&#\" + i.charCodeAt(0) + \";\";\r\n  });\r\n}\r\n\r\n/**\r\n * 渲染编辑器\r\n * [x] => <img src=\"x\" />\r\n * @param {strig} content\r\n */\r\nexport function renderContent(content, onClick) {\r\n  let newContent = content;\r\n  if (newContent.indexOf(IMAGE_SPLIT) !== -1) {\r\n    newContent = newContent.split(IMAGE_SPLIT);\r\n    newContent.pop();\r\n    newContent = newContent.join(\"\");\r\n  }\r\n\r\n  return htmlEncode(newContent).replace(REGEXP, function(a, b) {\r\n    const src = a.slice(1, -1);\r\n\r\n    // 兼容旧的评\r\n    // 因为旧的评论用 [img url] 方式存储的\r\n    if (isUrl(src)) {\r\n      return `<br/><img src=\"${src}\" alt=\"${src}\" style=\"max-width: 100%\" />`;\r\n    }\r\n    const value = emojiObejct[src] ? emojiObejct[src].value : src;\r\n    return `<img src=\"${prefixUrl}${value}.${ext}\" alt=\"${value}\" />`;\r\n  });\r\n}\r\n"]}
1
+{"version":3,"sources":["../src/helper.js"],"names":["isFunction","isUrl","arrayToObject","htmlEncode","renderContent","emojiObejct","emoji","functionToCheck","toString","call","userInput","regexp","res","match","array","keyField","reduce","obj","item","str","replace","i","charCodeAt","content","onClick","newContent","indexOf","IMAGE_SPLIT","split","pop","join","REGEXP","a","b","src","slice","value","prefixUrl","ext"],"mappings":";;;;;QAKgBA,U,GAAAA,U;QAMAC,K,GAAAA,K;QAYAC,a,GAAAA,a;QAYAC,U,GAAAA,U;QAYAC,a,GAAAA,a;;AA/ChB;;AACA;;;;;;AAEA,IAAMC,cAAcH,cAAcI,eAAd,EAAqB,OAArB,CAApB;;AAEO,SAASN,UAAT,CAAoBO,eAApB,EAAqC;AAC1C,SACEA,mBAAmB,GAAGC,QAAH,CAAYC,IAAZ,CAAiBF,eAAjB,MAAsC,mBAD3D;AAGD;;AAEM,SAASN,KAAT,CAAeS,SAAf,EAA0B;AAC/B,MAAMC,SAAS,kGAAf;AACA,MAAIC,MAAMF,UAAUG,KAAV,CAAgBF,MAAhB,CAAV;AACA,MAAIC,QAAQ,IAAZ,EAAkB,OAAO,KAAP,CAAlB,KACK,OAAO,IAAP;AACN;;AAED;;;;;AAKO,SAASV,aAAT,CAAuBY,KAAvB,EAA8BC,QAA9B,EAAwC;AAC7C,SAAOD,MAAME,MAAN,CAAa,UAACC,GAAD,EAAMC,IAAN,EAAe;AACjCD,QAAIC,KAAKH,QAAL,CAAJ,IAAsBG,IAAtB;AACA,WAAOD,GAAP;AACD,GAHM,EAGJ,EAHI,CAAP;AAID;;AAED;;;;;AAKO,SAASd,UAAT,CAAoBgB,GAApB,EAAyB;AAC9B,MAAI,CAACA,GAAL,EAAU,OAAO,EAAP;AACV,SAAOA,IAAIC,OAAJ,CAAY,SAAZ,EAAuB,UAAUC,CAAV,EAAa;AACzC,WAAO,OAAOA,EAAEC,UAAF,CAAa,CAAb,CAAP,GAAyB,GAAhC;AACD,GAFM,CAAP;AAGD;;AAED;;;;;AAKO,SAASlB,aAAT,CAAuBmB,OAAvB,EAAgCC,OAAhC,EAAyC;AAC9C,MAAIC,aAAaF,OAAjB;AACA,MAAIE,WAAWC,OAAX,CAAmBC,qBAAnB,MAAoC,CAAC,CAAzC,EAA4C;AAC1CF,iBAAaA,WAAWG,KAAX,CAAiBD,qBAAjB,CAAb;AACAF,eAAWI,GAAX;AACAJ,iBAAaA,WAAWK,IAAX,CAAgB,EAAhB,CAAb;AACD;;AAED,SAAO3B,WAAWsB,UAAX,EAAuBL,OAAvB,CAA+BW,gBAA/B,EAAuC,UAAUC,CAAV,EAAaC,CAAb,EAAgB;AAC5D,QAAMC,MAAMF,EAAEG,KAAF,CAAQ,CAAR,EAAW,CAAC,CAAZ,CAAZ;;AAEA;AACA;AACA,QAAIlC,MAAMiC,GAAN,CAAJ,EAAgB;AACd,kCAAyBA,GAAzB,iBAAsCA,GAAtC;AACD;AACD,QAAME,QAAQ/B,YAAY6B,GAAZ,IAAmB7B,YAAY6B,GAAZ,EAAiBE,KAApC,GAA4CF,GAA1D;AACA,2BAAoBG,gBAApB,GAAgCD,KAAhC,SAAyCE,UAAzC,iBAAsDF,KAAtD;AACD,GAVM,EAUJhB,OAVI,CAUI,KAVJ,EAUW,QAVX,CAAP;AAWD","file":"helper.js","sourcesContent":["import { REGEXP, IMAGE_SPLIT } from \"./constant\";\r\nimport emoji, { prefixUrl, ext } from \"./emoji\";\r\n\r\nconst emojiObejct = arrayToObject(emoji, \"title\");\r\n\r\nexport function isFunction(functionToCheck) {\r\n  return (\r\n    functionToCheck && {}.toString.call(functionToCheck) === \"[object Function]\"\r\n  );\r\n}\r\n\r\nexport function isUrl(userInput) {\r\n  const regexp = /(http(s)?:\\/\\/.)?(www\\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g;\r\n  var res = userInput.match(regexp);\r\n  if (res === null) return false;\r\n  else return true;\r\n}\r\n\r\n/**\r\n * 将对象数组转换为对象\r\n * @param {array} array Array of Objects\r\n * @param {string} keyField string\r\n */\r\nexport function arrayToObject(array, keyField) {\r\n  return array.reduce((obj, item) => {\r\n    obj[item[keyField]] = item;\r\n    return obj;\r\n  }, {});\r\n}\r\n\r\n/**\r\n * HTML 编码\r\n * 将 < > 等字符串进行编码\r\n * @param {string} str 文本\r\n */\r\nexport function htmlEncode(str) {\r\n  if (!str) return \"\";\r\n  return str.replace(/[<>]/gim, function (i) {\r\n    return \"&#\" + i.charCodeAt(0) + \";\";\r\n  });\r\n}\r\n\r\n/**\r\n * 渲染编辑器\r\n * [x] => <img src=\"x\" />\r\n * @param {strig} content\r\n */\r\nexport function renderContent(content, onClick) {\r\n  let newContent = content;\r\n  if (newContent.indexOf(IMAGE_SPLIT) !== -1) {\r\n    newContent = newContent.split(IMAGE_SPLIT);\r\n    newContent.pop();\r\n    newContent = newContent.join(\"\");\r\n  }\r\n\r\n  return htmlEncode(newContent).replace(REGEXP, function (a, b) {\r\n    const src = a.slice(1, -1);\r\n\r\n    // 兼容旧的评\r\n    // 因为旧的评论用 [img url] 方式存储的\r\n    if (isUrl(src)) {\r\n      return `<br/><img src=\"${src}\" alt=\"${src}\" style=\"max-width: 100%\" />`;\r\n    }\r\n    const value = emojiObejct[src] ? emojiObejct[src].value : src;\r\n    return `<img src=\"${prefixUrl}${value}.${ext}\" alt=\"${value}\" />`;\r\n  }).replace(/\\n/g, '<br />');\r\n}\r\n"]}

+ 1
- 1
lib/index.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
package.json View File

1
 {
1
 {
2
   "name": "comment",
2
   "name": "comment",
3
-  "version": "0.5.5",
3
+  "version": "0.5.6",
4
   "main": "lib/App.js",
4
   "main": "lib/App.js",
5
   "description": "通用评论",
5
   "description": "通用评论",
6
   "keywords": [
6
   "keywords": [

+ 22
- 0
src/components/Editor/index.css View File

1
+.comment-editor-toolbar {
2
+  text-align: right;
3
+  padding: 0 20px 10px 0;
4
+}
5
+
6
+.comment-editor-toolbar-error {
7
+  color: red;
8
+}
9
+
1
 .comment-editor {
10
 .comment-editor {
2
   box-sizing: border-box;
11
   box-sizing: border-box;
3
   margin: 0;
12
   margin: 0;
16
   border-radius: 4px;
25
   border-radius: 4px;
17
   transition: all 0.3s, height 0s;
26
   transition: all 0.3s, height 0s;
18
 }
27
 }
28
+
19
 .comment-editor textarea.ant-input {
29
 .comment-editor textarea.ant-input {
20
   border: none;
30
   border: none;
21
   border-bottom: 1px solid #eee;
31
   border-bottom: 1px solid #eee;
22
   border-bottom-right-radius: 0;
32
   border-bottom-right-radius: 0;
23
   border-bottom-left-radius: 0;
33
   border-bottom-left-radius: 0;
24
 }
34
 }
35
+
25
 .comment-editor textarea.ant-input:hover {
36
 .comment-editor textarea.ant-input:hover {
26
   border: none;
37
   border: none;
27
   border-bottom: 1px solid #eee;
38
   border-bottom: 1px solid #eee;
28
 }
39
 }
40
+
29
 .comment-editor textarea.ant-input:focus {
41
 .comment-editor textarea.ant-input:focus {
30
   box-shadow: none;
42
   box-shadow: none;
31
   border-bottom: 1px solid #eee;
43
   border-bottom: 1px solid #eee;
32
 }
44
 }
45
+
33
 .comment-editor [contentEditable="true"]:empty:not(:focus):before {
46
 .comment-editor [contentEditable="true"]:empty:not(:focus):before {
34
   content: attr(data-text);
47
   content: attr(data-text);
35
   color: #bfbfbf;
48
   color: #bfbfbf;
36
 }
49
 }
50
+
37
 .comment-editor:focus,
51
 .comment-editor:focus,
38
 .comment-editor:hover {
52
 .comment-editor:hover {
39
   border-color: #40a9ff;
53
   border-color: #40a9ff;
40
   outline: 0;
54
   outline: 0;
41
   box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
55
   box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
42
 }
56
 }
57
+
43
 .comment-toolbar {
58
 .comment-toolbar {
44
   display: inline-block;
59
   display: inline-block;
45
   width: 100%;
60
   width: 100%;
46
   margin: 5px 0 0 0;
61
   margin: 5px 0 0 0;
47
 }
62
 }
63
+
48
 .comment-toolbar .comment-toolbar-icon {
64
 .comment-toolbar .comment-toolbar-icon {
49
   font-size: 23px;
65
   font-size: 23px;
50
   cursor: pointer;
66
   cursor: pointer;
51
   transition: color 0.3s;
67
   transition: color 0.3s;
52
 }
68
 }
69
+
53
 .comment-toolbar .comment-toolbar-icon:hover {
70
 .comment-toolbar .comment-toolbar-icon:hover {
54
   color: #40a9ff;
71
   color: #40a9ff;
55
 }
72
 }
73
+
56
 .comment-toolbar-left {
74
 .comment-toolbar-left {
57
   float: left;
75
   float: left;
58
   margin: 8px 11px;
76
   margin: 8px 11px;
59
 }
77
 }
78
+
60
 .comment-toolbar-right {
79
 .comment-toolbar-right {
61
   float: right;
80
   float: right;
62
   margin: 5px 11px;
81
   margin: 5px 11px;
65
 .comment-emoji-popover .ant-popover-inner-content {
84
 .comment-emoji-popover .ant-popover-inner-content {
66
   padding: 12px 16px 20px 16px;
85
   padding: 12px 16px 20px 16px;
67
 }
86
 }
87
+
68
 .comment-emoji-popover .ant-carousel .slick-dots {
88
 .comment-emoji-popover .ant-carousel .slick-dots {
69
   bottom: -10px;
89
   bottom: -10px;
70
 }
90
 }
91
+
71
 .comment-emoji-popover .ant-carousel .slick-dots li.slick-active button {
92
 .comment-emoji-popover .ant-carousel .slick-dots li.slick-active button {
72
   background-color: #7b868a;
93
   background-color: #7b868a;
73
 }
94
 }
95
+
74
 .comment-emoji-popover .ant-carousel .slick-dots li button {
96
 .comment-emoji-popover .ant-carousel .slick-dots li button {
75
   background-color: #a2aeb5;
97
   background-color: #a2aeb5;
76
 }
98
 }

+ 130
- 110
src/components/Editor/index.js View File

1
 import React from "react";
1
 import React from "react";
2
 import PropTypes from "prop-types";
2
 import PropTypes from "prop-types";
3
-import { Icon, Button, Popover, Input } from "antd";
3
+import { Icon, Button, Popover, Input, message } from "antd";
4
+import classnames from "classnames";
4
 import { OSS_LINK } from "../../constant";
5
 import { OSS_LINK } from "../../constant";
5
 import { isFunction } from "../../helper";
6
 import { isFunction } from "../../helper";
6
 import Upload from "./Upload";
7
 import Upload from "./Upload";
102
    * 需要父组件传入 onSubmit
103
    * 需要父组件传入 onSubmit
103
    */
104
    */
104
   handleSubmit() {
105
   handleSubmit() {
106
+    const { maxLength } = this.props;
105
     let { value, fileMap, fileList } = this.state;
107
     let { value, fileMap, fileList } = this.state;
108
+    if (value.length > maxLength) {
109
+      message.error(`字数不得超过${maxLength}字`);
110
+      return;
111
+    }
106
     const files = [];
112
     const files = [];
107
     if (fileList.length) {
113
     if (fileList.length) {
108
       fileList.forEach(item => {
114
       fileList.forEach(item => {
158
       btnDisabled,
164
       btnDisabled,
159
       button,
165
       button,
160
       emojiToolIcon,
166
       emojiToolIcon,
161
-      imageToolIcon
167
+      imageToolIcon,
168
+      maxLength
162
     } = this.props;
169
     } = this.props;
163
     const handleSubmit = this.handleSubmit;
170
     const handleSubmit = this.handleSubmit;
164
     const disabledSubmit =
171
     const disabledSubmit =
165
       btnDisabled ||
172
       btnDisabled ||
166
       (!this.props.value && !this.state.value && !this.state.fileList.length);
173
       (!this.props.value && !this.state.value && !this.state.fileList.length);
174
+    const inputValue = value || this.state.value;
167
     return (
175
     return (
168
-      <div className="comment-editor">
169
-        <TextArea
170
-          value={value || this.state.value}
171
-          onChange={e => this.handleChange(e.target.value)}
172
-          rows={rows}
173
-          placeholder={placeholder}
174
-        />
176
+      <div>
177
+        <div
178
+          className={classnames({
179
+            "comment-editor-toolbar": true,
180
+            "comment-editor-toolbar-error": inputValue.length > maxLength
181
+          })}
182
+        >
183
+          已输入 {inputValue.length} / {maxLength} 字
184
+        </div>
185
+        <div className="comment-editor">
186
+          <TextArea
187
+            value={inputValue}
188
+            onChange={e => this.handleChange(e.target.value)}
189
+            rows={rows}
190
+            placeholder={placeholder}
191
+          />
175
 
192
 
176
-        <div className="comment-toolbar">
177
-          <div className="comment-toolbar-left">
178
-            {showEmoji && (
179
-              <Popover
180
-                trigger="click"
181
-                placement="bottomLeft"
182
-                autoAdjustOverflow={false}
183
-                content={
184
-                  <div style={{ width: 200 }}>
185
-                    <Emoji onClick={this.handleClickEmoji} />
186
-                  </div>
187
-                }
188
-                overlayClassName="comment-emoji-popover"
189
-              >
190
-                {emojiToolIcon || (
191
-                  <Icon type="smile-o" className="comment-toolbar-icon" />
192
-                )}
193
-              </Popover>
194
-            )}
193
+          <div className="comment-toolbar">
194
+            <div className="comment-toolbar-left">
195
+              {showEmoji && (
196
+                <Popover
197
+                  trigger="click"
198
+                  placement="bottomLeft"
199
+                  autoAdjustOverflow={false}
200
+                  content={
201
+                    <div style={{ width: 200 }}>
202
+                      <Emoji onClick={this.handleClickEmoji} />
203
+                    </div>
204
+                  }
205
+                  overlayClassName="comment-emoji-popover"
206
+                >
207
+                  {emojiToolIcon || (
208
+                    <Icon type="smile-o" className="comment-toolbar-icon" />
209
+                  )}
210
+                </Popover>
211
+              )}
195
 
212
 
196
-            {showUpload ? (
197
-              <Popover
198
-                trigger="click"
199
-                visible={this.state.showUpload}
200
-                overlayStyle={{ zIndex: 999 }}
201
-                onVisibleChange={
202
-                  closeUploadWhenBlur
203
-                    ? visible => {
204
-                        this.handleShowUpload(visible);
205
-                      }
206
-                    : null
207
-                }
208
-                content={
209
-                  <div
210
-                    style={{
211
-                      width: 112 * maxUpload,
212
-                      minHeight: 100,
213
-                      margin: "0 auto"
214
-                    }}
215
-                  >
216
-                    <Upload
217
-                      onChangeFileList={this.handleChangeFileList}
218
-                      onUpload={this.handleUpload}
219
-                      maxUpload={maxUpload}
220
-                      fileList={this.state.fileList}
221
-                      showError={this.props.showError}
222
-                      onError={this.props.onError}
223
-                    />
224
-                  </div>
225
-                }
226
-                placement="bottomLeft"
227
-                title={
228
-                  <div style={{ margin: "5px auto" }}>
229
-                    <span>
230
-                      上传图片
231
-                      {maxUpload >= 2 ? (
232
-                        <span style={{ color: "#666", fontWeight: 400 }}>
233
-                          (您还能上传{maxUpload - this.state.fileList.length}张图片)
234
-                        </span>
235
-                      ) : null}
236
-                    </span>
237
-                    <Icon
238
-                      type="close"
239
-                      onClick={() => this.handleShowUpload(false)}
213
+              {showUpload ? (
214
+                <Popover
215
+                  trigger="click"
216
+                  visible={this.state.showUpload}
217
+                  overlayStyle={{ zIndex: 999 }}
218
+                  onVisibleChange={
219
+                    closeUploadWhenBlur
220
+                      ? visible => {
221
+                          this.handleShowUpload(visible);
222
+                        }
223
+                      : null
224
+                  }
225
+                  content={
226
+                    <div
240
                       style={{
227
                       style={{
241
-                        float: "right",
242
-                        cursor: "pointer",
243
-                        marginTop: 4
228
+                        width: 112 * maxUpload,
229
+                        minHeight: 100,
230
+                        margin: "0 auto"
244
                       }}
231
                       }}
232
+                    >
233
+                      <Upload
234
+                        onChangeFileList={this.handleChangeFileList}
235
+                        onUpload={this.handleUpload}
236
+                        maxUpload={maxUpload}
237
+                        fileList={this.state.fileList}
238
+                        showError={this.props.showError}
239
+                        onError={this.props.onError}
240
+                      />
241
+                    </div>
242
+                  }
243
+                  placement="bottomLeft"
244
+                  title={
245
+                    <div style={{ margin: "5px auto" }}>
246
+                      <span>
247
+                        上传图片
248
+                        {maxUpload >= 2 ? (
249
+                          <span style={{ color: "#666", fontWeight: 400 }}>
250
+                            (您还能上传{maxUpload - this.state.fileList.length}张图片)
251
+                          </span>
252
+                        ) : null}
253
+                      </span>
254
+                      <Icon
255
+                        type="close"
256
+                        onClick={() => this.handleShowUpload(false)}
257
+                        style={{
258
+                          float: "right",
259
+                          cursor: "pointer",
260
+                          marginTop: 4
261
+                        }}
262
+                      />
263
+                    </div>
264
+                  }
265
+                >
266
+                  {imageToolIcon ? (
267
+                    React.cloneElement(imageToolIcon, {
268
+                      onClick: () => this.handleShowUpload(true)
269
+                    })
270
+                  ) : (
271
+                    <Icon
272
+                      type="picture"
273
+                      className="comment-toolbar-icon"
274
+                      style={{ marginLeft: 10 }}
275
+                      onClick={() => this.handleShowUpload(true)}
245
                     />
276
                     />
246
-                  </div>
247
-                }
248
-              >
249
-                {imageToolIcon ? (
250
-                  React.cloneElement(imageToolIcon, {
251
-                    onClick: () => this.handleShowUpload(true)
252
-                  })
253
-                ) : (
254
-                  <Icon
255
-                    type="picture"
256
-                    className="comment-toolbar-icon"
257
-                    style={{ marginLeft: 10 }}
258
-                    onClick={() => this.handleShowUpload(true)}
259
-                  />
260
-                )}
261
-              </Popover>
262
-            ) : null}
263
-          </div>
277
+                  )}
278
+                </Popover>
279
+              ) : null}
280
+            </div>
264
 
281
 
265
-          <div className="comment-toolbar-right">
266
-            {button ? (
267
-              React.cloneElement(button, {
268
-                onClick: button.props.onClick || handleSubmit
269
-              })
270
-            ) : (
271
-              <Button
272
-                onClick={() => this.handleSubmit()}
273
-                type="primary"
274
-                loading={btnLoading}
275
-                disabled={disabledSubmit}
276
-              >
277
-                {btnSubmitText}
278
-              </Button>
279
-            )}
282
+            <div className="comment-toolbar-right">
283
+              {button ? (
284
+                React.cloneElement(button, {
285
+                  onClick: button.props.onClick || handleSubmit
286
+                })
287
+              ) : (
288
+                <Button
289
+                  onClick={() => this.handleSubmit()}
290
+                  type="primary"
291
+                  loading={btnLoading}
292
+                  disabled={disabledSubmit}
293
+                >
294
+                  {btnSubmitText}
295
+                </Button>
296
+              )}
297
+            </div>
280
           </div>
298
           </div>
281
         </div>
299
         </div>
282
       </div>
300
       </div>
302
   emojiToolIcon: PropTypes.node,
320
   emojiToolIcon: PropTypes.node,
303
   imageToolIcon: PropTypes.node,
321
   imageToolIcon: PropTypes.node,
304
   showError: PropTypes.bool,
322
   showError: PropTypes.bool,
305
-  onError: PropTypes.func
323
+  onError: PropTypes.func,
324
+  maxLength: PropTypes.number
306
 };
325
 };
307
 
326
 
308
 Editor.defaultProps = {
327
 Editor.defaultProps = {
315
   btnSubmitText: "发表",
334
   btnSubmitText: "发表",
316
   btnLoading: false,
335
   btnLoading: false,
317
   btnDisabled: false,
336
   btnDisabled: false,
318
-  showError: true
337
+  showError: true,
338
+  maxLength: 140
319
 };
339
 };
320
 
340
 
321
 export default Editor;
341
 export default Editor;

+ 12
- 10
src/helper.js View File

53
     newContent = newContent.join("");
53
     newContent = newContent.join("");
54
   }
54
   }
55
 
55
 
56
-  return htmlEncode(newContent).replace(REGEXP, function(a, b) {
57
-    const src = a.slice(1, -1);
56
+  return htmlEncode(newContent)
57
+    .replace(REGEXP, function(a, b) {
58
+      const src = a.slice(1, -1);
58
 
59
 
59
-    // 兼容旧的评
60
-    // 因为旧的评论用 [img url] 方式存储的
61
-    if (isUrl(src)) {
62
-      return `<br/><img src="${src}" alt="${src}" style="max-width: 100%" />`;
63
-    }
64
-    const value = emojiObejct[src] ? emojiObejct[src].value : src;
65
-    return `<img src="${prefixUrl}${value}.${ext}" alt="${value}" />`;
66
-  });
60
+      // 兼容旧的评
61
+      // 因为旧的评论用 [img url] 方式存储的
62
+      if (isUrl(src)) {
63
+        return `<br/><img src="${src}" alt="${src}" style="max-width: 100%" />`;
64
+      }
65
+      const value = emojiObejct[src] ? emojiObejct[src].value : src;
66
+      return `<img src="${prefixUrl}${value}.${ext}" alt="${value}" />`;
67
+    })
68
+    .replace(/\n/g, "<br />");
67
 }
69
 }