20 Commits

Author SHA1 Message Date
  Roxas 3f2f41970b add default show upload props 3 years ago
  AdamFu a5c4a7f2dd Merge branch 'master' of allen6/comment into master 4 years ago
  Allen 6365ddacee fix: 修复跟读 loading 空白问题 4 years ago
  Allen 48149d4ee5 feat: AudioPlayer 添加 loading 过程 4 years ago
  AdamFu f79e8f9daf Merge branch 'develop' of Tzhx/comment into master 4 years ago
  adam dec1b6fd70 fix:修正跟读的双语 4 years ago
  adam 7a67067d6a fix:跟读播放的时候,暂停其他的播放中的音频 4 years ago
  Tzhx 7da2eb9b93 feat: 支持跟读 4 years ago
  AdamFu daff054613 Merge branch 'feature/img' of Evo/comment into master 4 years ago
  Evo eb03b3e300 yarn lib 4 years ago
  Evo a888ebe920 image upload兼容性修改:添加limitOne适配私信 4 years ago
  Evo 016964ff38 移动端只能上传一张图片;执行yarn lib 4 years ago
  Evo e963be4624 图片上传适配移动端: 调整popover size;调整title 4 years ago
  AdamFu 9a188d5039 Merge branch 'feature/enter' of Evo/comment into master 4 years ago
  Evo d125718223 Update: 执行yarn lib 4 years ago
  Evo 556bf1c6ca Update: Enter触发submit事件,不是订阅Enter事件 4 years ago
  AdamFu 78fe198b54 Merge branch 'feature/enter' of Evo/comment into master 4 years ago
  Evo 25f5e26125 Add: comments on handlePressEnter 4 years ago
  Evo 855671a6fc Add: Enter event; 4 years ago
  Roxas 38edbfc2c7 feature: 编辑器内部值支持预处理函数preRenderValue 4 years ago
47 changed files with 1075 additions and 201 deletions
  1. 1
    0
      README.md
  2. 13
    5
      lib/App.js
  3. 1
    1
      lib/App.js.map
  4. BIN
      lib/assert/btn_audio_play.png
  5. BIN
      lib/assert/loading.gif
  6. 226
    0
      lib/components/AudioPlayer/index.js
  7. 1
    0
      lib/components/AudioPlayer/index.js.map
  8. 65
    0
      lib/components/AudioPlayer/index.less
  9. 12
    0
      lib/components/CommentList/index.css
  10. 36
    5
      lib/components/CommentList/index.js
  11. 1
    1
      lib/components/CommentList/index.js.map
  12. 27
    0
      lib/components/ContentItem/index.css
  13. 23
    1
      lib/components/ContentItem/index.js
  14. 1
    1
      lib/components/ContentItem/index.js.map
  15. 1
    1
      lib/components/EditComment/EditComment.js
  16. 1
    1
      lib/components/EditComment/EditComment.js.map
  17. 2
    2
      lib/components/Editor/Upload.js
  18. 1
    1
      lib/components/Editor/Upload.js.map
  19. 25
    0
      lib/components/Editor/index.css
  20. 106
    59
      lib/components/Editor/index.js
  21. 1
    1
      lib/components/Editor/index.js.map
  22. 10
    8
      lib/index.js
  23. 1
    1
      lib/index.js.map
  24. 2
    0
      lib/lang/en-US.js
  25. 1
    1
      lib/lang/en-US.js.map
  26. 2
    0
      lib/lang/zh-CN.js
  27. 1
    1
      lib/lang/zh-CN.js.map
  28. 10
    0
      lib/utils.js
  29. 1
    0
      lib/utils.js.map
  30. 4
    4
      lib/version.json
  31. 1
    1
      package.json
  32. 8
    4
      src/App.js
  33. BIN
      src/assert/btn_audio_play.png
  34. BIN
      src/assert/loading.gif
  35. 144
    0
      src/components/AudioPlayer/index.js
  36. 65
    0
      src/components/AudioPlayer/index.less
  37. 12
    0
      src/components/CommentList/index.css
  38. 35
    6
      src/components/CommentList/index.js
  39. 27
    0
      src/components/ContentItem/index.css
  40. 78
    59
      src/components/ContentItem/index.js
  41. 4
    1
      src/components/EditComment/EditComment.js
  42. 25
    0
      src/components/Editor/index.css
  43. 82
    29
      src/components/Editor/index.js
  44. 9
    7
      src/index.js
  45. 2
    0
      src/lang/en-US.js
  46. 2
    0
      src/lang/zh-CN.js
  47. 5
    0
      src/utils.js

+ 1
- 0
README.md View File

372
 - [ ] 头像 404 `https://links123-images.oss-cn-hangzhou.aliyuncs.com/avatar/`
372
 - [ ] 头像 404 `https://links123-images.oss-cn-hangzhou.aliyuncs.com/avatar/`
373
 - [ ] 上传图片的时候偶尔会出现InvalidPart
373
 - [ ] 上传图片的时候偶尔会出现InvalidPart
374
 - [ ] 上传图片失败时, 提示并且不显示缩略图
374
 - [ ] 上传图片失败时, 提示并且不显示缩略图
375
+- [x] 支持Ente事件(PC端和手机键盘Enter)

+ 13
- 5
lib/App.js View File

278
 
278
 
279
       var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
279
       var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
280
           _ref2$page = _ref2.page,
280
           _ref2$page = _ref2.page,
281
-          page = _ref2$page === undefined ? 1 : _ref2$page;
281
+          page = _ref2$page === undefined ? 1 : _ref2$page,
282
+          _ref2$filterSpeak = _ref2.filterSpeak,
283
+          filterSpeak = _ref2$filterSpeak === undefined ? 0 : _ref2$filterSpeak;
282
 
284
 
283
       var pageType = this.props.pageType;
285
       var pageType = this.props.pageType;
284
 
286
 
289
           businessId = _props.businessId,
291
           businessId = _props.businessId,
290
           limit = _props.limit;
292
           limit = _props.limit;
291
 
293
 
292
-      this.axios.get(API + "/comments?type=" + type + "&business_id=" + businessId + "&page=" + page + "&limit=" + limit).then(function (response) {
294
+      this.axios.get(API + "/comments?type=" + type + "&business_id=" + businessId + "&is_speak=" + filterSpeak + "&page=" + page + "&limit=" + limit).then(function (response) {
293
         var _response$data = response.data,
295
         var _response$data = response.data,
294
             list = _response$data.list,
296
             list = _response$data.list,
295
             page = _response$data.page,
297
             page = _response$data.page,
745
         sUpdateComment: this.sUpdateComment,
747
         sUpdateComment: this.sUpdateComment,
746
         handleEdit: this.handleEdit
748
         handleEdit: this.handleEdit
747
       });
749
       });
750
+
748
       return this.state.initDone && _react2.default.createElement(
751
       return this.state.initDone && _react2.default.createElement(
749
         _Comment.CommentContext.Provider,
752
         _Comment.CommentContext.Provider,
750
         { value: value },
753
         { value: value },
766
           commentId: this.state.commentId,
769
           commentId: this.state.commentId,
767
           userId: this.state.content.user_id,
770
           userId: this.state.content.user_id,
768
           content: this.state.content,
771
           content: this.state.content,
769
-          handleClose: this.handleClose
772
+          handleClose: this.handleClose,
773
+          preRenderValue: this.props.preRenderValue
770
         })
774
         })
771
       );
775
       );
772
     }
776
     }
797
   onUpdateComment: _propTypes2.default.func,
801
   onUpdateComment: _propTypes2.default.func,
798
   locales: _propTypes2.default.string, //  传入的语言环境, en-US/zh-CN
802
   locales: _propTypes2.default.string, //  传入的语言环境, en-US/zh-CN
799
   onCountChange: _propTypes2.default.func, // 评论数量变更时的回调
803
   onCountChange: _propTypes2.default.func, // 评论数量变更时的回调
800
-  onCommentFail: _propTypes2.default.func // 评论失败时的回调
804
+  onCommentFail: _propTypes2.default.func, // 评论失败时的回调
805
+  preRenderValue: _propTypes2.default.func // 编辑器渲染前对值需要做的工作
801
 };
806
 };
802
 
807
 
803
 App.defaultProps = {
808
 App.defaultProps = {
820
   onUpdateComment: function onUpdateComment() {},
825
   onUpdateComment: function onUpdateComment() {},
821
   onBeforeUpdateComment: function onBeforeUpdateComment() {},
826
   onBeforeUpdateComment: function onBeforeUpdateComment() {},
822
   onCountChange: function onCountChange() {},
827
   onCountChange: function onCountChange() {},
823
-  onCommentFail: function onCommentFail() {}
828
+  onCommentFail: function onCommentFail() {},
829
+  preRenderValue: function preRenderValue(v) {
830
+    return v;
831
+  }
824
 };
832
 };
825
 
833
 
826
 exports.Editor = _Editor2.default;
834
 exports.Editor = _Editor2.default;

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


BIN
lib/assert/btn_audio_play.png View File


BIN
lib/assert/loading.gif View File


+ 226
- 0
lib/components/AudioPlayer/index.js View File

1
+"use strict";
2
+
3
+Object.defineProperty(exports, "__esModule", {
4
+  value: true
5
+});
6
+
7
+var _slider = require("antd/es/slider");
8
+
9
+var _slider2 = _interopRequireDefault(_slider);
10
+
11
+var _icon = require("antd/es/icon");
12
+
13
+var _icon2 = _interopRequireDefault(_icon);
14
+
15
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
16
+
17
+require("antd/es/slider/style");
18
+
19
+require("antd/es/icon/style");
20
+
21
+var _react = require("react");
22
+
23
+var _react2 = _interopRequireDefault(_react);
24
+
25
+var _dayjs = require("dayjs");
26
+
27
+var _dayjs2 = _interopRequireDefault(_dayjs);
28
+
29
+var _duration = require("dayjs/plugin/duration");
30
+
31
+var _duration2 = _interopRequireDefault(_duration);
32
+
33
+var _utc = require("dayjs/plugin/utc");
34
+
35
+var _utc2 = _interopRequireDefault(_utc);
36
+
37
+require("./index.less");
38
+
39
+var _loading = require("../../assert/loading.gif");
40
+
41
+var _loading2 = _interopRequireDefault(_loading);
42
+
43
+var _btn_audio_play = require("../../assert/btn_audio_play.png");
44
+
45
+var _btn_audio_play2 = _interopRequireDefault(_btn_audio_play);
46
+
47
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
48
+
49
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
50
+
51
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
52
+
53
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
54
+
55
+_dayjs2.default.extend(_duration2.default);
56
+_dayjs2.default.extend(_utc2.default);
57
+
58
+var AudioPlayer = function (_React$Component) {
59
+  _inherits(AudioPlayer, _React$Component);
60
+
61
+  function AudioPlayer(props) {
62
+    _classCallCheck(this, AudioPlayer);
63
+
64
+    var _this = _possibleConstructorReturn(this, (AudioPlayer.__proto__ || Object.getPrototypeOf(AudioPlayer)).call(this, props));
65
+
66
+    _this.state = {
67
+      duration: 0,
68
+      currentDuration: 0,
69
+      isPlaying: false,
70
+      isLoading: false
71
+    };
72
+    return _this;
73
+  }
74
+
75
+  _createClass(AudioPlayer, [{
76
+    key: "componentDidMount",
77
+    value: function componentDidMount() {
78
+      var _this2 = this;
79
+
80
+      if (this.player) {
81
+        var src = this.props.src;
82
+
83
+        this.player.oncanplay = function (event) {
84
+          return _this2.setState({ duration: event.target.duration });
85
+        };
86
+        this.player.onended = function () {
87
+          return _this2.setState({ isPlaying: false, currentDuration: 0 });
88
+        };
89
+        this.player.onpause = function () {
90
+          return _this2.setState({ isPlaying: false, isLoading: false });
91
+        };
92
+        this.player.onplay = function () {
93
+          return _this2.setState({ isPlaying: false, isLoading: true });
94
+        };
95
+        this.player.onplaying = function () {
96
+          return _this2.setState({ isPlaying: true, isLoading: false });
97
+        };
98
+        this.player.ontimeupdate = function (event) {
99
+          return _this2.setState({ currentDuration: event.target.currentTime });
100
+        };
101
+        this.player.src = src;
102
+      }
103
+    }
104
+  }, {
105
+    key: "componentDidUpdate",
106
+    value: function componentDidUpdate(prevProps) {
107
+      var _this3 = this;
108
+
109
+      if (this.props.src !== prevProps.src) {
110
+        this.player.oncanplay = function (event) {
111
+          return _this3.setState({ duration: event.target.duration });
112
+        };
113
+        this.player.onended = function () {
114
+          return _this3.setState({ isPlaying: false, currentDuration: 0 });
115
+        };
116
+        this.player.onpause = function () {
117
+          return _this3.setState({ isPlaying: false, isLoading: false });
118
+        };
119
+        this.player.onplay = function () {
120
+          return _this3.setState({ isPlaying: false, isLoading: true });
121
+        };
122
+        this.player.onplaying = function () {
123
+          return _this3.setState({ isPlaying: true, isLoading: false });
124
+        };
125
+        this.player.ontimeupdate = function (event) {
126
+          return _this3.setState({ currentDuration: event.target.currentTime });
127
+        };
128
+        this.player.src = this.props.src;
129
+      }
130
+    }
131
+  }, {
132
+    key: "clickPlayOrPause",
133
+    value: function clickPlayOrPause(isPlaying) {
134
+      if (!this.player) {
135
+        return;
136
+      }
137
+      if (isPlaying) {
138
+        this.player.pause();
139
+      } else {
140
+        // 如果是播放,先暂停其他的播放
141
+        var playerList = document.getElementsByTagName("audio");
142
+        if (playerList) {
143
+          Array.from(playerList).forEach(function (player) {
144
+            if (!player.paused) {
145
+              player.pause();
146
+            }
147
+          });
148
+        }
149
+        this.player.play();
150
+      }
151
+    }
152
+  }, {
153
+    key: "getPlayIcon",
154
+    value: function getPlayIcon() {
155
+      var _state = this.state,
156
+          isPlaying = _state.isPlaying,
157
+          isLoading = _state.isLoading;
158
+
159
+      var playIconElem = void 0;
160
+
161
+      if (isLoading) {
162
+        playIconElem = _react2.default.createElement("img", { className: "icon-loading", src: _loading2.default, alt: "iconLoading" });
163
+      } else if (isPlaying) {
164
+        playIconElem = _react2.default.createElement(_icon2.default, { className: "pause", type: "pause" });
165
+      } else {
166
+        playIconElem = _react2.default.createElement("img", { className: "icon-loading", src: _btn_audio_play2.default, alt: "iconPlaying" });
167
+      }
168
+      return playIconElem;
169
+    }
170
+  }, {
171
+    key: "render",
172
+    value: function render() {
173
+      var _this4 = this;
174
+
175
+      var _state2 = this.state,
176
+          currentDuration = _state2.currentDuration,
177
+          isPlaying = _state2.isPlaying,
178
+          duration = _state2.duration;
179
+
180
+      return _react2.default.createElement(
181
+        "div",
182
+        { className: "comment-item-speak-audio-container" },
183
+        _react2.default.createElement("audio", {
184
+          ref: function ref(_ref) {
185
+            _this4.player = _ref;
186
+          },
187
+          style: { display: "none" }
188
+        }),
189
+        _react2.default.createElement(
190
+          "span",
191
+          {
192
+            className: "icon",
193
+            onClick: function onClick() {
194
+              _this4.clickPlayOrPause(isPlaying);
195
+            }
196
+          },
197
+          this.getPlayIcon()
198
+        ),
199
+        _react2.default.createElement(_slider2.default, {
200
+          step: 0.001,
201
+          className: "slider",
202
+          tooltipVisible: false,
203
+          value: currentDuration,
204
+          max: duration,
205
+          onChange: function onChange(value) {
206
+            if (_this4.player) {
207
+              _this4.player.currentTime = value;
208
+            }
209
+          }
210
+        }),
211
+        _react2.default.createElement(
212
+          "span",
213
+          { className: "time" },
214
+          _dayjs2.default.utc(_dayjs2.default.duration(currentDuration, "seconds").asMilliseconds()).format("mm:ss"),
215
+          "/",
216
+          _dayjs2.default.utc(_dayjs2.default.duration(duration, "seconds").asMilliseconds()).format("mm:ss")
217
+        )
218
+      );
219
+    }
220
+  }]);
221
+
222
+  return AudioPlayer;
223
+}(_react2.default.Component);
224
+
225
+exports.default = AudioPlayer;
226
+//# sourceMappingURL=index.js.map

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


+ 65
- 0
lib/components/AudioPlayer/index.less View File

1
+.comment-item-speak-audio-container {
2
+  background-color: #f5f5f5;
3
+  border: 1px solid rgba(210, 210, 210, 1);
4
+  border-radius: 4px;
5
+  display: flex;
6
+  align-items: center;
7
+  height: 100%;
8
+
9
+  .icon {
10
+    cursor: pointer;
11
+    width: 28px;
12
+    height: 28px;
13
+    line-height: 28px;
14
+    text-align: center;
15
+    border-radius: 14px;
16
+    background-color: #fff;
17
+    color: #71c135;
18
+    margin-left: 12px;
19
+    font-size: 14px;
20
+  }
21
+
22
+  .icon-loading {
23
+    width: 100%;
24
+    height: 100%;
25
+  }
26
+
27
+  .slider {
28
+    margin: 0 0 0 16px;
29
+    width: 195px;
30
+
31
+    :global {
32
+      .ant-slider-rail {
33
+        background-color: #dfdfdf;
34
+      }
35
+    }
36
+  }
37
+
38
+  .time {
39
+    margin-left: 14px;
40
+    color: #848484;
41
+    font-size: 12px;
42
+  }
43
+}
44
+
45
+@media screen and (max-width: 616px) and (min-width: 449px) {
46
+  .comment-item-speak-audio-container {
47
+    .slider {
48
+      width: 195px;
49
+    }
50
+  }
51
+}
52
+@media screen and (max-width: 449px) and (min-width: 365px) {
53
+  .comment-item-speak-audio-container {
54
+    .slider {
55
+      width: 114px;
56
+    }
57
+  }
58
+}
59
+@media screen and (max-width: 365px) {
60
+  .comment-item-speak-audio-container {
61
+    .slider {
62
+      width: 60px;
63
+    }
64
+  }
65
+}

+ 12
- 0
lib/components/CommentList/index.css View File

17
 .comment-list-pagination {
17
 .comment-list-pagination {
18
   text-align: center;
18
   text-align: center;
19
 }
19
 }
20
+
21
+.comment-list-filter-speak {
22
+  margin-left: 20px;
23
+  font-size: 14px;
24
+  color: rgba(93, 93, 93, 0.65);
25
+}
26
+
27
+@media screen and (max-width: 365px) {
28
+  .comment-list-filter-speak {
29
+    float: right;
30
+  }
31
+}

+ 36
- 5
lib/components/CommentList/index.js View File

8
 
8
 
9
 var _spin2 = _interopRequireDefault(_spin);
9
 var _spin2 = _interopRequireDefault(_spin);
10
 
10
 
11
+var _checkbox = require("antd/es/checkbox");
12
+
13
+var _checkbox2 = _interopRequireDefault(_checkbox);
14
+
11
 var _pagination = require("antd/es/pagination");
15
 var _pagination = require("antd/es/pagination");
12
 
16
 
13
 var _pagination2 = _interopRequireDefault(_pagination);
17
 var _pagination2 = _interopRequireDefault(_pagination);
16
 
20
 
17
 require("antd/es/spin/style");
21
 require("antd/es/spin/style");
18
 
22
 
23
+require("antd/es/checkbox/style");
24
+
19
 require("antd/es/pagination/style");
25
 require("antd/es/pagination/style");
20
 
26
 
21
 var _react = require("react");
27
 var _react = require("react");
52
 
58
 
53
     var _this = _possibleConstructorReturn(this, (CommentList.__proto__ || Object.getPrototypeOf(CommentList)).call(this, props));
59
     var _this = _possibleConstructorReturn(this, (CommentList.__proto__ || Object.getPrototypeOf(CommentList)).call(this, props));
54
 
60
 
55
-    _this.state = {};
61
+    _this.state = {
62
+      filterSpeak: 0
63
+    };
56
     return _this;
64
     return _this;
57
   }
65
   }
58
 
66
 
74
           sGetComment = _props$app.sGetComment,
82
           sGetComment = _props$app.sGetComment,
75
           onPageChange = _props$app.onPageChange,
83
           onPageChange = _props$app.onPageChange,
76
           onGetMoreBtnClick = _props$app.onGetMoreBtnClick;
84
           onGetMoreBtnClick = _props$app.onGetMoreBtnClick;
85
+      var filterSpeak = this.state.filterSpeak;
77
 
86
 
78
       if (pageType === "slice") {
87
       if (pageType === "slice") {
79
         // 截断多余评论,通过点击查看更多跳转
88
         // 截断多余评论,通过点击查看更多跳转
93
             {
102
             {
94
               className: "comment-list-show-more",
103
               className: "comment-list-show-more",
95
               onClick: function onClick() {
104
               onClick: function onClick() {
96
-                sGetComment({ page: page + 1 });
105
+                sGetComment({ page: page + 1, filterSpeak: filterSpeak });
97
                 onPageChange(page + 1);
106
                 onPageChange(page + 1);
98
               }
107
               }
99
             },
108
             },
115
             current: page,
124
             current: page,
116
             total: total,
125
             total: total,
117
             onChange: function onChange(p) {
126
             onChange: function onChange(p) {
118
-              sGetComment({ page: p });
127
+              sGetComment({ page: p, filterSpeak: filterSpeak });
119
               onPageChange(p);
128
               onPageChange(p);
120
             }
129
             }
121
           })
130
           })
125
   }, {
134
   }, {
126
     key: "render",
135
     key: "render",
127
     value: function render() {
136
     value: function render() {
137
+      var _this2 = this;
138
+
128
       var _props$app2 = this.props.app,
139
       var _props$app2 = this.props.app,
129
           list = _props$app2.list,
140
           list = _props$app2.list,
130
           total = _props$app2.total,
141
           total = _props$app2.total,
131
-          loading = _props$app2.loading;
142
+          loading = _props$app2.loading,
143
+          isSpeak = _props$app2.isSpeak,
144
+          sGetComment = _props$app2.sGetComment,
145
+          onPageChange = _props$app2.onPageChange;
132
 
146
 
133
 
147
 
134
       var spinning = Boolean(loading.sGetComment || loading.sCommentFavor || loading.sReplyFavor);
148
       var spinning = Boolean(loading.sGetComment || loading.sCommentFavor || loading.sReplyFavor);
141
           _react2.default.createElement(
155
           _react2.default.createElement(
142
             "div",
156
             "div",
143
             null,
157
             null,
144
-            _reactIntlUniversal2.default.get("comment.totalComment", { total: total })
158
+            _reactIntlUniversal2.default.get("comment.totalComment", { total: total }),
159
+            isSpeak && _react2.default.createElement(
160
+              _checkbox2.default,
161
+              {
162
+                className: "comment-list-filter-speak",
163
+                onChange: function onChange(e) {
164
+                  _this2.setState({
165
+                    filterSpeak: e.target.checked ? 1 : 0
166
+                  });
167
+                  sGetComment({
168
+                    page: 1,
169
+                    filterSpeak: e.target.checked ? 1 : 0
170
+                  });
171
+                  onPageChange(1);
172
+                }
173
+              },
174
+              _reactIntlUniversal2.default.get("comment.filterSpeak")
175
+            )
145
           ),
176
           ),
146
           list.map(function (item) {
177
           list.map(function (item) {
147
             return _react2.default.createElement(_CommentBox2.default, {
178
             return _react2.default.createElement(_CommentBox2.default, {

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


+ 27
- 0
lib/components/ContentItem/index.css View File

33
   word-break: break-all;
33
   word-break: break-all;
34
 }
34
 }
35
 
35
 
36
+.comment-item-speak {
37
+  margin-top: 4px;
38
+}
39
+
40
+.comment-item-speak-message {
41
+  font-size: 14px;
42
+  color: #71c135;
43
+}
44
+
45
+.comment-item-speak-audio-wrapper {
46
+  margin-top: 8px;
47
+  width: 350px;
48
+  height: 44px;
49
+}
50
+
36
 .comment-item-bottom {
51
 .comment-item-bottom {
37
   display: flex;
52
   display: flex;
38
   justify-content: flex-end;
53
   justify-content: flex-end;
156
     width: 85%;
171
     width: 85%;
157
     margin-left: 10px;
172
     margin-left: 10px;
158
   }
173
   }
174
+
175
+  .comment-item-speak-audio-wrapper {
176
+    width: 350px;
177
+  }
159
 }
178
 }
160
 
179
 
161
 @media screen and (max-width: 449px) and (min-width: 365px) {
180
 @media screen and (max-width: 449px) and (min-width: 365px) {
164
     width: 80%;
183
     width: 80%;
165
     margin-left: 10px;
184
     margin-left: 10px;
166
   }
185
   }
186
+
187
+  .comment-item-speak-audio-wrapper {
188
+    width: 266px;
189
+  }
167
 }
190
 }
168
 
191
 
169
 @media screen and (max-width: 365px) {
192
 @media screen and (max-width: 365px) {
172
     width: 75%;
195
     width: 75%;
173
     margin-left: 10px;
196
     margin-left: 10px;
174
   }
197
   }
198
+
199
+  .comment-item-speak-audio-wrapper {
200
+    width: 218px;
201
+  }
175
 }
202
 }
176
 
203
 
177
 @media (max-width: 575px) {
204
 @media (max-width: 575px) {

+ 23
- 1
lib/components/ContentItem/index.js View File

80
 
80
 
81
 var _ImagePreviewer2 = _interopRequireDefault(_ImagePreviewer);
81
 var _ImagePreviewer2 = _interopRequireDefault(_ImagePreviewer);
82
 
82
 
83
+var _AudioPlayer = require("../AudioPlayer");
84
+
85
+var _AudioPlayer2 = _interopRequireDefault(_AudioPlayer);
86
+
83
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
87
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
84
 
88
 
85
 function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
89
 function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
197
           app = _props.app,
201
           app = _props.app,
198
           user_id = _props.user_id,
202
           user_id = _props.user_id,
199
           page = _props.page;
203
           page = _props.page;
204
+      var medias = content.medias,
205
+          isSpeak = content.is_speak;
200
       var _props$app = this.props.app,
206
       var _props$app = this.props.app,
201
           locale = _props$app.locale,
207
           locale = _props$app.locale,
202
           showHoverCard = _props$app.showHoverCard,
208
           showHoverCard = _props$app.showHoverCard,
295
               __html: (0, _helper.renderContent)(this.renderTextWithReply(newContent, content))
301
               __html: (0, _helper.renderContent)(this.renderTextWithReply(newContent, content))
296
             }
302
             }
297
           }),
303
           }),
304
+          isSpeak && _react2.default.createElement(
305
+            "div",
306
+            { className: "comment-item-speak" },
307
+            _react2.default.createElement(
308
+              "span",
309
+              { className: "comment-item-speak-message" },
310
+              "[",
311
+              _reactIntlUniversal2.default.get("comment.speakComment"),
312
+              "]"
313
+            ),
314
+            _react2.default.createElement(
315
+              "div",
316
+              { className: "comment-item-speak-audio-wrapper" },
317
+              _react2.default.createElement(_AudioPlayer2.default, { src: medias && medias[0] && medias[0].url })
318
+            )
319
+          ),
298
           // image为空时不渲染comment-item-image
320
           // image为空时不渲染comment-item-image
299
           imageList.length > 0 && imageList[0] !== "" && _react2.default.createElement(
321
           imageList.length > 0 && imageList[0] !== "" && _react2.default.createElement(
300
             "div",
322
             "div",
351
                 showReply ? _react2.default.createElement(_icon2.default, { type: "up" }) : _react2.default.createElement(_icon2.default, { type: "down" })
373
                 showReply ? _react2.default.createElement(_icon2.default, { type: "up" }) : _react2.default.createElement(_icon2.default, { type: "down" })
352
               )
374
               )
353
             ) : null,
375
             ) : null,
354
-            showEdit && app.userId === content.user_id && _react2.default.createElement("i", {
376
+            showEdit && !isSpeak && app.userId === content.user_id && _react2.default.createElement("i", {
355
               className: "comment-item-edit",
377
               className: "comment-item-edit",
356
               onClick: function onClick() {
378
               onClick: function onClick() {
357
                 return _this2.props.app.handleEdit({
379
                 return _this2.props.app.handleEdit({

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


+ 1
- 1
lib/components/EditComment/EditComment.js View File

193
             commentId: this.props.commentId,
193
             commentId: this.props.commentId,
194
             userId: this.props.content.user_id,
194
             userId: this.props.content.user_id,
195
             fileList: this.state.fileList,
195
             fileList: this.state.fileList,
196
-            value: this.state.value,
196
+            value: this.props.preRenderValue && this.props.preRenderValue(this.state.value),
197
             onChange: function onChange(value) {
197
             onChange: function onChange(value) {
198
               _this2.setState({
198
               _this2.setState({
199
                 value: value
199
                 value: value

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


+ 2
- 2
lib/components/Editor/Upload.js View File

232
             onClick: _this3.handleCloseClick.bind(_this3, index),
232
             onClick: _this3.handleCloseClick.bind(_this3, index),
233
             key: file.uid,
233
             key: file.uid,
234
             style: {
234
             style: {
235
-              left: index % 3 * 112 + 110 + "px",
236
-              top: Math.floor(index / 3) * 112 + 26 + "px"
235
+              left: index % 3 * 112 + 98 + "px",
236
+              top: Math.floor(index / 3) * 112 + 16 + "px"
237
             }
237
             }
238
           });
238
           });
239
         }),
239
         }),

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


+ 25
- 0
lib/components/Editor/index.css View File

130
     height: 28px;
130
     height: 28px;
131
   }
131
   }
132
 }
132
 }
133
+
134
+.comment-img-popover {
135
+  /* 一行显示3张图 */
136
+  width: 336px;
137
+  min-height: 100px;
138
+  margin: 0 auto;
139
+}
140
+
141
+.comment-img-title {
142
+  margin: 5px auto;
143
+}
144
+.comment-img-title-counter {
145
+  color: #666;
146
+  font-weight: 400;
147
+}
148
+
149
+@media (max-width: 575px) {
150
+  .comment-img-popover {
151
+    /* 一行显示2张图 */
152
+    width: 224px;
153
+  }
154
+  .comment-img-title-counter {
155
+    display: none;
156
+  }
157
+}

+ 106
- 59
lib/components/Editor/index.js View File

36
 
36
 
37
 require("antd/es/input/style");
37
 require("antd/es/input/style");
38
 
38
 
39
-var _react = require("react");
39
+var _dayjs = require("dayjs");
40
 
40
 
41
-var _react2 = _interopRequireDefault(_react);
41
+var _dayjs2 = _interopRequireDefault(_dayjs);
42
+
43
+var _shortid = require("shortid");
44
+
45
+var _shortid2 = _interopRequireDefault(_shortid);
42
 
46
 
43
 var _propTypes = require("prop-types");
47
 var _propTypes = require("prop-types");
44
 
48
 
48
 
52
 
49
 var _classnames2 = _interopRequireDefault(_classnames);
53
 var _classnames2 = _interopRequireDefault(_classnames);
50
 
54
 
51
-var _reactIntlUniversal = require("react-intl-universal");
52
-
53
-var _reactIntlUniversal2 = _interopRequireDefault(_reactIntlUniversal);
54
-
55
-var _dayjs = require("dayjs");
55
+var _react = require("react");
56
 
56
 
57
-var _dayjs2 = _interopRequireDefault(_dayjs);
57
+var _react2 = _interopRequireDefault(_react);
58
 
58
 
59
-var _shortid = require("shortid");
59
+var _reactIntlUniversal = require("react-intl-universal");
60
 
60
 
61
-var _shortid2 = _interopRequireDefault(_shortid);
61
+var _reactIntlUniversal2 = _interopRequireDefault(_reactIntlUniversal);
62
 
62
 
63
-var _constant = require("../../constant");
63
+var _Emoji = require("./Emoji");
64
 
64
 
65
-var _helper = require("../../helper");
65
+var _Emoji2 = _interopRequireDefault(_Emoji);
66
 
66
 
67
 var _Upload = require("./Upload");
67
 var _Upload = require("./Upload");
68
 
68
 
69
 var _Upload2 = _interopRequireDefault(_Upload);
69
 var _Upload2 = _interopRequireDefault(_Upload);
70
 
70
 
71
-var _Emoji = require("./Emoji");
72
-
73
-var _Emoji2 = _interopRequireDefault(_Emoji);
74
-
75
 var _Comment = require("../../Comment");
71
 var _Comment = require("../../Comment");
76
 
72
 
77
 var _Comment2 = _interopRequireDefault(_Comment);
73
 var _Comment2 = _interopRequireDefault(_Comment);
78
 
74
 
75
+var _utils = require("./../../utils");
76
+
77
+var _constant = require("../../constant");
78
+
79
+var _helper = require("../../helper");
80
+
79
 require("./index.css");
81
 require("./index.css");
80
 
82
 
81
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
83
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
138
     _this.handlePaste = _this.handlePaste.bind(_this);
140
     _this.handlePaste = _this.handlePaste.bind(_this);
139
     _this.resetState = _this.resetState.bind(_this);
141
     _this.resetState = _this.resetState.bind(_this);
140
     _this.handleEmojiScroll = _this.handleEmojiScroll.bind(_this);
142
     _this.handleEmojiScroll = _this.handleEmojiScroll.bind(_this);
143
+    _this.handlePressEnter = _this.handlePressEnter.bind(_this);
144
+    _this.invokeFileListChange = _this.invokeFileListChange.bind(_this);
141
     return _this;
145
     return _this;
142
   }
146
   }
143
 
147
 
212
       if (fileList.length > this.props.maxUpload) {
216
       if (fileList.length > this.props.maxUpload) {
213
         list = fileList.slice(0, this.props.maxUpload);
217
         list = fileList.slice(0, this.props.maxUpload);
214
       }
218
       }
215
-      this.props.handleChangeFileList(list);
216
-      this.setState({ fileList: list });
219
+      this.invokeFileListChange(list);
217
     }
220
     }
218
 
221
 
219
     /**
222
     /**
251
         }
254
         }
252
         return item;
255
         return item;
253
       });
256
       });
254
-      this.props.handleChangeFileList(fileList);
255
-      this.setState({ fileMap: fileMap, fileList: fileList });
257
+      this.setState({ fileMap: fileMap });
258
+      this.invokeFileListChange(fileList);
259
+    }
260
+
261
+    /**
262
+     *  **统一处理fileList的修改**
263
+     *  1. upload
264
+     *  2. paste
265
+     *  PS: 移动端需要做额外操作
266
+     *  -- evo 20200223
267
+     */
268
+
269
+  }, {
270
+    key: "invokeFileListChange",
271
+    value: function invokeFileListChange(fileList) {
272
+      var _props2 = this.props,
273
+          limitOne = _props2.limitOne,
274
+          handleChangeFileList = _props2.handleChangeFileList;
275
+
276
+      handleChangeFileList(fileList);
277
+      this.setState({ fileList: fileList });
278
+      if (limitOne && _utils.isMobile) {
279
+        var file = fileList[0];
280
+        if (file && file.status === "done" && !file.thumbUrl.includes("data:image")) {
281
+          this.setState({ uploadVisible: false });
282
+        }
283
+      }
256
     }
284
     }
257
 
285
 
258
     /**
286
     /**
292
             type: file.type,
320
             type: file.type,
293
             uid: new Date().valueOf()
321
             uid: new Date().valueOf()
294
           });
322
           });
295
-          _this2.props.handleChangeFileList(fileList);
296
-          _this2.setState({
297
-            fileList: fileList
298
-          });
323
+          _this2.invokeFileListChange(fileList);
299
         }).catch(function (e) {
324
         }).catch(function (e) {
300
           var msg = e.message || _constant.ERROR_DEFAULT;
325
           var msg = e.message || _constant.ERROR_DEFAULT;
301
           if (_this2.props.showError) {
326
           if (_this2.props.showError) {
378
   }, {
403
   }, {
379
     key: "checkDisabledSubmit",
404
     key: "checkDisabledSubmit",
380
     value: function checkDisabledSubmit() {
405
     value: function checkDisabledSubmit() {
381
-      var _props2 = this.props,
382
-          btnDisabled = _props2.btnDisabled,
383
-          value = _props2.value,
384
-          fileList = _props2.fileList;
406
+      var _props3 = this.props,
407
+          btnDisabled = _props3.btnDisabled,
408
+          value = _props3.value,
409
+          fileList = _props3.fileList;
385
 
410
 
386
       if (btnDisabled) {
411
       if (btnDisabled) {
387
         return true;
412
         return true;
400
       }
425
       }
401
       return true;
426
       return true;
402
     }
427
     }
428
+
429
+    /**
430
+     *  **处理Enter事件**
431
+     *  1. `allowEnterSubmit`为true时enter触发submit事件
432
+     *  2. `e.preventDefault`为了防止enter事件后仍触发换行
433
+     *  3. enter事件开启后,仍可以用`shift + enter`触发换行
434
+     *  -- evo 20200222
435
+     */
436
+
437
+  }, {
438
+    key: "handlePressEnter",
439
+    value: function handlePressEnter(e) {
440
+      if (this.props.allowEnterSubmit) {
441
+        if (!e.shiftKey) {
442
+          e.preventDefault();
443
+          this.handleSubmit();
444
+        }
445
+      }
446
+    }
403
   }, {
447
   }, {
404
     key: "render",
448
     key: "render",
405
     value: function render() {
449
     value: function render() {
406
       var _this4 = this;
450
       var _this4 = this;
407
 
451
 
408
-      var _props3 = this.props,
409
-          value = _props3.value,
410
-          rows = _props3.rows,
411
-          showEmoji = _props3.showEmoji,
412
-          showUpload = _props3.showUpload,
413
-          multiple = _props3.multiple,
414
-          emojiPopoverPlacement = _props3.emojiPopoverPlacement,
415
-          uploadPopoverPlacement = _props3.uploadPopoverPlacement,
416
-          uploadOverlayClassName = _props3.uploadOverlayClassName,
417
-          fileList = _props3.fileList,
418
-          maxUpload = _props3.maxUpload,
419
-          btnLoading = _props3.btnLoading,
420
-          button = _props3.button,
421
-          emojiToolIcon = _props3.emojiToolIcon,
422
-          imageToolIcon = _props3.imageToolIcon,
423
-          maxLength = _props3.maxLength,
424
-          autoFocus = _props3.autoFocus,
425
-          app = _props3.app;
452
+      var _props4 = this.props,
453
+          value = _props4.value,
454
+          rows = _props4.rows,
455
+          showEmoji = _props4.showEmoji,
456
+          showUpload = _props4.showUpload,
457
+          multiple = _props4.multiple,
458
+          emojiPopoverPlacement = _props4.emojiPopoverPlacement,
459
+          uploadPopoverPlacement = _props4.uploadPopoverPlacement,
460
+          uploadOverlayClassName = _props4.uploadOverlayClassName,
461
+          fileList = _props4.fileList,
462
+          maxUpload = _props4.maxUpload,
463
+          btnLoading = _props4.btnLoading,
464
+          button = _props4.button,
465
+          emojiToolIcon = _props4.emojiToolIcon,
466
+          imageToolIcon = _props4.imageToolIcon,
467
+          maxLength = _props4.maxLength,
468
+          autoFocus = _props4.autoFocus,
469
+          app = _props4.app;
426
 
470
 
427
       var placeholder = this.props.placeholder || _reactIntlUniversal2.default.get("editor.placeholder");
471
       var placeholder = this.props.placeholder || _reactIntlUniversal2.default.get("editor.placeholder");
428
       var btnSubmitText = this.props.btnSubmitText || _reactIntlUniversal2.default.get("editor.SubmitBtn");
472
       var btnSubmitText = this.props.btnSubmitText || _reactIntlUniversal2.default.get("editor.SubmitBtn");
431
       var inputValue = value || this.state.value;
475
       var inputValue = value || this.state.value;
432
       var uploadFileList = fileList || this.state.fileList;
476
       var uploadFileList = fileList || this.state.fileList;
433
       var isLogin = app.currentUser && (app.currentUser.user_id > 0 || app.currentUser.id > 0);
477
       var isLogin = app.currentUser && (app.currentUser.user_id > 0 || app.currentUser.id > 0);
478
+
434
       return _react2.default.createElement(
479
       return _react2.default.createElement(
435
         "div",
480
         "div",
436
         { className: "comment-editor-container", onPaste: this.handlePaste },
481
         { className: "comment-editor-container", onPaste: this.handlePaste },
449
             _react2.default.createElement(TextArea, {
494
             _react2.default.createElement(TextArea, {
450
               value: inputValue,
495
               value: inputValue,
451
               onChange: function onChange(e) {
496
               onChange: function onChange(e) {
452
-                return _this4.handleChange(e.target.value);
497
+                _this4.handleChange(e.target.value);
453
               },
498
               },
454
               rows: rows,
499
               rows: rows,
455
               placeholder: placeholder,
500
               placeholder: placeholder,
456
-              autoFocus: autoFocus
501
+              autoFocus: autoFocus,
502
+              onPressEnter: this.handlePressEnter
457
             }),
503
             }),
458
             _react2.default.createElement(
504
             _react2.default.createElement(
459
               "div",
505
               "div",
503
                     },
549
                     },
504
                     content: _react2.default.createElement(
550
                     content: _react2.default.createElement(
505
                       "div",
551
                       "div",
506
-                      {
507
-                        style: {
508
-                          width: 336, // 一行显示3张
509
-                          minHeight: 100,
510
-                          margin: "0 auto"
511
-                        }
512
-                      },
552
+                      { className: "comment-img-popover" },
513
                       _react2.default.createElement(_Upload2.default, {
553
                       _react2.default.createElement(_Upload2.default, {
514
                         onRef: function onRef(node) {
554
                         onRef: function onRef(node) {
515
                           return _this4.uploadRef = node;
555
                           return _this4.uploadRef = node;
526
                     ),
566
                     ),
527
                     title: _react2.default.createElement(
567
                     title: _react2.default.createElement(
528
                       "div",
568
                       "div",
529
-                      { style: { margin: "5px auto" } },
569
+                      { className: "comment-img-title" },
530
                       _react2.default.createElement(
570
                       _react2.default.createElement(
531
                         "span",
571
                         "span",
532
                         null,
572
                         null,
533
                         _reactIntlUniversal2.default.get("editor.uploadTip"),
573
                         _reactIntlUniversal2.default.get("editor.uploadTip"),
534
                         maxUpload >= 2 ? _react2.default.createElement(
574
                         maxUpload >= 2 ? _react2.default.createElement(
535
                           "span",
575
                           "span",
536
-                          { style: { color: "#666", fontWeight: 400 } },
576
+                          { className: "comment-img-title-counter" },
537
                           _reactIntlUniversal2.default.get("editor.uploadCount", {
577
                           _reactIntlUniversal2.default.get("editor.uploadCount", {
538
                             count: maxUpload - uploadFileList.length
578
                             count: maxUpload - uploadFileList.length
539
                           })
579
                           })
628
   imageToolIcon: _propTypes2.default.node,
668
   imageToolIcon: _propTypes2.default.node,
629
   showError: _propTypes2.default.bool,
669
   showError: _propTypes2.default.bool,
630
   onError: _propTypes2.default.func,
670
   onError: _propTypes2.default.func,
631
-  maxLength: _propTypes2.default.number
671
+  maxLength: _propTypes2.default.number,
672
+  // Enter事件相关
673
+  allowEnterSubmit: _propTypes2.default.bool,
674
+  // 私信仅允许选中一个,此处可以优化为通用的limit
675
+  limitOne: _propTypes2.default.bool
632
 };
676
 };
633
 
677
 
634
 Editor.defaultProps = {
678
 Editor.defaultProps = {
648
   showError: true,
692
   showError: true,
649
   maxLength: 5000,
693
   maxLength: 5000,
650
   app: {},
694
   app: {},
651
-  handleChangeFileList: function handleChangeFileList() {}
695
+  handleChangeFileList: function handleChangeFileList() {},
696
+  // Enter事件相关
697
+  allowEnterSubmit: false,
698
+  limitOne: false
652
 };
699
 };
653
 
700
 
654
 exports.default = (0, _Comment2.default)(Editor);
701
 exports.default = (0, _Comment2.default)(Editor);

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


+ 10
- 8
lib/index.js View File

68
           fileList: this.state.fileList,
68
           fileList: this.state.fileList,
69
           value: this.state.value,
69
           value: this.state.value,
70
           onChange: function onChange(value) {
70
           onChange: function onChange(value) {
71
-            _this2.setState({
72
-              value: value
73
-            });
71
+            _this2.setState({ value: value });
74
           },
72
           },
75
           handleChangeFileList: function handleChangeFileList(fileList) {
73
           handleChangeFileList: function handleChangeFileList(fileList) {
76
-            console.log("----", fileList);
77
             _this2.setState({
74
             _this2.setState({
78
               fileList: fileList
75
               fileList: fileList
79
             });
76
             });
80
-          }
77
+          },
78
+          allowEnterSubmit: true,
79
+          limitOne: true
81
         }))
80
         }))
82
       );
81
       );
83
     }
82
     }
123
 if (process.env.NODE_ENV !== "production") {
122
 if (process.env.NODE_ENV !== "production") {
124
   renderComment({
123
   renderComment({
125
     id: "root-comment",
124
     id: "root-comment",
126
-    type: 1,
127
-    businessId: "test",
125
+    type: 3,
126
+    businessId: "5ea8320dedd68200018e733d",
128
     businessUserId: 4,
127
     businessUserId: 4,
129
     userId: 58297,
128
     userId: 58297,
130
     currentUser: {
129
     currentUser: {
177
       });
176
       });
178
     },
177
     },
179
     onCountChange: function onCountChange(c) {
178
     onCountChange: function onCountChange(c) {
180
-      console.log(c);
179
+      // console.log(c);
181
     },
180
     },
182
     onDelete: function onDelete(type, data) {
181
     onDelete: function onDelete(type, data) {
183
       console.log(type, data);
182
       console.log(type, data);
194
     sendMessage: function sendMessage(id) {
193
     sendMessage: function sendMessage(id) {
195
       console.log("sendMessage", id);
194
       console.log("sendMessage", id);
196
     },
195
     },
196
+    preRenderValue: function preRenderValue(value) {
197
+      return "" + value;
198
+    },
197
     editorProps: {
199
     editorProps: {
198
       onCommentSuccess: function onCommentSuccess(data) {
200
       onCommentSuccess: function onCommentSuccess(data) {
199
         console.log(data);
201
         console.log(data);

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


+ 2
- 0
lib/lang/en-US.js View File

16
   "comment.totalComment": "Total {total, plural, =1 {one comment} other {# comments}}",
16
   "comment.totalComment": "Total {total, plural, =1 {one comment} other {# comments}}",
17
   "comment.reply": "Reply",
17
   "comment.reply": "Reply",
18
   "comment.moreComment": "More comments",
18
   "comment.moreComment": "More comments",
19
+  "comment.filterSpeak": "Show imitations only",
20
+  "comment.speakComment": "Imitation audio",
19
 
21
 
20
   "reply.totalReply": "Total {total, plural, =1 {one reply} other {# replies}}",
22
   "reply.totalReply": "Total {total, plural, =1 {one reply} other {# replies}}",
21
   "reply.moreReply": "More replies",
23
   "reply.moreReply": "More replies",

+ 1
- 1
lib/lang/en-US.js.map View File

1
-{"version":3,"sources":["../../src/lang/en-US.js"],"names":["USdata"],"mappings":";;;;;AAAA,IAAMA,SAAS;AACb,2BAAyB,uBADZ;AAEb,wBAAsB,eAFT;AAGb,sBAAoB,2BAHP;AAIb,sBAAoB,MAJP;AAKb,sBAAoB,iBALP;AAMb,wBAAsB,iCANT;AAOb,sBAAoB,QAPP;;AASb,qBAAmB,SATN;AAUb,0BACE,4DAXW;AAYb,mBAAiB,OAZJ;AAab,yBAAuB,eAbV;;AAeb,sBAAoB,yDAfP;AAgBb,qBAAmB,cAhBN;AAiBb,oBAAkB,cAjBL;;AAmBb,sBAAoB,MAnBP;AAoBb,0BAAwB,kBApBX;;AAsBb,sBAAoB,SAtBP;AAuBb,mBAAiB,SAvBJ;AAwBb,uBAAqB,QAxBR;AAyBb,uBAAqB,QAzBR;;AA2Bb,2BAAyB,kBA3BZ;AA4Bb,wBAAsB,cA5BT;AA6Bb,qBAAmB,kBA7BN;AA8Bb,qBAAmB,eA9BN;AA+Bb,yBAAuB,kBA/BV;AAgCb,0BAAwB,YAhCX;AAiCb,+BAA6B,SAjChB;AAkCb,yBAAuB;AAlCV,CAAf;;kBAqCeA,M","file":"en-US.js","sourcesContent":["const USdata = {\n  \"editor.alreadyEntered\": \"{count} words entered\",\n  \"editor.placeholder\": \"Say something\",\n  \"editor.maxLength\": \"Maximum {maxLength} words\",\n  \"editor.SubmitBtn\": \"Send\",\n  \"editor.uploadTip\": \"Upload pictures\",\n  \"editor.uploadCount\": \"(You could upload {count} more)\",\n  \"editor.uploadBtn\": \"Upload\",\n\n  \"comment.tourist\": \"Visitor\",\n  \"comment.totalComment\":\n    \"Total {total, plural, =1 {one comment} other {# comments}}\",\n  \"comment.reply\": \"Reply\",\n  \"comment.moreComment\": \"More comments\",\n\n  \"reply.totalReply\": \"Total {total, plural, =1 {one reply} other {# replies}}\",\n  \"reply.moreReply\": \"More replies\",\n  \"reply.collapse\": \"Fold replies\",\n\n  \"picture.collapse\": \"Fold\",\n  \"picture.viewOriginal\": \"See the original\",\n\n  \"popConfirm.title\": \"Delete?\",\n  \"popConfirm.ok\": \"Confirm\",\n  \"popConfirm.cancel\": \"Cancel\",\n  \"popConfirm.delete\": \"Delete\",\n\n  \"message.noMoreComment\": \"No more comments\",\n  \"message.noMoreData\": \"No more data\",\n  \"message.notNull\": \"It's still empty\",\n  \"message.success\": \"Comments sent\",\n  \"message.replyNoNull\": \"It's still empty\",\n  \"message.replySuccess\": \"Reply sent\",\n  \"message.cancelLickSuccess\": \"Unliked\",\n  \"message.likeSuccess\": \"Liked\"\n};\n\nexport default USdata;\n"]}
1
+{"version":3,"sources":["../../src/lang/en-US.js"],"names":["USdata"],"mappings":";;;;;AAAA,IAAMA,SAAS;AACb,2BAAyB,uBADZ;AAEb,wBAAsB,eAFT;AAGb,sBAAoB,2BAHP;AAIb,sBAAoB,MAJP;AAKb,sBAAoB,iBALP;AAMb,wBAAsB,iCANT;AAOb,sBAAoB,QAPP;;AASb,qBAAmB,SATN;AAUb,0BACE,4DAXW;AAYb,mBAAiB,OAZJ;AAab,yBAAuB,eAbV;AAcb,yBAAuB,sBAdV;AAeb,0BAAwB,iBAfX;;AAiBb,sBAAoB,yDAjBP;AAkBb,qBAAmB,cAlBN;AAmBb,oBAAkB,cAnBL;;AAqBb,sBAAoB,MArBP;AAsBb,0BAAwB,kBAtBX;;AAwBb,sBAAoB,SAxBP;AAyBb,mBAAiB,SAzBJ;AA0Bb,uBAAqB,QA1BR;AA2Bb,uBAAqB,QA3BR;;AA6Bb,2BAAyB,kBA7BZ;AA8Bb,wBAAsB,cA9BT;AA+Bb,qBAAmB,kBA/BN;AAgCb,qBAAmB,eAhCN;AAiCb,yBAAuB,kBAjCV;AAkCb,0BAAwB,YAlCX;AAmCb,+BAA6B,SAnChB;AAoCb,yBAAuB;AApCV,CAAf;;kBAuCeA,M","file":"en-US.js","sourcesContent":["const USdata = {\n  \"editor.alreadyEntered\": \"{count} words entered\",\n  \"editor.placeholder\": \"Say something\",\n  \"editor.maxLength\": \"Maximum {maxLength} words\",\n  \"editor.SubmitBtn\": \"Send\",\n  \"editor.uploadTip\": \"Upload pictures\",\n  \"editor.uploadCount\": \"(You could upload {count} more)\",\n  \"editor.uploadBtn\": \"Upload\",\n\n  \"comment.tourist\": \"Visitor\",\n  \"comment.totalComment\":\n    \"Total {total, plural, =1 {one comment} other {# comments}}\",\n  \"comment.reply\": \"Reply\",\n  \"comment.moreComment\": \"More comments\",\n  \"comment.filterSpeak\": \"Show imitations only\",\n  \"comment.speakComment\": \"Imitation audio\",\n\n  \"reply.totalReply\": \"Total {total, plural, =1 {one reply} other {# replies}}\",\n  \"reply.moreReply\": \"More replies\",\n  \"reply.collapse\": \"Fold replies\",\n\n  \"picture.collapse\": \"Fold\",\n  \"picture.viewOriginal\": \"See the original\",\n\n  \"popConfirm.title\": \"Delete?\",\n  \"popConfirm.ok\": \"Confirm\",\n  \"popConfirm.cancel\": \"Cancel\",\n  \"popConfirm.delete\": \"Delete\",\n\n  \"message.noMoreComment\": \"No more comments\",\n  \"message.noMoreData\": \"No more data\",\n  \"message.notNull\": \"It's still empty\",\n  \"message.success\": \"Comments sent\",\n  \"message.replyNoNull\": \"It's still empty\",\n  \"message.replySuccess\": \"Reply sent\",\n  \"message.cancelLickSuccess\": \"Unliked\",\n  \"message.likeSuccess\": \"Liked\"\n};\n\nexport default USdata;\n"]}

+ 2
- 0
lib/lang/zh-CN.js View File

16
   "comment.totalComment": "共{total}条评论",
16
   "comment.totalComment": "共{total}条评论",
17
   "comment.reply": "回复",
17
   "comment.reply": "回复",
18
   "comment.moreComment": "更多评论",
18
   "comment.moreComment": "更多评论",
19
+  "comment.filterSpeak": "只显示跟读",
20
+  "comment.speakComment": "跟读语音",
19
 
21
 
20
   "reply.totalReply": "共{total}条回复",
22
   "reply.totalReply": "共{total}条回复",
21
   "reply.moreReply": "更多回复",
23
   "reply.moreReply": "更多回复",

+ 1
- 1
lib/lang/zh-CN.js.map View File

1
-{"version":3,"sources":["../../src/lang/zh-CN.js"],"names":["CNdata"],"mappings":";;;;;AAAA,IAAMA,SAAS;AACb,2BAAyB,yBADZ;AAEb,wBAAsB,UAFT;AAGb,sBAAoB,iBAHP;AAIb,sBAAoB,IAJP;AAKb,sBAAoB,MALP;AAMb,wBAAsB,mBANT;AAOb,sBAAoB,IAPP;;AASb,qBAAmB,IATN;AAUb,0BAAwB,aAVX;AAWb,mBAAiB,IAXJ;AAYb,yBAAuB,MAZV;;AAcb,sBAAoB,aAdP;AAeb,qBAAmB,MAfN;AAgBb,oBAAkB,MAhBL;;AAkBb,sBAAoB,IAlBP;AAmBb,0BAAwB,MAnBX;;AAqBb,sBAAoB,QArBP;AAsBb,mBAAiB,IAtBJ;AAuBb,uBAAqB,IAvBR;AAwBb,uBAAqB,IAxBR;;AA0Bb,2BAAyB,SA1BZ;AA2Bb,wBAAsB,UA3BT;AA4Bb,qBAAmB,OA5BN;AA6Bb,qBAAmB,QA7BN;AA8Bb,yBAAuB,OA9BV;AA+Bb,0BAAwB,QA/BX;AAgCb,+BAA6B,QAhChB;AAiCb,yBAAuB;AAjCV,CAAf;;kBAoCeA,M","file":"zh-CN.js","sourcesContent":["const CNdata = {\n  \"editor.alreadyEntered\": \"已输入{count}/{maxLength}字\",\n  \"editor.placeholder\": \"说点什么吧...\",\n  \"editor.maxLength\": \"字数上限{maxLength}\",\n  \"editor.SubmitBtn\": \"发送\",\n  \"editor.uploadTip\": \"上传图片\",\n  \"editor.uploadCount\": \"(您还能上传{count}张图片)\",\n  \"editor.uploadBtn\": \"上传\",\n\n  \"comment.tourist\": \"游客\",\n  \"comment.totalComment\": \"共{total}条评论\",\n  \"comment.reply\": \"回复\",\n  \"comment.moreComment\": \"更多评论\",\n\n  \"reply.totalReply\": \"共{total}条回复\",\n  \"reply.moreReply\": \"更多回复\",\n  \"reply.collapse\": \"收起回复\",\n\n  \"picture.collapse\": \"收起\",\n  \"picture.viewOriginal\": \"查看原图\",\n\n  \"popConfirm.title\": \"确定要删除吗\",\n  \"popConfirm.ok\": \"确定\",\n  \"popConfirm.cancel\": \"取消\",\n  \"popConfirm.delete\": \"删除\",\n\n  \"message.noMoreComment\": \"没有更多评论了\",\n  \"message.noMoreData\": \"没有更多数据了!\",\n  \"message.notNull\": \"没写内容呢\",\n  \"message.success\": \"评论已发送!\",\n  \"message.replyNoNull\": \"没写内容呢\",\n  \"message.replySuccess\": \"回复已发送!\",\n  \"message.cancelLickSuccess\": \"已取消点赞!\",\n  \"message.likeSuccess\": \"已赞!\"\n};\n\nexport default CNdata;\n"]}
1
+{"version":3,"sources":["../../src/lang/zh-CN.js"],"names":["CNdata"],"mappings":";;;;;AAAA,IAAMA,SAAS;AACb,2BAAyB,yBADZ;AAEb,wBAAsB,UAFT;AAGb,sBAAoB,iBAHP;AAIb,sBAAoB,IAJP;AAKb,sBAAoB,MALP;AAMb,wBAAsB,mBANT;AAOb,sBAAoB,IAPP;;AASb,qBAAmB,IATN;AAUb,0BAAwB,aAVX;AAWb,mBAAiB,IAXJ;AAYb,yBAAuB,MAZV;AAab,yBAAuB,OAbV;AAcb,0BAAwB,MAdX;;AAgBb,sBAAoB,aAhBP;AAiBb,qBAAmB,MAjBN;AAkBb,oBAAkB,MAlBL;;AAoBb,sBAAoB,IApBP;AAqBb,0BAAwB,MArBX;;AAuBb,sBAAoB,QAvBP;AAwBb,mBAAiB,IAxBJ;AAyBb,uBAAqB,IAzBR;AA0Bb,uBAAqB,IA1BR;;AA4Bb,2BAAyB,SA5BZ;AA6Bb,wBAAsB,UA7BT;AA8Bb,qBAAmB,OA9BN;AA+Bb,qBAAmB,QA/BN;AAgCb,yBAAuB,OAhCV;AAiCb,0BAAwB,QAjCX;AAkCb,+BAA6B,QAlChB;AAmCb,yBAAuB;AAnCV,CAAf;;kBAsCeA,M","file":"zh-CN.js","sourcesContent":["const CNdata = {\n  \"editor.alreadyEntered\": \"已输入{count}/{maxLength}字\",\n  \"editor.placeholder\": \"说点什么吧...\",\n  \"editor.maxLength\": \"字数上限{maxLength}\",\n  \"editor.SubmitBtn\": \"发送\",\n  \"editor.uploadTip\": \"上传图片\",\n  \"editor.uploadCount\": \"(您还能上传{count}张图片)\",\n  \"editor.uploadBtn\": \"上传\",\n\n  \"comment.tourist\": \"游客\",\n  \"comment.totalComment\": \"共{total}条评论\",\n  \"comment.reply\": \"回复\",\n  \"comment.moreComment\": \"更多评论\",\n  \"comment.filterSpeak\": \"只显示跟读\",\n  \"comment.speakComment\": \"跟读语音\",\n\n  \"reply.totalReply\": \"共{total}条回复\",\n  \"reply.moreReply\": \"更多回复\",\n  \"reply.collapse\": \"收起回复\",\n\n  \"picture.collapse\": \"收起\",\n  \"picture.viewOriginal\": \"查看原图\",\n\n  \"popConfirm.title\": \"确定要删除吗\",\n  \"popConfirm.ok\": \"确定\",\n  \"popConfirm.cancel\": \"取消\",\n  \"popConfirm.delete\": \"删除\",\n\n  \"message.noMoreComment\": \"没有更多评论了\",\n  \"message.noMoreData\": \"没有更多数据了!\",\n  \"message.notNull\": \"没写内容呢\",\n  \"message.success\": \"评论已发送!\",\n  \"message.replyNoNull\": \"没写内容呢\",\n  \"message.replySuccess\": \"回复已发送!\",\n  \"message.cancelLickSuccess\": \"已取消点赞!\",\n  \"message.likeSuccess\": \"已赞!\"\n};\n\nexport default CNdata;\n"]}

+ 10
- 0
lib/utils.js View File

1
+"use strict";
2
+
3
+Object.defineProperty(exports, "__esModule", {
4
+  value: true
5
+});
6
+/**
7
+ * 575: 为了和media query中的判断保持一致
8
+ */
9
+var isMobile = exports.isMobile = typeof window.orientation === "number" && document.body.offsetWidth < 575;
10
+//# sourceMappingURL=utils.js.map

+ 1
- 0
lib/utils.js.map View File

1
+{"version":3,"sources":["../src/utils.js"],"names":["isMobile","window","orientation","document","body","offsetWidth"],"mappings":";;;;;AAAA;;;AAGO,IAAMA,8BACX,OAAOC,OAAOC,WAAd,KAA8B,QAA9B,IAA0CC,SAASC,IAAT,CAAcC,WAAd,GAA4B,GADjE","file":"utils.js","sourcesContent":["/**\n * 575: 为了和media query中的判断保持一致\n */\nexport const isMobile =\n  typeof window.orientation === \"number\" && document.body.offsetWidth < 575;\n"]}

+ 4
- 4
lib/version.json View File

1
 {
1
 {
2
     "name":       "comment",
2
     "name":       "comment",
3
-    "buildDate":  1574306910894,
3
+    "buildDate":  1588435719936,
4
     "version":    "1.0.4",
4
     "version":    "1.0.4",
5
-    "numCommits": 195,
6
-    "hash":       "db0a99c",
7
-    "dirty":      false
5
+    "numCommits": 215,
6
+    "hash":       "7d03c9b",
7
+    "dirty":      true
8
 }
8
 }

+ 1
- 1
package.json View File

16
   "dependencies": {
16
   "dependencies": {
17
     "antd": "^3.19.3",
17
     "antd": "^3.19.3",
18
     "axios": "^0.18.0",
18
     "axios": "^0.18.0",
19
-    "dayjs": "^1.7.2",
19
+    "dayjs": "^1.8.25",
20
     "js-cookie": "^2.2.0",
20
     "js-cookie": "^2.2.0",
21
     "prop-types": "^15.6.2",
21
     "prop-types": "^15.6.2",
22
     "react": "^16.4.1",
22
     "react": "^16.4.1",

+ 8
- 4
src/App.js View File

190
   /**
190
   /**
191
    * 获取评论列表
191
    * 获取评论列表
192
    */
192
    */
193
-  sGetComment({ page = 1 } = {}) {
193
+  sGetComment({ page = 1, filterSpeak = 0 } = {}) {
194
     const { pageType } = this.props;
194
     const { pageType } = this.props;
195
     this.handleChangeLoading("sGetComment", true);
195
     this.handleChangeLoading("sGetComment", true);
196
     const { API, type, businessId, limit } = this.props;
196
     const { API, type, businessId, limit } = this.props;
197
     this.axios
197
     this.axios
198
       .get(
198
       .get(
199
-        `${API}/comments?type=${type}&business_id=${businessId}&page=${page}&limit=${limit}`
199
+        `${API}/comments?type=${type}&business_id=${businessId}&is_speak=${filterSpeak}&page=${page}&limit=${limit}`
200
       )
200
       )
201
       .then(response => {
201
       .then(response => {
202
         const { list, page, total } = response.data;
202
         const { list, page, total } = response.data;
594
       sUpdateComment: this.sUpdateComment,
594
       sUpdateComment: this.sUpdateComment,
595
       handleEdit: this.handleEdit
595
       handleEdit: this.handleEdit
596
     };
596
     };
597
+
597
     return (
598
     return (
598
       this.state.initDone && (
599
       this.state.initDone && (
599
         <CommentContext.Provider value={value}>
600
         <CommentContext.Provider value={value}>
617
               userId={this.state.content.user_id}
618
               userId={this.state.content.user_id}
618
               content={this.state.content}
619
               content={this.state.content}
619
               handleClose={this.handleClose}
620
               handleClose={this.handleClose}
621
+              preRenderValue={this.props.preRenderValue}
620
             />
622
             />
621
           )}
623
           )}
622
         </CommentContext.Provider>
624
         </CommentContext.Provider>
647
   onUpdateComment: PropTypes.func,
649
   onUpdateComment: PropTypes.func,
648
   locales: PropTypes.string, //  传入的语言环境, en-US/zh-CN
650
   locales: PropTypes.string, //  传入的语言环境, en-US/zh-CN
649
   onCountChange: PropTypes.func, // 评论数量变更时的回调
651
   onCountChange: PropTypes.func, // 评论数量变更时的回调
650
-  onCommentFail: PropTypes.func // 评论失败时的回调
652
+  onCommentFail: PropTypes.func, // 评论失败时的回调
653
+  preRenderValue: PropTypes.func // 编辑器渲染前对值需要做的工作
651
 };
654
 };
652
 
655
 
653
 App.defaultProps = {
656
 App.defaultProps = {
673
   onUpdateComment: () => {},
676
   onUpdateComment: () => {},
674
   onBeforeUpdateComment: () => {},
677
   onBeforeUpdateComment: () => {},
675
   onCountChange: () => {},
678
   onCountChange: () => {},
676
-  onCommentFail: () => {}
679
+  onCommentFail: () => {},
680
+  preRenderValue: v => v
677
 };
681
 };
678
 
682
 
679
 export { Editor, RenderText };
683
 export { Editor, RenderText };

BIN
src/assert/btn_audio_play.png View File


BIN
src/assert/loading.gif View File


+ 144
- 0
src/components/AudioPlayer/index.js View File

1
+import React from "react";
2
+import { Slider, Icon } from "antd";
3
+import dayjs from "dayjs";
4
+import durationPlugin from "dayjs/plugin/duration";
5
+import utcPlugin from "dayjs/plugin/utc";
6
+import "./index.less";
7
+import iconLoading from "../../assert/loading.gif";
8
+import btnAudioPlay from "../../assert/btn_audio_play.png";
9
+
10
+dayjs.extend(durationPlugin);
11
+dayjs.extend(utcPlugin);
12
+
13
+class AudioPlayer extends React.Component {
14
+  constructor(props) {
15
+    super(props);
16
+    this.state = {
17
+      duration: 0,
18
+      currentDuration: 0,
19
+      isPlaying: false,
20
+      isLoading: false
21
+    };
22
+  }
23
+
24
+  componentDidMount() {
25
+    if (this.player) {
26
+      const { src } = this.props;
27
+      this.player.oncanplay = event =>
28
+        this.setState({ duration: event.target.duration });
29
+      this.player.onended = () =>
30
+        this.setState({ isPlaying: false, currentDuration: 0 });
31
+      this.player.onpause = () =>
32
+        this.setState({ isPlaying: false, isLoading: false });
33
+      this.player.onplay = () =>
34
+        this.setState({ isPlaying: false, isLoading: true });
35
+      this.player.onplaying = () =>
36
+        this.setState({ isPlaying: true, isLoading: false });
37
+      this.player.ontimeupdate = event =>
38
+        this.setState({ currentDuration: event.target.currentTime });
39
+      this.player.src = src;
40
+    }
41
+  }
42
+
43
+  componentDidUpdate(prevProps) {
44
+    if (this.props.src !== prevProps.src) {
45
+      this.player.oncanplay = event =>
46
+        this.setState({ duration: event.target.duration });
47
+      this.player.onended = () =>
48
+        this.setState({ isPlaying: false, currentDuration: 0 });
49
+      this.player.onpause = () =>
50
+        this.setState({ isPlaying: false, isLoading: false });
51
+      this.player.onplay = () =>
52
+        this.setState({ isPlaying: false, isLoading: true });
53
+      this.player.onplaying = () =>
54
+        this.setState({ isPlaying: true, isLoading: false });
55
+      this.player.ontimeupdate = event =>
56
+        this.setState({ currentDuration: event.target.currentTime });
57
+      this.player.src = this.props.src;
58
+    }
59
+  }
60
+
61
+  clickPlayOrPause(isPlaying) {
62
+    if (!this.player) {
63
+      return;
64
+    }
65
+    if (isPlaying) {
66
+      this.player.pause();
67
+    } else {
68
+      // 如果是播放,先暂停其他的播放
69
+      const playerList = document.getElementsByTagName("audio");
70
+      if (playerList) {
71
+        Array.from(playerList).forEach(player => {
72
+          if (!player.paused) {
73
+            player.pause();
74
+          }
75
+        });
76
+      }
77
+      this.player.play();
78
+    }
79
+  }
80
+
81
+  getPlayIcon() {
82
+    const { isPlaying, isLoading } = this.state;
83
+    let playIconElem;
84
+
85
+    if (isLoading) {
86
+      playIconElem = (
87
+        <img className="icon-loading" src={iconLoading} alt="iconLoading" />
88
+      );
89
+    } else if (isPlaying) {
90
+      playIconElem = <Icon className="pause" type="pause" />;
91
+    } else {
92
+      playIconElem = (
93
+        <img className="icon-loading" src={btnAudioPlay} alt="iconPlaying" />
94
+      );
95
+    }
96
+    return playIconElem;
97
+  }
98
+
99
+  render() {
100
+    const { currentDuration, isPlaying, duration } = this.state;
101
+    return (
102
+      <div className="comment-item-speak-audio-container">
103
+        {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
104
+        <audio
105
+          ref={ref => {
106
+            this.player = ref;
107
+          }}
108
+          style={{ display: "none" }}
109
+        />
110
+        <span
111
+          className="icon"
112
+          onClick={() => {
113
+            this.clickPlayOrPause(isPlaying);
114
+          }}
115
+        >
116
+          {this.getPlayIcon()}
117
+        </span>
118
+        <Slider
119
+          step={0.001}
120
+          className="slider"
121
+          tooltipVisible={false}
122
+          value={currentDuration}
123
+          max={duration}
124
+          onChange={value => {
125
+            if (this.player) {
126
+              this.player.currentTime = value;
127
+            }
128
+          }}
129
+        />
130
+        <span className="time">
131
+          {dayjs
132
+            .utc(dayjs.duration(currentDuration, "seconds").asMilliseconds())
133
+            .format("mm:ss")}
134
+          /
135
+          {dayjs
136
+            .utc(dayjs.duration(duration, "seconds").asMilliseconds())
137
+            .format("mm:ss")}
138
+        </span>
139
+      </div>
140
+    );
141
+  }
142
+}
143
+
144
+export default AudioPlayer;

+ 65
- 0
src/components/AudioPlayer/index.less View File

1
+.comment-item-speak-audio-container {
2
+  background-color: #f5f5f5;
3
+  border: 1px solid rgba(210, 210, 210, 1);
4
+  border-radius: 4px;
5
+  display: flex;
6
+  align-items: center;
7
+  height: 100%;
8
+
9
+  .icon {
10
+    cursor: pointer;
11
+    width: 28px;
12
+    height: 28px;
13
+    line-height: 28px;
14
+    text-align: center;
15
+    border-radius: 14px;
16
+    background-color: #fff;
17
+    color: #71c135;
18
+    margin-left: 12px;
19
+    font-size: 14px;
20
+  }
21
+
22
+  .icon-loading {
23
+    width: 100%;
24
+    height: 100%;
25
+  }
26
+
27
+  .slider {
28
+    margin: 0 0 0 16px;
29
+    width: 195px;
30
+
31
+    :global {
32
+      .ant-slider-rail {
33
+        background-color: #dfdfdf;
34
+      }
35
+    }
36
+  }
37
+
38
+  .time {
39
+    margin-left: 14px;
40
+    color: #848484;
41
+    font-size: 12px;
42
+  }
43
+}
44
+
45
+@media screen and (max-width: 616px) and (min-width: 449px) {
46
+  .comment-item-speak-audio-container {
47
+    .slider {
48
+      width: 195px;
49
+    }
50
+  }
51
+}
52
+@media screen and (max-width: 449px) and (min-width: 365px) {
53
+  .comment-item-speak-audio-container {
54
+    .slider {
55
+      width: 114px;
56
+    }
57
+  }
58
+}
59
+@media screen and (max-width: 365px) {
60
+  .comment-item-speak-audio-container {
61
+    .slider {
62
+      width: 60px;
63
+    }
64
+  }
65
+}

+ 12
- 0
src/components/CommentList/index.css View File

17
 .comment-list-pagination {
17
 .comment-list-pagination {
18
   text-align: center;
18
   text-align: center;
19
 }
19
 }
20
+
21
+.comment-list-filter-speak {
22
+  margin-left: 20px;
23
+  font-size: 14px;
24
+  color: rgba(93, 93, 93, 0.65);
25
+}
26
+
27
+@media screen and (max-width: 365px) {
28
+  .comment-list-filter-speak {
29
+    float: right;
30
+  }
31
+}

+ 35
- 6
src/components/CommentList/index.js View File

1
 import React, { Component } from "react";
1
 import React, { Component } from "react";
2
-import { Spin, Pagination } from "antd";
2
+import { Spin, Pagination, Checkbox } from "antd";
3
 import intl from "react-intl-universal";
3
 import intl from "react-intl-universal";
4
 import Comment from "../../Comment";
4
 import Comment from "../../Comment";
5
 import CommentBox from "../CommentBox";
5
 import CommentBox from "../CommentBox";
8
 class CommentList extends Component {
8
 class CommentList extends Component {
9
   constructor(props) {
9
   constructor(props) {
10
     super(props);
10
     super(props);
11
-    this.state = {};
11
+    this.state = {
12
+      filterSpeak: 0
13
+    };
12
   }
14
   }
13
 
15
 
14
   componentWillMount() {
16
   componentWillMount() {
27
       onPageChange,
29
       onPageChange,
28
       onGetMoreBtnClick
30
       onGetMoreBtnClick
29
     } = this.props.app;
31
     } = this.props.app;
32
+    const { filterSpeak } = this.state;
30
     if (pageType === "slice") {
33
     if (pageType === "slice") {
31
       // 截断多余评论,通过点击查看更多跳转
34
       // 截断多余评论,通过点击查看更多跳转
32
       return (
35
       return (
40
           <div
43
           <div
41
             className="comment-list-show-more"
44
             className="comment-list-show-more"
42
             onClick={() => {
45
             onClick={() => {
43
-              sGetComment({ page: page + 1 });
46
+              sGetComment({ page: page + 1, filterSpeak });
44
               onPageChange(page + 1);
47
               onPageChange(page + 1);
45
             }}
48
             }}
46
           >
49
           >
58
             current={page}
61
             current={page}
59
             total={total}
62
             total={total}
60
             onChange={p => {
63
             onChange={p => {
61
-              sGetComment({ page: p });
64
+              sGetComment({ page: p, filterSpeak });
62
               onPageChange(p);
65
               onPageChange(p);
63
             }}
66
             }}
64
           />
67
           />
68
   }
71
   }
69
 
72
 
70
   render() {
73
   render() {
71
-    const { list, total, loading } = this.props.app;
74
+    const {
75
+      list,
76
+      total,
77
+      loading,
78
+      isSpeak,
79
+      sGetComment,
80
+      onPageChange
81
+    } = this.props.app;
72
 
82
 
73
     const spinning = Boolean(
83
     const spinning = Boolean(
74
       loading.sGetComment || loading.sCommentFavor || loading.sReplyFavor
84
       loading.sGetComment || loading.sCommentFavor || loading.sReplyFavor
77
       <div>
87
       <div>
78
         <Spin spinning={spinning}>
88
         <Spin spinning={spinning}>
79
           {/* <div>共 {total} 条评论</div> */}
89
           {/* <div>共 {total} 条评论</div> */}
80
-          <div>{intl.get("comment.totalComment", { total })}</div>
90
+          <div>
91
+            {intl.get("comment.totalComment", { total })}
92
+            {isSpeak && (
93
+              <Checkbox
94
+                className="comment-list-filter-speak"
95
+                onChange={e => {
96
+                  this.setState({
97
+                    filterSpeak: e.target.checked ? 1 : 0
98
+                  });
99
+                  sGetComment({
100
+                    page: 1,
101
+                    filterSpeak: e.target.checked ? 1 : 0
102
+                  });
103
+                  onPageChange(1);
104
+                }}
105
+              >
106
+                {intl.get("comment.filterSpeak")}
107
+              </Checkbox>
108
+            )}
109
+          </div>
81
           {list.map(item => (
110
           {list.map(item => (
82
             <CommentBox
111
             <CommentBox
83
               content={item}
112
               content={item}

+ 27
- 0
src/components/ContentItem/index.css View File

33
   word-break: break-all;
33
   word-break: break-all;
34
 }
34
 }
35
 
35
 
36
+.comment-item-speak {
37
+  margin-top: 4px;
38
+}
39
+
40
+.comment-item-speak-message {
41
+  font-size: 14px;
42
+  color: #71c135;
43
+}
44
+
45
+.comment-item-speak-audio-wrapper {
46
+  margin-top: 8px;
47
+  width: 350px;
48
+  height: 44px;
49
+}
50
+
36
 .comment-item-bottom {
51
 .comment-item-bottom {
37
   display: flex;
52
   display: flex;
38
   justify-content: flex-end;
53
   justify-content: flex-end;
156
     width: 85%;
171
     width: 85%;
157
     margin-left: 10px;
172
     margin-left: 10px;
158
   }
173
   }
174
+
175
+  .comment-item-speak-audio-wrapper {
176
+    width: 350px;
177
+  }
159
 }
178
 }
160
 
179
 
161
 @media screen and (max-width: 449px) and (min-width: 365px) {
180
 @media screen and (max-width: 449px) and (min-width: 365px) {
164
     width: 80%;
183
     width: 80%;
165
     margin-left: 10px;
184
     margin-left: 10px;
166
   }
185
   }
186
+
187
+  .comment-item-speak-audio-wrapper {
188
+    width: 266px;
189
+  }
167
 }
190
 }
168
 
191
 
169
 @media screen and (max-width: 365px) {
192
 @media screen and (max-width: 365px) {
172
     width: 75%;
195
     width: 75%;
173
     margin-left: 10px;
196
     margin-left: 10px;
174
   }
197
   }
198
+
199
+  .comment-item-speak-audio-wrapper {
200
+    width: 218px;
201
+  }
175
 }
202
 }
176
 
203
 
177
 @media (max-width: 575px) {
204
 @media (max-width: 575px) {

+ 78
- 59
src/components/ContentItem/index.js View File

14
 import { IMAGE_SPLIT } from "../../constant";
14
 import { IMAGE_SPLIT } from "../../constant";
15
 import "./index.css";
15
 import "./index.css";
16
 import ImagePreviewer from "../ImagePreviewer/ImagePreviewer";
16
 import ImagePreviewer from "../ImagePreviewer/ImagePreviewer";
17
+import AudioPlayer from "../AudioPlayer";
17
 
18
 
18
 // dayjs.locale("zh-cn");
19
 // dayjs.locale("zh-cn");
19
 dayjs.extend(relativeTime);
20
 dayjs.extend(relativeTime);
103
       user_id,
104
       user_id,
104
       page
105
       page
105
     } = this.props;
106
     } = this.props;
107
+    const { medias, is_speak: isSpeak } = content;
106
     const { locale, showHoverCard, showEdit } = this.props.app;
108
     const { locale, showHoverCard, showEdit } = this.props.app;
107
     const { showInput } = this.state;
109
     const { showInput } = this.state;
108
     let newContent = content.content;
110
     let newContent = content.content;
206
               )
208
               )
207
             }}
209
             }}
208
           />
210
           />
211
+
212
+          {isSpeak && (
213
+            <div className="comment-item-speak">
214
+              <span className="comment-item-speak-message">
215
+                [{intl.get("comment.speakComment")}]
216
+              </span>
217
+              <div className="comment-item-speak-audio-wrapper">
218
+                <AudioPlayer src={medias && medias[0] && medias[0].url} />
219
+              </div>
220
+            </div>
221
+          )}
209
           {// image为空时不渲染comment-item-image
222
           {// image为空时不渲染comment-item-image
210
-          imageList.length > 0 && imageList[0] !== "" && (
211
-            <div className="comment-item-image">
212
-              {!this.state.showPreviewer &&
213
-                imgs.map((item, index) => {
214
-                  if (item.type === "divider") {
223
+          imageList.length > 0 &&
224
+            imageList[0] !== "" && (
225
+              <div className="comment-item-image">
226
+                {!this.state.showPreviewer &&
227
+                  imgs.map((item, index) => {
228
+                    if (item.type === "divider") {
229
+                      return (
230
+                        <div className="comment-item-image-wrapper" key={index}>
231
+                          <div className="comment-img-divider" />
232
+                          {/* <img src={item} alt={item} className="comment-img" /> */}
233
+                        </div>
234
+                      );
235
+                    }
215
                     return (
236
                     return (
216
-                      <div className="comment-item-image-wrapper" key={index}>
217
-                        <div className="comment-img-divider" />
237
+                      <div
238
+                        className="comment-item-image-wrapper"
239
+                        key={index}
240
+                        onClick={() => {
241
+                          let i = index;
242
+                          if (needClear) {
243
+                            if (index > 3) {
244
+                              i -= 1;
245
+                            }
246
+                            if (index > 7) {
247
+                              i -= 1;
248
+                            }
249
+                          }
250
+                          this.showPreviewer(i);
251
+                        }}
252
+                      >
253
+                        <div
254
+                          style={{ backgroundImage: `url(${item})` }}
255
+                          className="comment-img-thumbnail"
256
+                        />
218
                         {/* <img src={item} alt={item} className="comment-img" /> */}
257
                         {/* <img src={item} alt={item} className="comment-img" /> */}
219
                       </div>
258
                       </div>
220
                     );
259
                     );
221
-                  }
222
-                  return (
223
-                    <div
224
-                      className="comment-item-image-wrapper"
225
-                      key={index}
226
-                      onClick={() => {
227
-                        let i = index;
228
-                        if (needClear) {
229
-                          if (index > 3) {
230
-                            i -= 1;
231
-                          }
232
-                          if (index > 7) {
233
-                            i -= 1;
234
-                          }
235
-                        }
236
-                        this.showPreviewer(i);
237
-                      }}
238
-                    >
239
-                      <div
240
-                        style={{ backgroundImage: `url(${item})` }}
241
-                        className="comment-img-thumbnail"
242
-                      />
243
-                      {/* <img src={item} alt={item} className="comment-img" /> */}
244
-                    </div>
245
-                  );
246
-                })}
247
-              {this.state.showPreviewer && (
248
-                <ImagePreviewer
249
-                  list={imageList}
250
-                  index={this.state.previewerIndex}
251
-                  onFold={this.hidePreviewer}
252
-                />
253
-              )}
254
-              <div className="clearfix" />
255
-            </div>
256
-          )}
260
+                  })}
261
+                {this.state.showPreviewer && (
262
+                  <ImagePreviewer
263
+                    list={imageList}
264
+                    index={this.state.previewerIndex}
265
+                    onFold={this.hidePreviewer}
266
+                  />
267
+                )}
268
+                <div className="clearfix" />
269
+              </div>
270
+            )}
257
           <div className="comment-item-bottom">
271
           <div className="comment-item-bottom">
258
             {content.reply_count ? (
272
             {content.reply_count ? (
259
               <div>
273
               <div>
264
                 </a>
278
                 </a>
265
               </div>
279
               </div>
266
             ) : null}
280
             ) : null}
267
-            {showEdit && app.userId === content.user_id && (
268
-              <i
269
-                className="comment-item-edit"
270
-                onClick={() =>
271
-                  this.props.app.handleEdit({
272
-                    action,
273
-                    replyId,
274
-                    commentId,
275
-                    userId: content.user_id,
276
-                    content,
277
-                    replyPage: page
278
-                  })
279
-                }
280
-              />
281
-            )}
281
+            {showEdit &&
282
+              !isSpeak &&
283
+              app.userId === content.user_id && (
284
+                <i
285
+                  className="comment-item-edit"
286
+                  onClick={() =>
287
+                    this.props.app.handleEdit({
288
+                      action,
289
+                      replyId,
290
+                      commentId,
291
+                      userId: content.user_id,
292
+                      content,
293
+                      replyPage: page
294
+                    })
295
+                  }
296
+                />
297
+              )}
282
             {app.userId === content.user_id && (
298
             {app.userId === content.user_id && (
283
               <Popconfirm
299
               <Popconfirm
284
                 // title="确定要删除吗?"
300
                 // title="确定要删除吗?"
318
                 }
334
                 }
319
               />
335
               />
320
             </div>
336
             </div>
321
-            <span>&nbsp;{content.favor_count}</span>
337
+            <span>
338
+              &nbsp;
339
+              {content.favor_count}
340
+            </span>
322
             <div
341
             <div
323
               onClick={this.handleToggleInput}
342
               onClick={this.handleToggleInput}
324
               className="comment-item-reply"
343
               className="comment-item-reply"

+ 4
- 1
src/components/EditComment/EditComment.js View File

111
               commentId={this.props.commentId}
111
               commentId={this.props.commentId}
112
               userId={this.props.content.user_id}
112
               userId={this.props.content.user_id}
113
               fileList={this.state.fileList}
113
               fileList={this.state.fileList}
114
-              value={this.state.value}
114
+              value={
115
+                this.props.preRenderValue &&
116
+                this.props.preRenderValue(this.state.value)
117
+              }
115
               onChange={value => {
118
               onChange={value => {
116
                 this.setState({
119
                 this.setState({
117
                   value
120
                   value

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

130
     height: 28px;
130
     height: 28px;
131
   }
131
   }
132
 }
132
 }
133
+
134
+.comment-img-popover {
135
+  /* 一行显示3张图 */
136
+  width: 336px;
137
+  min-height: 100px;
138
+  margin: 0 auto;
139
+}
140
+
141
+.comment-img-title {
142
+  margin: 5px auto;
143
+}
144
+.comment-img-title-counter {
145
+  color: #666;
146
+  font-weight: 400;
147
+}
148
+
149
+@media (max-width: 575px) {
150
+  .comment-img-popover {
151
+    /* 一行显示2张图 */
152
+    width: 224px;
153
+  }
154
+  .comment-img-title-counter {
155
+    display: none;
156
+  }
157
+}

+ 82
- 29
src/components/Editor/index.js View File

1
-import React, { Fragment } from "react";
1
+import dayjs from "dayjs";
2
+import shortid from "shortid";
2
 import PropTypes from "prop-types";
3
 import PropTypes from "prop-types";
3
-import { Icon, Button, Popover, Input, message } from "antd";
4
 import classnames from "classnames";
4
 import classnames from "classnames";
5
+import React, { Fragment } from "react";
5
 import intl from "react-intl-universal";
6
 import intl from "react-intl-universal";
6
-import dayjs from "dayjs";
7
-import shortid from "shortid";
8
-import { OSS_LINK } from "../../constant";
9
-import { isFunction } from "../../helper";
10
-import Upload from "./Upload";
7
+import { Icon, Button, Popover, Input, message } from "antd";
11
 import Emoji from "./Emoji";
8
 import Emoji from "./Emoji";
9
+import Upload from "./Upload";
12
 import Comment from "../../Comment";
10
 import Comment from "../../Comment";
11
+import { isMobile } from "./../../utils";
12
+import { OSS_LINK } from "../../constant";
13
+import { isFunction } from "../../helper";
13
 import {
14
 import {
14
   OSS_ENDPOINT,
15
   OSS_ENDPOINT,
15
   OSS_BUCKET,
16
   OSS_BUCKET,
58
       value: props.value || "", // 编辑器里面的值
59
       value: props.value || "", // 编辑器里面的值
59
       fileList: props.fileList || [], // 图片列表
60
       fileList: props.fileList || [], // 图片列表
60
       fileMap: {}, // 已经上传的图片路径和 uid 的映射 { uid: path }
61
       fileMap: {}, // 已经上传的图片路径和 uid 的映射 { uid: path }
61
-      uploadVisible: false
62
+      uploadVisible: false // 上传图片弹窗是否可视
62
     };
63
     };
63
     this.handleChange = this.handleChange.bind(this);
64
     this.handleChange = this.handleChange.bind(this);
64
     this.handleClickEmoji = this.handleClickEmoji.bind(this);
65
     this.handleClickEmoji = this.handleClickEmoji.bind(this);
69
     this.handlePaste = this.handlePaste.bind(this);
70
     this.handlePaste = this.handlePaste.bind(this);
70
     this.resetState = this.resetState.bind(this);
71
     this.resetState = this.resetState.bind(this);
71
     this.handleEmojiScroll = this.handleEmojiScroll.bind(this);
72
     this.handleEmojiScroll = this.handleEmojiScroll.bind(this);
73
+    this.handlePressEnter = this.handlePressEnter.bind(this);
74
+    this.invokeFileListChange = this.invokeFileListChange.bind(this);
72
   }
75
   }
73
 
76
 
74
   componentDidMount() {
77
   componentDidMount() {
75
-    const { app, onRef } = this.props;
78
+    const { app, onRef, uploadDefaultShow } = this.props;
76
     if (
79
     if (
77
       app.currentUser &&
80
       app.currentUser &&
78
       (app.currentUser.user_id > 0 || app.currentUser.id > 0)
81
       (app.currentUser.user_id > 0 || app.currentUser.id > 0)
82
     if (isFunction(onRef)) {
85
     if (isFunction(onRef)) {
83
       onRef(this);
86
       onRef(this);
84
     }
87
     }
88
+    if (uploadDefaultShow) {
89
+      const uploadFileList = this.props.fileList || this.state.fileList;
90
+      if (uploadFileList.length > 0) {
91
+        this.setState({
92
+          uploadVisible: true,
93
+        });
94
+      }
95
+    }
85
   }
96
   }
86
 
97
 
87
   handleEmojiScroll(e) {
98
   handleEmojiScroll(e) {
130
     if (fileList.length > this.props.maxUpload) {
141
     if (fileList.length > this.props.maxUpload) {
131
       list = fileList.slice(0, this.props.maxUpload);
142
       list = fileList.slice(0, this.props.maxUpload);
132
     }
143
     }
133
-    this.props.handleChangeFileList(list);
134
-    this.setState({ fileList: list });
144
+    this.invokeFileListChange(list);
135
   }
145
   }
136
 
146
 
137
   /**
147
   /**
160
       }
170
       }
161
       return item;
171
       return item;
162
     });
172
     });
163
-    this.props.handleChangeFileList(fileList);
164
-    this.setState({ fileMap, fileList });
173
+    this.setState({ fileMap });
174
+    this.invokeFileListChange(fileList);
175
+  }
176
+
177
+  /**
178
+   *  **统一处理fileList的修改**
179
+   *  1. upload
180
+   *  2. paste
181
+   *  PS: 移动端需要做额外操作
182
+   *  -- evo 20200223
183
+   */
184
+  invokeFileListChange(fileList) {
185
+    const { limitOne, handleChangeFileList } = this.props;
186
+    handleChangeFileList(fileList);
187
+    this.setState({ fileList });
188
+    if (limitOne && isMobile) {
189
+      const file = fileList[0];
190
+      if (
191
+        file &&
192
+        file.status === "done" &&
193
+        !file.thumbUrl.includes("data:image")
194
+      ) {
195
+        this.setState({ uploadVisible: false });
196
+      }
197
+    }
165
   }
198
   }
166
 
199
 
167
   /**
200
   /**
197
             type: file.type,
230
             type: file.type,
198
             uid: new Date().valueOf()
231
             uid: new Date().valueOf()
199
           });
232
           });
200
-          this.props.handleChangeFileList(fileList);
201
-          this.setState({
202
-            fileList
203
-          });
233
+          this.invokeFileListChange(fileList);
204
         })
234
         })
205
         .catch(e => {
235
         .catch(e => {
206
           const msg = e.message || ERROR_DEFAULT;
236
           const msg = e.message || ERROR_DEFAULT;
294
     return true;
324
     return true;
295
   }
325
   }
296
 
326
 
327
+  /**
328
+   *  **处理Enter事件**
329
+   *  1. `allowEnterSubmit`为true时enter触发submit事件
330
+   *  2. `e.preventDefault`为了防止enter事件后仍触发换行
331
+   *  3. enter事件开启后,仍可以用`shift + enter`触发换行
332
+   *  -- evo 20200222
333
+   */
334
+  handlePressEnter(e) {
335
+    if (this.props.allowEnterSubmit) {
336
+      if (!e.shiftKey) {
337
+        e.preventDefault();
338
+        this.handleSubmit();
339
+      }
340
+    }
341
+  }
342
+
297
   render() {
343
   render() {
298
     const {
344
     const {
299
       value,
345
       value,
326
     const isLogin =
372
     const isLogin =
327
       app.currentUser &&
373
       app.currentUser &&
328
       (app.currentUser.user_id > 0 || app.currentUser.id > 0);
374
       (app.currentUser.user_id > 0 || app.currentUser.id > 0);
375
+
329
     return (
376
     return (
330
       <div className="comment-editor-container" onPaste={this.handlePaste}>
377
       <div className="comment-editor-container" onPaste={this.handlePaste}>
331
         {isLogin ? (
378
         {isLogin ? (
339
             <div className="comment-editor">
386
             <div className="comment-editor">
340
               <TextArea
387
               <TextArea
341
                 value={inputValue}
388
                 value={inputValue}
342
-                onChange={e => this.handleChange(e.target.value)}
389
+                onChange={e => {
390
+                  this.handleChange(e.target.value);
391
+                }}
343
                 rows={rows}
392
                 rows={rows}
344
                 placeholder={placeholder}
393
                 placeholder={placeholder}
345
                 autoFocus={autoFocus}
394
                 autoFocus={autoFocus}
395
+                onPressEnter={this.handlePressEnter}
346
               />
396
               />
347
 
397
 
348
               <div className="comment-toolbar">
398
               <div className="comment-toolbar">
390
                         });
440
                         });
391
                       }}
441
                       }}
392
                       content={
442
                       content={
393
-                        <div
394
-                          style={{
395
-                            width: 336, // 一行显示3张
396
-                            minHeight: 100,
397
-                            margin: "0 auto"
398
-                          }}
399
-                        >
443
+                        <div className="comment-img-popover">
400
                           <Upload
444
                           <Upload
401
                             onRef={node => (this.uploadRef = node)}
445
                             onRef={node => (this.uploadRef = node)}
402
                             multiple={multiple}
446
                             multiple={multiple}
411
                         </div>
455
                         </div>
412
                       }
456
                       }
413
                       title={
457
                       title={
414
-                        <div style={{ margin: "5px auto" }}>
458
+                        <div className="comment-img-title">
415
                           <span>
459
                           <span>
416
                             {intl.get("editor.uploadTip")}
460
                             {intl.get("editor.uploadTip")}
417
                             {maxUpload >= 2 ? (
461
                             {maxUpload >= 2 ? (
418
-                              <span style={{ color: "#666", fontWeight: 400 }}>
462
+                              <span className="comment-img-title-counter">
419
                                 {intl.get("editor.uploadCount", {
463
                                 {intl.get("editor.uploadCount", {
420
                                   count: maxUpload - uploadFileList.length
464
                                   count: maxUpload - uploadFileList.length
421
                                 })}
465
                                 })}
503
   button: PropTypes.node,
547
   button: PropTypes.node,
504
   emojiToolIcon: PropTypes.node,
548
   emojiToolIcon: PropTypes.node,
505
   imageToolIcon: PropTypes.node,
549
   imageToolIcon: PropTypes.node,
550
+  uploadDefaultShow: PropTypes.bool,
506
   showError: PropTypes.bool,
551
   showError: PropTypes.bool,
507
   onError: PropTypes.func,
552
   onError: PropTypes.func,
508
-  maxLength: PropTypes.number
553
+  maxLength: PropTypes.number,
554
+  // Enter事件相关
555
+  allowEnterSubmit: PropTypes.bool,
556
+  // 私信仅允许选中一个,此处可以优化为通用的limit
557
+  limitOne: PropTypes.bool
509
 };
558
 };
510
 
559
 
511
 Editor.defaultProps = {
560
 Editor.defaultProps = {
513
   // placeholder: "说点什么吧",
562
   // placeholder: "说点什么吧",
514
   showEmoji: true,
563
   showEmoji: true,
515
   showUpload: true,
564
   showUpload: true,
565
+  uploadDefaultShow: true,
516
   multiple: true,
566
   multiple: true,
517
   emojiPopoverPlacement: "bottomLeft",
567
   emojiPopoverPlacement: "bottomLeft",
518
   closeUploadWhenBlur: false,
568
   closeUploadWhenBlur: false,
525
   showError: true,
575
   showError: true,
526
   maxLength: 5000,
576
   maxLength: 5000,
527
   app: {},
577
   app: {},
528
-  handleChangeFileList: () => {}
578
+  handleChangeFileList: () => {},
579
+  // Enter事件相关
580
+  allowEnterSubmit: false,
581
+  limitOne: false
529
 };
582
 };
530
 
583
 
531
 export default Comment(Editor);
584
 export default Comment(Editor);

+ 9
- 7
src/index.js View File

35
           fileList={this.state.fileList}
35
           fileList={this.state.fileList}
36
           value={this.state.value}
36
           value={this.state.value}
37
           onChange={value => {
37
           onChange={value => {
38
-            this.setState({
39
-              value
40
-            });
38
+            this.setState({ value });
41
           }}
39
           }}
42
           handleChangeFileList={fileList => {
40
           handleChangeFileList={fileList => {
43
-            console.log("----", fileList);
44
             this.setState({
41
             this.setState({
45
               fileList
42
               fileList
46
             });
43
             });
47
           }}
44
           }}
45
+          allowEnterSubmit
46
+          limitOne
48
         />
47
         />
49
       </App>
48
       </App>
50
     );
49
     );
88
 if (process.env.NODE_ENV !== "production") {
87
 if (process.env.NODE_ENV !== "production") {
89
   renderComment({
88
   renderComment({
90
     id: "root-comment",
89
     id: "root-comment",
91
-    type: 1,
92
-    businessId: "test",
90
+    type: 3,
91
+    businessId: "5ea8320dedd68200018e733d",
93
     businessUserId: 4,
92
     businessUserId: 4,
94
     userId: 58297,
93
     userId: 58297,
95
     currentUser: {
94
     currentUser: {
142
       });
141
       });
143
     },
142
     },
144
     onCountChange: c => {
143
     onCountChange: c => {
145
-      console.log(c);
144
+      // console.log(c);
146
     },
145
     },
147
     onDelete: (type, data) => {
146
     onDelete: (type, data) => {
148
       console.log(type, data);
147
       console.log(type, data);
159
     sendMessage: id => {
158
     sendMessage: id => {
160
       console.log("sendMessage", id);
159
       console.log("sendMessage", id);
161
     },
160
     },
161
+    preRenderValue: value => {
162
+      return `${value}`;
163
+    },
162
     editorProps: {
164
     editorProps: {
163
       onCommentSuccess: data => {
165
       onCommentSuccess: data => {
164
         console.log(data);
166
         console.log(data);

+ 2
- 0
src/lang/en-US.js View File

12
     "Total {total, plural, =1 {one comment} other {# comments}}",
12
     "Total {total, plural, =1 {one comment} other {# comments}}",
13
   "comment.reply": "Reply",
13
   "comment.reply": "Reply",
14
   "comment.moreComment": "More comments",
14
   "comment.moreComment": "More comments",
15
+  "comment.filterSpeak": "Show imitations only",
16
+  "comment.speakComment": "Imitation audio",
15
 
17
 
16
   "reply.totalReply": "Total {total, plural, =1 {one reply} other {# replies}}",
18
   "reply.totalReply": "Total {total, plural, =1 {one reply} other {# replies}}",
17
   "reply.moreReply": "More replies",
19
   "reply.moreReply": "More replies",

+ 2
- 0
src/lang/zh-CN.js View File

11
   "comment.totalComment": "共{total}条评论",
11
   "comment.totalComment": "共{total}条评论",
12
   "comment.reply": "回复",
12
   "comment.reply": "回复",
13
   "comment.moreComment": "更多评论",
13
   "comment.moreComment": "更多评论",
14
+  "comment.filterSpeak": "只显示跟读",
15
+  "comment.speakComment": "跟读语音",
14
 
16
 
15
   "reply.totalReply": "共{total}条回复",
17
   "reply.totalReply": "共{total}条回复",
16
   "reply.moreReply": "更多回复",
18
   "reply.moreReply": "更多回复",

+ 5
- 0
src/utils.js View File

1
+/**
2
+ * 575: 为了和media query中的判断保持一致
3
+ */
4
+export const isMobile =
5
+  typeof window.orientation === "number" && document.body.offsetWidth < 575;