Browse Source

Merge branch 'develop' of Tzhx/comment into master

AdamFu 4 years ago
parent
commit
f79e8f9daf
41 changed files with 1067 additions and 445 deletions
  1. 4
    2
      lib/App.js
  2. 1
    1
      lib/App.js.map
  3. 30
    30
      lib/App.module.css
  4. 1
    1
      lib/Comment.js.map
  5. 1
    1
      lib/avatar.js.map
  6. 1
    1
      lib/axios.js.map
  7. 193
    0
      lib/components/AudioPlayer/index.js
  8. 1
    0
      lib/components/AudioPlayer/index.js.map
  9. 60
    0
      lib/components/AudioPlayer/index.less
  10. 49
    49
      lib/components/CommentBox/index.css
  11. 12
    0
      lib/components/CommentList/index.css
  12. 36
    5
      lib/components/CommentList/index.js
  13. 1
    1
      lib/components/CommentList/index.js.map
  14. 27
    0
      lib/components/ContentItem/index.css
  15. 23
    1
      lib/components/ContentItem/index.js
  16. 1
    1
      lib/components/ContentItem/index.js.map
  17. 39
    39
      lib/components/Editor/Emoji.css
  18. 1
    1
      lib/components/Editor/Upload.js.map
  19. 112
    112
      lib/components/ImagePreviewer/ImagePreviewer.css
  20. 117
    117
      lib/components/ImagePreviewer/ImagePreviewer.less
  21. 3
    3
      lib/components/ImagePreviewer/README.md
  22. 1
    1
      lib/components/RenderText/index.js.map
  23. 2
    2
      lib/index.js
  24. 1
    1
      lib/index.js.map
  25. 2
    0
      lib/lang/en-US.js
  26. 1
    1
      lib/lang/en-US.js.map
  27. 2
    0
      lib/lang/zh-CN.js
  28. 1
    1
      lib/lang/zh-CN.js.map
  29. 1
    1
      lib/registerServiceWorker.js.map
  30. 3
    3
      lib/version.json
  31. 1
    1
      package.json
  32. 2
    2
      src/App.js
  33. 119
    0
      src/components/AudioPlayer/index.js
  34. 60
    0
      src/components/AudioPlayer/index.less
  35. 12
    0
      src/components/CommentList/index.css
  36. 35
    6
      src/components/CommentList/index.js
  37. 27
    0
      src/components/ContentItem/index.css
  38. 78
    59
      src/components/ContentItem/index.js
  39. 2
    2
      src/index.js
  40. 2
    0
      src/lang/en-US.js
  41. 2
    0
      src/lang/zh-CN.js

+ 4
- 2
lib/App.js View File

@@ -278,7 +278,9 @@ var App = function (_Component) {
278 278
 
279 279
       var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
280 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 285
       var pageType = this.props.pageType;
284 286
 
@@ -289,7 +291,7 @@ var App = function (_Component) {
289 291
           businessId = _props.businessId,
290 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 295
         var _response$data = response.data,
294 296
             list = _response$data.list,
295 297
             page = _response$data.page,

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


+ 30
- 30
lib/App.module.css View File

@@ -1,30 +1,30 @@
1
-.comment {
2
-  width: 100%;
3
-  padding: 10px;
4
-  margin-bottom: 100px;
5
-}
6
-
7
-.comment .ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
8
-  position: fixed;
9
-  top: 45%;
10
-}
11
-
12
-.comment-header-tag {
13
-  border: 1px solid #cecece;
14
-  border-radius: 0;
15
-  color: #666;
16
-}
17
-
18
-.comment-header-tip {
19
-  color: #5198eb;
20
-  margin-right: 15px;
21
-  margin-left: 5px;
22
-}
23
-
24
-.comment-header-text {
25
-  color: #666;
26
-}
27
-
28
-.comment-img {
29
-  max-width: 100%;
30
-}
1
+.comment {
2
+  width: 100%;
3
+  padding: 10px;
4
+  margin-bottom: 100px;
5
+}
6
+
7
+.comment .ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
8
+  position: fixed;
9
+  top: 45%;
10
+}
11
+
12
+.comment-header-tag {
13
+  border: 1px solid #cecece;
14
+  border-radius: 0;
15
+  color: #666;
16
+}
17
+
18
+.comment-header-tip {
19
+  color: #5198eb;
20
+  margin-right: 15px;
21
+  margin-left: 5px;
22
+}
23
+
24
+.comment-header-text {
25
+  color: #666;
26
+}
27
+
28
+.comment-img {
29
+  max-width: 100%;
30
+}

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

@@ -1 +1 @@
1
-{"version":3,"sources":["../src/Comment.js"],"names":["Comment","CommentContext","React","createContext","Component","props","app"],"mappings":";;;;;;;;;QAKgBA,O,GAAAA,O;;AALhB;;;;;;AAEA,IAAMC,iBAAiBC,gBAAMC,aAAN,EAAvB;;AAEA;AACO,SAASH,OAAT,CAAiBI,SAAjB,EAA4B;AACjC;AACA,SAAO,UAASC,KAAT,EAAgB;AACrB;AACA;AACA,WACE;AAAC,oBAAD,CAAgB,QAAhB;AAAA;AACG;AAAA,eAAO,8BAAC,SAAD,eAAeA,KAAf,IAAsB,KAAKC,GAA3B,IAAP;AAAA;AADH,KADF;AAKD,GARD;AASD;;QAEQL,c,GAAAA,c;kBAEMD,O","file":"Comment.js","sourcesContent":["import React from \"react\";\n\nconst CommentContext = React.createContext();\n\n// This function takes a component...\nexport function Comment(Component) {\n  // ...and returns another component...\n  return function(props) {\n    // ... and renders the wrapped component with the context theme!\n    // Notice that we pass through any additional props as well\n    return (\n      <CommentContext.Consumer>\n        {app => <Component {...props} app={app} />}\n      </CommentContext.Consumer>\n    );\n  };\n}\n\nexport { CommentContext };\n\nexport default Comment;\n"]}
1
+{"version":3,"sources":["../src/Comment.js"],"names":["Comment","CommentContext","React","createContext","Component","props","app"],"mappings":";;;;;;;;;QAKgBA,O,GAAAA,O;;AALhB;;;;;;AAEA,IAAMC,iBAAiBC,gBAAMC,aAAN,EAAvB;;AAEA;AACO,SAASH,OAAT,CAAiBI,SAAjB,EAA4B;AACjC;AACA,SAAO,UAASC,KAAT,EAAgB;AACrB;AACA;AACA,WACE;AAAC,oBAAD,CAAgB,QAAhB;AAAA;AACG;AAAA,eAAO,8BAAC,SAAD,eAAeA,KAAf,IAAsB,KAAKC,GAA3B,IAAP;AAAA;AADH,KADF;AAKD,GARD;AASD;;QAEQL,c,GAAAA,c;kBAEMD,O","file":"Comment.js","sourcesContent":["import React from \"react\";\r\n\r\nconst CommentContext = React.createContext();\r\n\r\n// This function takes a component...\r\nexport function Comment(Component) {\r\n  // ...and returns another component...\r\n  return function(props) {\r\n    // ... and renders the wrapped component with the context theme!\r\n    // Notice that we pass through any additional props as well\r\n    return (\r\n      <CommentContext.Consumer>\r\n        {app => <Component {...props} app={app} />}\r\n      </CommentContext.Consumer>\r\n    );\r\n  };\r\n}\r\n\r\nexport { CommentContext };\r\n\r\nexport default Comment;\r\n"]}

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


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

@@ -1 +1 @@
1
-{"version":3,"sources":["../src/axios.js"],"names":["axios","defaults","withCredentials","headers","common"],"mappings":";;;;;;AAAA;;;;;;AAEAA,gBAAMC,QAAN,CAAeC,eAAf,GAAiC,IAAjC;AACAF,gBAAMC,QAAN,CAAeE,OAAf,CAAuBC,MAAvB,CAA8B,eAA9B,IAAiD,SAAjD;;kBAEeJ,e","file":"axios.js","sourcesContent":["import axios from \"axios\";\n\naxios.defaults.withCredentials = true;\naxios.defaults.headers.common[\"Authorization\"] = \"Bearer \";\n\nexport default axios;\n"]}
1
+{"version":3,"sources":["../src/axios.js"],"names":["axios","defaults","withCredentials","headers","common"],"mappings":";;;;;;AAAA;;;;;;AAEAA,gBAAMC,QAAN,CAAeC,eAAf,GAAiC,IAAjC;AACAF,gBAAMC,QAAN,CAAeE,OAAf,CAAuBC,MAAvB,CAA8B,eAA9B,IAAiD,SAAjD;;kBAEeJ,e","file":"axios.js","sourcesContent":["import axios from \"axios\";\r\n\r\naxios.defaults.withCredentials = true;\r\naxios.defaults.headers.common[\"Authorization\"] = \"Bearer \";\r\n\r\nexport default axios;\r\n"]}

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

@@ -0,0 +1,193 @@
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
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
40
+
41
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
42
+
43
+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; }
44
+
45
+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; }
46
+
47
+_dayjs2.default.extend(_duration2.default);
48
+_dayjs2.default.extend(_utc2.default);
49
+
50
+var AudioPlayer = function (_React$Component) {
51
+  _inherits(AudioPlayer, _React$Component);
52
+
53
+  function AudioPlayer(props) {
54
+    _classCallCheck(this, AudioPlayer);
55
+
56
+    var _this = _possibleConstructorReturn(this, (AudioPlayer.__proto__ || Object.getPrototypeOf(AudioPlayer)).call(this, props));
57
+
58
+    _this.state = {
59
+      duration: 0,
60
+      currentDuration: 0,
61
+      isPlaying: false
62
+    };
63
+    return _this;
64
+  }
65
+
66
+  _createClass(AudioPlayer, [{
67
+    key: "componentDidMount",
68
+    value: function componentDidMount() {
69
+      var _this2 = this;
70
+
71
+      if (this.player) {
72
+        var src = this.props.src;
73
+
74
+        this.player.oncanplay = function (event) {
75
+          return _this2.setState({ duration: event.target.duration });
76
+        };
77
+        this.player.onended = function () {
78
+          return _this2.setState({ isPlaying: false, currentDuration: 0 });
79
+        };
80
+        this.player.onpause = function () {
81
+          return _this2.setState({ isPlaying: false });
82
+        };
83
+        this.player.onplay = function () {
84
+          return _this2.setState({ isPlaying: true });
85
+        };
86
+        this.player.ontimeupdate = function (event) {
87
+          return _this2.setState({ currentDuration: event.target.currentTime });
88
+        };
89
+        this.player.src = src;
90
+      }
91
+    }
92
+  }, {
93
+    key: "componentDidUpdate",
94
+    value: function componentDidUpdate(prevProps) {
95
+      var _this3 = this;
96
+
97
+      if (this.props.src !== prevProps.src) {
98
+        this.player.oncanplay = function (event) {
99
+          return _this3.setState({ duration: event.target.duration });
100
+        };
101
+        this.player.onended = function () {
102
+          return _this3.setState({ isPlaying: false, currentDuration: 0 });
103
+        };
104
+        this.player.onpause = function () {
105
+          return _this3.setState({ isPlaying: false });
106
+        };
107
+        this.player.onplay = function () {
108
+          return _this3.setState({ isPlaying: true });
109
+        };
110
+        this.player.ontimeupdate = function (event) {
111
+          return _this3.setState({ currentDuration: event.target.currentTime });
112
+        };
113
+        this.player.src = this.props.src;
114
+      }
115
+    }
116
+  }, {
117
+    key: "clickPlayOrPause",
118
+    value: function clickPlayOrPause(isPlaying) {
119
+      if (!this.player) {
120
+        return;
121
+      }
122
+      if (isPlaying) {
123
+        this.player.pause();
124
+      } else {
125
+        // 如果是播放,先暂停其他的播放
126
+        var playerList = document.getElementsByTagName("audio");
127
+        if (playerList) {
128
+          Array.from(playerList).forEach(function (player) {
129
+            if (!player.paused) {
130
+              player.pause();
131
+            }
132
+          });
133
+        }
134
+        this.player.play();
135
+      }
136
+    }
137
+  }, {
138
+    key: "render",
139
+    value: function render() {
140
+      var _this4 = this;
141
+
142
+      var _state = this.state,
143
+          currentDuration = _state.currentDuration,
144
+          isPlaying = _state.isPlaying,
145
+          duration = _state.duration;
146
+
147
+      return _react2.default.createElement(
148
+        "div",
149
+        { className: "comment-item-speak-audio-container" },
150
+        _react2.default.createElement("audio", {
151
+          ref: function ref(_ref) {
152
+            _this4.player = _ref;
153
+          },
154
+          style: { display: "none" }
155
+        }),
156
+        _react2.default.createElement(
157
+          "span",
158
+          {
159
+            className: "icon",
160
+            onClick: function onClick() {
161
+              _this4.clickPlayOrPause(isPlaying);
162
+            }
163
+          },
164
+          isPlaying ? _react2.default.createElement(_icon2.default, { className: "pause", type: "pause" }) : _react2.default.createElement("i", { className: "schedule schedule-icon_image_audio" })
165
+        ),
166
+        _react2.default.createElement(_slider2.default, {
167
+          step: 0.001,
168
+          className: "slider",
169
+          tooltipVisible: false,
170
+          value: currentDuration,
171
+          max: duration,
172
+          onChange: function onChange(value) {
173
+            if (_this4.player) {
174
+              _this4.player.currentTime = value;
175
+            }
176
+          }
177
+        }),
178
+        _react2.default.createElement(
179
+          "span",
180
+          { className: "time" },
181
+          _dayjs2.default.utc(_dayjs2.default.duration(currentDuration, "seconds").asMilliseconds()).format("mm:ss"),
182
+          "/",
183
+          _dayjs2.default.utc(_dayjs2.default.duration(duration, "seconds").asMilliseconds()).format("mm:ss")
184
+        )
185
+      );
186
+    }
187
+  }]);
188
+
189
+  return AudioPlayer;
190
+}(_react2.default.Component);
191
+
192
+exports.default = AudioPlayer;
193
+//# sourceMappingURL=index.js.map

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


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

@@ -0,0 +1,60 @@
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
+  .slider {
23
+    margin: 0 0 0 16px;
24
+    width: 195px;
25
+
26
+    :global {
27
+      .ant-slider-rail {
28
+        background-color: #dfdfdf;
29
+      }
30
+    }
31
+  }
32
+
33
+  .time {
34
+    margin-left: 14px;
35
+    color: #848484;
36
+    font-size: 12px;
37
+  }
38
+}
39
+
40
+@media screen and (max-width: 616px) and (min-width: 449px) {
41
+  .comment-item-speak-audio-container {
42
+    .slider {
43
+      width: 195px;
44
+    }
45
+  }
46
+}
47
+@media screen and (max-width: 449px) and (min-width: 365px) {
48
+  .comment-item-speak-audio-container {
49
+    .slider {
50
+      width: 114px;
51
+    }
52
+  }
53
+}
54
+@media screen and (max-width: 365px) {
55
+  .comment-item-speak-audio-container {
56
+    .slider {
57
+      width: 60px;
58
+    }
59
+  }
60
+}

+ 49
- 49
lib/components/CommentBox/index.css View File

@@ -1,49 +1,49 @@
1
-.comment-show-more {
2
-  color: #4a90e2;
3
-  text-align: center;
4
-  width: 100px;
5
-  background-color: #f8f8f8;
6
-  cursor: pointer;
7
-  padding: 10px;
8
-  margin: 0 auto;
9
-  transition: all 0.3s;
10
-}
11
-.comment-show-more:hover {
12
-  background-color: #f5f5f5;
13
-  color: #1890ff;
14
-}
15
-.comment-more-box {
16
-  text-align: center;
17
-  width: 90%;
18
-  margin-left: 50px;
19
-  margin-top: 10px;
20
-  height: 40px;
21
-  display: inline-block;
22
-}
23
-@media screen and (max-width: 616px) and (min-width: 449px) {
24
-  .comment-more-box {
25
-    text-align: center;
26
-    width: 85%;
27
-    margin-left: 50px;
28
-    height: 40px;
29
-    display: inline-block;
30
-  }
31
-}
32
-@media screen and (max-width: 449px) and (min-width: 365px) {
33
-  .comment-more-box {
34
-    text-align: center;
35
-    width: 80%;
36
-    margin-left: 50px;
37
-    height: 40px;
38
-    display: inline-block;
39
-  }
40
-}
41
-@media screen and (max-width: 365px) {
42
-  .comment-more-box {
43
-    text-align: center;
44
-    width: 75%;
45
-    margin-left: 50px;
46
-    height: 40px;
47
-    display: inline-block;
48
-  }
49
-}
1
+.comment-show-more {
2
+  color: #4a90e2;
3
+  text-align: center;
4
+  width: 100px;
5
+  background-color: #f8f8f8;
6
+  cursor: pointer;
7
+  padding: 10px;
8
+  margin: 0 auto;
9
+  transition: all 0.3s;
10
+}
11
+.comment-show-more:hover {
12
+  background-color: #f5f5f5;
13
+  color: #1890ff;
14
+}
15
+.comment-more-box {
16
+  text-align: center;
17
+  width: 90%;
18
+  margin-left: 50px;
19
+  margin-top: 10px;
20
+  height: 40px;
21
+  display: inline-block;
22
+}
23
+@media screen and (max-width: 616px) and (min-width: 449px) {
24
+  .comment-more-box {
25
+    text-align: center;
26
+    width: 85%;
27
+    margin-left: 50px;
28
+    height: 40px;
29
+    display: inline-block;
30
+  }
31
+}
32
+@media screen and (max-width: 449px) and (min-width: 365px) {
33
+  .comment-more-box {
34
+    text-align: center;
35
+    width: 80%;
36
+    margin-left: 50px;
37
+    height: 40px;
38
+    display: inline-block;
39
+  }
40
+}
41
+@media screen and (max-width: 365px) {
42
+  .comment-more-box {
43
+    text-align: center;
44
+    width: 75%;
45
+    margin-left: 50px;
46
+    height: 40px;
47
+    display: inline-block;
48
+  }
49
+}

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

@@ -17,3 +17,15 @@
17 17
 .comment-list-pagination {
18 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,6 +8,10 @@ var _spin = require("antd/es/spin");
8 8
 
9 9
 var _spin2 = _interopRequireDefault(_spin);
10 10
 
11
+var _checkbox = require("antd/es/checkbox");
12
+
13
+var _checkbox2 = _interopRequireDefault(_checkbox);
14
+
11 15
 var _pagination = require("antd/es/pagination");
12 16
 
13 17
 var _pagination2 = _interopRequireDefault(_pagination);
@@ -16,6 +20,8 @@ var _createClass = function () { function defineProperties(target, props) { for
16 20
 
17 21
 require("antd/es/spin/style");
18 22
 
23
+require("antd/es/checkbox/style");
24
+
19 25
 require("antd/es/pagination/style");
20 26
 
21 27
 var _react = require("react");
@@ -52,7 +58,9 @@ var CommentList = function (_Component) {
52 58
 
53 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 64
     return _this;
57 65
   }
58 66
 
@@ -74,6 +82,7 @@ var CommentList = function (_Component) {
74 82
           sGetComment = _props$app.sGetComment,
75 83
           onPageChange = _props$app.onPageChange,
76 84
           onGetMoreBtnClick = _props$app.onGetMoreBtnClick;
85
+      var filterSpeak = this.state.filterSpeak;
77 86
 
78 87
       if (pageType === "slice") {
79 88
         // 截断多余评论,通过点击查看更多跳转
@@ -93,7 +102,7 @@ var CommentList = function (_Component) {
93 102
             {
94 103
               className: "comment-list-show-more",
95 104
               onClick: function onClick() {
96
-                sGetComment({ page: page + 1 });
105
+                sGetComment({ page: page + 1, filterSpeak: filterSpeak });
97 106
                 onPageChange(page + 1);
98 107
               }
99 108
             },
@@ -115,7 +124,7 @@ var CommentList = function (_Component) {
115 124
             current: page,
116 125
             total: total,
117 126
             onChange: function onChange(p) {
118
-              sGetComment({ page: p });
127
+              sGetComment({ page: p, filterSpeak: filterSpeak });
119 128
               onPageChange(p);
120 129
             }
121 130
           })
@@ -125,10 +134,15 @@ var CommentList = function (_Component) {
125 134
   }, {
126 135
     key: "render",
127 136
     value: function render() {
137
+      var _this2 = this;
138
+
128 139
       var _props$app2 = this.props.app,
129 140
           list = _props$app2.list,
130 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 148
       var spinning = Boolean(loading.sGetComment || loading.sCommentFavor || loading.sReplyFavor);
@@ -141,7 +155,24 @@ var CommentList = function (_Component) {
141 155
           _react2.default.createElement(
142 156
             "div",
143 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 177
           list.map(function (item) {
147 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,6 +33,21 @@
33 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 51
 .comment-item-bottom {
37 52
   display: flex;
38 53
   justify-content: flex-end;
@@ -156,6 +171,10 @@
156 171
     width: 85%;
157 172
     margin-left: 10px;
158 173
   }
174
+
175
+  .comment-item-speak-audio-wrapper {
176
+    width: 350px;
177
+  }
159 178
 }
160 179
 
161 180
 @media screen and (max-width: 449px) and (min-width: 365px) {
@@ -164,6 +183,10 @@
164 183
     width: 80%;
165 184
     margin-left: 10px;
166 185
   }
186
+
187
+  .comment-item-speak-audio-wrapper {
188
+    width: 266px;
189
+  }
167 190
 }
168 191
 
169 192
 @media screen and (max-width: 365px) {
@@ -172,6 +195,10 @@
172 195
     width: 75%;
173 196
     margin-left: 10px;
174 197
   }
198
+
199
+  .comment-item-speak-audio-wrapper {
200
+    width: 218px;
201
+  }
175 202
 }
176 203
 
177 204
 @media (max-width: 575px) {

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

@@ -80,6 +80,10 @@ var _ImagePreviewer = require("../ImagePreviewer/ImagePreviewer");
80 80
 
81 81
 var _ImagePreviewer2 = _interopRequireDefault(_ImagePreviewer);
82 82
 
83
+var _AudioPlayer = require("../AudioPlayer");
84
+
85
+var _AudioPlayer2 = _interopRequireDefault(_AudioPlayer);
86
+
83 87
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
84 88
 
85 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,6 +201,8 @@ var CommentItem = function (_Component) {
197 201
           app = _props.app,
198 202
           user_id = _props.user_id,
199 203
           page = _props.page;
204
+      var medias = content.medias,
205
+          isSpeak = content.is_speak;
200 206
       var _props$app = this.props.app,
201 207
           locale = _props$app.locale,
202 208
           showHoverCard = _props$app.showHoverCard,
@@ -295,6 +301,22 @@ var CommentItem = function (_Component) {
295 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 320
           // image为空时不渲染comment-item-image
299 321
           imageList.length > 0 && imageList[0] !== "" && _react2.default.createElement(
300 322
             "div",
@@ -351,7 +373,7 @@ var CommentItem = function (_Component) {
351 373
                 showReply ? _react2.default.createElement(_icon2.default, { type: "up" }) : _react2.default.createElement(_icon2.default, { type: "down" })
352 374
               )
353 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 377
               className: "comment-item-edit",
356 378
               onClick: function onClick() {
357 379
                 return _this2.props.app.handleEdit({

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


+ 39
- 39
lib/components/Editor/Emoji.css View File

@@ -1,39 +1,39 @@
1
-.emoji .item {
2
-  float: left;
3
-  width: 40px;
4
-  height: 40px;
5
-  cursor: pointer;
6
-  white-space: nowrap;
7
-  /* this is required unless you put the helper span closely near the img */
8
-  text-align: center;
9
-  margin: 0;
10
-}
11
-
12
-.emoji .item .helper {
13
-  display: inline-block;
14
-  height: 100%;
15
-  vertical-align: middle;
16
-}
17
-
18
-.emoji .item img {
19
-  margin: 0 auto;
20
-  vertical-align: middle;
21
-  padding: 3px;
22
-}
23
-
24
-.emoji .item img:hover {
25
-  border: 1px solid #40a9ff;
26
-}
27
-
28
-.ant-carousel .slick-dots {
29
-  height: 5px;
30
-}
31
-
32
-.ant-carousel .slick-dots li button {
33
-  height: 5px;
34
-  width: 25px;
35
-}
36
-
37
-.ant-carousel .slick-dots li.slick-active button {
38
-  width: 32px;
39
-}
1
+.emoji .item {
2
+  float: left;
3
+  width: 40px;
4
+  height: 40px;
5
+  cursor: pointer;
6
+  white-space: nowrap;
7
+  /* this is required unless you put the helper span closely near the img */
8
+  text-align: center;
9
+  margin: 0;
10
+}
11
+
12
+.emoji .item .helper {
13
+  display: inline-block;
14
+  height: 100%;
15
+  vertical-align: middle;
16
+}
17
+
18
+.emoji .item img {
19
+  margin: 0 auto;
20
+  vertical-align: middle;
21
+  padding: 3px;
22
+}
23
+
24
+.emoji .item img:hover {
25
+  border: 1px solid #40a9ff;
26
+}
27
+
28
+.ant-carousel .slick-dots {
29
+  height: 5px;
30
+}
31
+
32
+.ant-carousel .slick-dots li button {
33
+  height: 5px;
34
+  width: 25px;
35
+}
36
+
37
+.ant-carousel .slick-dots li.slick-active button {
38
+  width: 32px;
39
+}

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


+ 112
- 112
lib/components/ImagePreviewer/ImagePreviewer.css View File

@@ -1,112 +1,112 @@
1
-.container .toolbar {
2
-  border-bottom: 1px solid #f2f2f2;
3
-  margin: 0 5px;
4
-  padding-bottom: 10px;
5
-}
6
-.container .toolbar .button {
7
-  padding: 5px 10px;
8
-  cursor: pointer;
9
-}
10
-.container .toolbar .button.reversal i {
11
-  transform: rotate3d(0, 1, 0, 180deg);
12
-}
13
-.container .pictureWrapper {
14
-  position: relative;
15
-  text-align: center;
16
-  margin-top: 15px;
17
-  padding: 0 5px;
18
-}
19
-.container .pictureWrapper .tools {
20
-  position: absolute;
21
-  left: 0;
22
-  top: 0;
23
-  bottom: 0;
24
-  right: 0;
25
-}
26
-.container .pictureWrapper .tools .item {
27
-  width: 33%;
28
-  display: inline-block;
29
-  height: 100%;
30
-}
31
-.container .pictureWrapper .tools .item.left {
32
-  cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/73_yM_fx-.undefined"),
33
-    auto;
34
-}
35
-.container .pictureWrapper .tools .item.shrink {
36
-  cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/oGPpbJ7Rk.undefined"),
37
-    auto;
38
-}
39
-.container .pictureWrapper .tools .item.right {
40
-  cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined"),
41
-    auto;
42
-}
43
-.container .pictureWrapper .picture {
44
-  max-width: 100%;
45
-  max-height: 400px;
46
-}
47
-.container .pictureWrapper .prev {
48
-  position: absolute;
49
-  width: 50px;
50
-  background-color: transparent;
51
-  z-index: 100;
52
-  opacity: 0.5;
53
-  font-size: 50px;
54
-  text-align: left;
55
-  cursor: pointer;
56
-  left: 5px;
57
-  top: 0;
58
-  bottom: 0;
59
-}
60
-.container .pictureWrapper .prev:hover {
61
-  background-color: #fff;
62
-}
63
-.container .pictureWrapper .next {
64
-  position: absolute;
65
-  width: 50px;
66
-  background-color: transparent;
67
-  z-index: 100;
68
-  opacity: 0.5;
69
-  font-size: 50px;
70
-  text-align: left;
71
-  cursor: pointer;
72
-  right: 5px;
73
-  top: 0;
74
-  bottom: 0;
75
-}
76
-.container .pictureWrapper .next:hover {
77
-  background-color: #fff;
78
-}
79
-.container .pictureWrapper .middle {
80
-  position: absolute;
81
-  top: 50%;
82
-  transform: translateY(-50%);
83
-}
84
-.container .list {
85
-  font-size: 0;
86
-  margin-top: 25px;
87
-}
88
-.container .list .wrapper {
89
-  width: 11.1%;
90
-  height: 0;
91
-  padding-bottom: 11.1%;
92
-  position: relative;
93
-  display: inline-block;
94
-  border-radius: 5px;
95
-  border: 1px solid transparent;
96
-  opacity: 0.5;
97
-}
98
-.container .list .wrapper.active {
99
-  border: 1px solid #fc4747;
100
-  opacity: 1;
101
-}
102
-.container .list .wrapper .thumbnail {
103
-  position: absolute;
104
-  top: 5px;
105
-  bottom: 5px;
106
-  left: 5px;
107
-  right: 5px;
108
-  background-size: cover;
109
-  background-position: center;
110
-  border-radius: 5px;
111
-  cursor: pointer;
112
-}
1
+.container .toolbar {
2
+  border-bottom: 1px solid #f2f2f2;
3
+  margin: 0 5px;
4
+  padding-bottom: 10px;
5
+}
6
+.container .toolbar .button {
7
+  padding: 5px 10px;
8
+  cursor: pointer;
9
+}
10
+.container .toolbar .button.reversal i {
11
+  transform: rotate3d(0, 1, 0, 180deg);
12
+}
13
+.container .pictureWrapper {
14
+  position: relative;
15
+  text-align: center;
16
+  margin-top: 15px;
17
+  padding: 0 5px;
18
+}
19
+.container .pictureWrapper .tools {
20
+  position: absolute;
21
+  left: 0;
22
+  top: 0;
23
+  bottom: 0;
24
+  right: 0;
25
+}
26
+.container .pictureWrapper .tools .item {
27
+  width: 33%;
28
+  display: inline-block;
29
+  height: 100%;
30
+}
31
+.container .pictureWrapper .tools .item.left {
32
+  cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/73_yM_fx-.undefined"),
33
+    auto;
34
+}
35
+.container .pictureWrapper .tools .item.shrink {
36
+  cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/oGPpbJ7Rk.undefined"),
37
+    auto;
38
+}
39
+.container .pictureWrapper .tools .item.right {
40
+  cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined"),
41
+    auto;
42
+}
43
+.container .pictureWrapper .picture {
44
+  max-width: 100%;
45
+  max-height: 400px;
46
+}
47
+.container .pictureWrapper .prev {
48
+  position: absolute;
49
+  width: 50px;
50
+  background-color: transparent;
51
+  z-index: 100;
52
+  opacity: 0.5;
53
+  font-size: 50px;
54
+  text-align: left;
55
+  cursor: pointer;
56
+  left: 5px;
57
+  top: 0;
58
+  bottom: 0;
59
+}
60
+.container .pictureWrapper .prev:hover {
61
+  background-color: #fff;
62
+}
63
+.container .pictureWrapper .next {
64
+  position: absolute;
65
+  width: 50px;
66
+  background-color: transparent;
67
+  z-index: 100;
68
+  opacity: 0.5;
69
+  font-size: 50px;
70
+  text-align: left;
71
+  cursor: pointer;
72
+  right: 5px;
73
+  top: 0;
74
+  bottom: 0;
75
+}
76
+.container .pictureWrapper .next:hover {
77
+  background-color: #fff;
78
+}
79
+.container .pictureWrapper .middle {
80
+  position: absolute;
81
+  top: 50%;
82
+  transform: translateY(-50%);
83
+}
84
+.container .list {
85
+  font-size: 0;
86
+  margin-top: 25px;
87
+}
88
+.container .list .wrapper {
89
+  width: 11.1%;
90
+  height: 0;
91
+  padding-bottom: 11.1%;
92
+  position: relative;
93
+  display: inline-block;
94
+  border-radius: 5px;
95
+  border: 1px solid transparent;
96
+  opacity: 0.5;
97
+}
98
+.container .list .wrapper.active {
99
+  border: 1px solid #fc4747;
100
+  opacity: 1;
101
+}
102
+.container .list .wrapper .thumbnail {
103
+  position: absolute;
104
+  top: 5px;
105
+  bottom: 5px;
106
+  left: 5px;
107
+  right: 5px;
108
+  background-size: cover;
109
+  background-position: center;
110
+  border-radius: 5px;
111
+  cursor: pointer;
112
+}

+ 117
- 117
lib/components/ImagePreviewer/ImagePreviewer.less View File

@@ -1,117 +1,117 @@
1
-.comment-image-preview-container {
2
-  .toolbar {
3
-    border-bottom: 1px solid #f2f2f2;
4
-    margin: 0 5px;
5
-    padding-bottom: 10px;
6
-    .button {
7
-      padding: 5px 10px;
8
-      cursor: pointer;
9
-      &.reversal i {
10
-        transform: rotate3d(0, 1, 0, 180deg);
11
-      }
12
-    }
13
-  }
14
-  .pictureWrapper {
15
-    position: relative; // width: 100%;
16
-    // padding-bottom: 68%;
17
-    // height: 0;
18
-    text-align: center;
19
-    margin-top: 15px;
20
-    padding: 0 5px;
21
-    .tools {
22
-      position: absolute;
23
-      left: 0;
24
-      top: 0;
25
-      bottom: 0;
26
-      right: 0;
27
-      .item {
28
-        width: 33%;
29
-        display: inline-block;
30
-        height: 100%;
31
-        &.left {
32
-          cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/73_yM_fx-.undefined"),
33
-            auto;
34
-        }
35
-        &.shrink {
36
-          cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/oGPpbJ7Rk.undefined"),
37
-            auto;
38
-        }
39
-        &.right {
40
-          cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined"),
41
-            auto;
42
-        }
43
-      }
44
-    }
45
-    .picture {
46
-      max-width: 100%;
47
-      max-height: 400px;
48
-    }
49
-    .tool() {
50
-      position: absolute;
51
-      width: 50px;
52
-      background-color: transparent;
53
-      z-index: 100;
54
-      opacity: 0.5;
55
-      font-size: 50px;
56
-      text-align: left;
57
-      cursor: pointer;
58
-      &:hover {
59
-        background-color: #fff;
60
-      }
61
-    }
62
-    .prev {
63
-      .tool();
64
-      left: 5px;
65
-      top: 0;
66
-      bottom: 0;
67
-    }
68
-    .next {
69
-      .tool();
70
-      right: 5px;
71
-      top: 0;
72
-      bottom: 0;
73
-    }
74
-    .middle {
75
-      position: absolute;
76
-      top: 50%;
77
-      transform: translateY(-50%);
78
-    } // .picture {
79
-    //     position: absolute;
80
-    //     bottom: 0;
81
-    //     top: 0;
82
-    //     left: 5px;
83
-    //     right: 5px;
84
-    //     background-size: contain;
85
-    //     background-position: center;
86
-    // }
87
-  }
88
-  .list {
89
-    font-size: 0;
90
-    margin-top: 25px;
91
-    .wrapper {
92
-      width: 11.1%;
93
-      height: 0;
94
-      padding-bottom: 11.1%;
95
-      position: relative;
96
-      display: inline-block;
97
-      border-radius: 5px;
98
-      border: 1px solid transparent;
99
-      opacity: 0.5;
100
-      &.active {
101
-        border: 1px solid #fc4747;
102
-        opacity: 1;
103
-      }
104
-      .thumbnail {
105
-        position: absolute;
106
-        top: 5px;
107
-        bottom: 5px;
108
-        left: 5px;
109
-        right: 5px;
110
-        background-size: cover;
111
-        background-position: center;
112
-        border-radius: 5px;
113
-        cursor: pointer;
114
-      }
115
-    }
116
-  }
117
-}
1
+.comment-image-preview-container {
2
+  .toolbar {
3
+    border-bottom: 1px solid #f2f2f2;
4
+    margin: 0 5px;
5
+    padding-bottom: 10px;
6
+    .button {
7
+      padding: 5px 10px;
8
+      cursor: pointer;
9
+      &.reversal i {
10
+        transform: rotate3d(0, 1, 0, 180deg);
11
+      }
12
+    }
13
+  }
14
+  .pictureWrapper {
15
+    position: relative; // width: 100%;
16
+    // padding-bottom: 68%;
17
+    // height: 0;
18
+    text-align: center;
19
+    margin-top: 15px;
20
+    padding: 0 5px;
21
+    .tools {
22
+      position: absolute;
23
+      left: 0;
24
+      top: 0;
25
+      bottom: 0;
26
+      right: 0;
27
+      .item {
28
+        width: 33%;
29
+        display: inline-block;
30
+        height: 100%;
31
+        &.left {
32
+          cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/73_yM_fx-.undefined"),
33
+            auto;
34
+        }
35
+        &.shrink {
36
+          cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/oGPpbJ7Rk.undefined"),
37
+            auto;
38
+        }
39
+        &.right {
40
+          cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined"),
41
+            auto;
42
+        }
43
+      }
44
+    }
45
+    .picture {
46
+      max-width: 100%;
47
+      max-height: 400px;
48
+    }
49
+    .tool() {
50
+      position: absolute;
51
+      width: 50px;
52
+      background-color: transparent;
53
+      z-index: 100;
54
+      opacity: 0.5;
55
+      font-size: 50px;
56
+      text-align: left;
57
+      cursor: pointer;
58
+      &:hover {
59
+        background-color: #fff;
60
+      }
61
+    }
62
+    .prev {
63
+      .tool();
64
+      left: 5px;
65
+      top: 0;
66
+      bottom: 0;
67
+    }
68
+    .next {
69
+      .tool();
70
+      right: 5px;
71
+      top: 0;
72
+      bottom: 0;
73
+    }
74
+    .middle {
75
+      position: absolute;
76
+      top: 50%;
77
+      transform: translateY(-50%);
78
+    } // .picture {
79
+    //     position: absolute;
80
+    //     bottom: 0;
81
+    //     top: 0;
82
+    //     left: 5px;
83
+    //     right: 5px;
84
+    //     background-size: contain;
85
+    //     background-position: center;
86
+    // }
87
+  }
88
+  .list {
89
+    font-size: 0;
90
+    margin-top: 25px;
91
+    .wrapper {
92
+      width: 11.1%;
93
+      height: 0;
94
+      padding-bottom: 11.1%;
95
+      position: relative;
96
+      display: inline-block;
97
+      border-radius: 5px;
98
+      border: 1px solid transparent;
99
+      opacity: 0.5;
100
+      &.active {
101
+        border: 1px solid #fc4747;
102
+        opacity: 1;
103
+      }
104
+      .thumbnail {
105
+        position: absolute;
106
+        top: 5px;
107
+        bottom: 5px;
108
+        left: 5px;
109
+        right: 5px;
110
+        background-size: cover;
111
+        background-position: center;
112
+        border-radius: 5px;
113
+        cursor: pointer;
114
+      }
115
+    }
116
+  }
117
+}

+ 3
- 3
lib/components/ImagePreviewer/README.md View File

@@ -1,4 +1,4 @@
1
-+ big //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/QPI84fxmD.undefined
2
-+ small //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/oGPpbJ7Rk.undefined
3
-+ prev //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/73_yM_fx-.undefined
1
++ big //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/QPI84fxmD.undefined
2
++ small //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/oGPpbJ7Rk.undefined
3
++ prev //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/73_yM_fx-.undefined
4 4
 + next //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined

+ 1
- 1
lib/components/RenderText/index.js.map View File

@@ -1 +1 @@
1
-{"version":3,"sources":["../../../src/components/RenderText/index.js"],"names":["App","__html","text"],"mappings":";;;;;;AAAA;;;;AACA;;;;AAEA,IAAMA,MAAM,SAANA,GAAM;AAAA,SACV;AACE,eAAU,sBADZ;AAEE,6BAAyB;AACvBC,cAAQ,2BAAcC,IAAd;AADe;AAF3B,IADU;AAAA,CAAZ;;kBASeF,G","file":"index.js","sourcesContent":["import React from \"react\";\nimport { renderContent } from \"../../helper\";\n\nconst App = text => (\n  <div\n    className=\"comment-item-content\"\n    dangerouslySetInnerHTML={{\n      __html: renderContent(text)\n    }}\n  />\n);\n\nexport default App;\n"]}
1
+{"version":3,"sources":["../../../src/components/RenderText/index.js"],"names":["App","__html","text"],"mappings":";;;;;;AAAA;;;;AACA;;;;AAEA,IAAMA,MAAM,SAANA,GAAM;AAAA,SACV;AACE,eAAU,sBADZ;AAEE,6BAAyB;AACvBC,cAAQ,2BAAcC,IAAd;AADe;AAF3B,IADU;AAAA,CAAZ;;kBASeF,G","file":"index.js","sourcesContent":["import React from \"react\";\r\nimport { renderContent } from \"../../helper\";\r\n\r\nconst App = text => (\r\n  <div\r\n    className=\"comment-item-content\"\r\n    dangerouslySetInnerHTML={{\r\n      __html: renderContent(text)\r\n    }}\r\n  />\r\n);\r\n\r\nexport default App;\r\n"]}

+ 2
- 2
lib/index.js View File

@@ -122,8 +122,8 @@ window.renderComment = renderComment;
122 122
 if (process.env.NODE_ENV !== "production") {
123 123
   renderComment({
124 124
     id: "root-comment",
125
-    type: 1,
126
-    businessId: "test",
125
+    type: 3,
126
+    businessId: "5ea8320dedd68200018e733d",
127 127
     businessUserId: 4,
128 128
     userId: 58297,
129 129
     currentUser: {

+ 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,6 +16,8 @@ var USdata = {
16 16
   "comment.totalComment": "Total {total, plural, =1 {one comment} other {# comments}}",
17 17
   "comment.reply": "Reply",
18 18
   "comment.moreComment": "More comments",
19
+  "comment.filterSpeak": "Show imitations only",
20
+  "comment.speakComment": "Imitation audio",
19 21
 
20 22
   "reply.totalReply": "Total {total, plural, =1 {one reply} other {# replies}}",
21 23
   "reply.moreReply": "More replies",

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

@@ -1 +1 @@
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,6 +16,8 @@ var CNdata = {
16 16
   "comment.totalComment": "共{total}条评论",
17 17
   "comment.reply": "回复",
18 18
   "comment.moreComment": "更多评论",
19
+  "comment.filterSpeak": "只显示跟读",
20
+  "comment.speakComment": "跟读语音",
19 21
 
20 22
   "reply.totalReply": "共{total}条回复",
21 23
   "reply.moreReply": "更多回复",

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

@@ -1 +1 @@
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"]}

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


+ 3
- 3
lib/version.json View File

@@ -1,8 +1,8 @@
1 1
 {
2 2
     "name":       "comment",
3
-    "buildDate":  1582467062540,
3
+    "buildDate":  1588149501351,
4 4
     "version":    "1.0.4",
5
-    "numCommits": 204,
6
-    "hash":       "016964f",
5
+    "numCommits": 211,
6
+    "hash":       "7a67067",
7 7
     "dirty":      true
8 8
 }

+ 1
- 1
package.json View File

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

+ 2
- 2
src/App.js View File

@@ -190,13 +190,13 @@ class App extends Component {
190 190
   /**
191 191
    * 获取评论列表
192 192
    */
193
-  sGetComment({ page = 1 } = {}) {
193
+  sGetComment({ page = 1, filterSpeak = 0 } = {}) {
194 194
     const { pageType } = this.props;
195 195
     this.handleChangeLoading("sGetComment", true);
196 196
     const { API, type, businessId, limit } = this.props;
197 197
     this.axios
198 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 201
       .then(response => {
202 202
         const { list, page, total } = response.data;

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

@@ -0,0 +1,119 @@
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
+
8
+dayjs.extend(durationPlugin);
9
+dayjs.extend(utcPlugin);
10
+
11
+class AudioPlayer extends React.Component {
12
+  constructor(props) {
13
+    super(props);
14
+    this.state = {
15
+      duration: 0,
16
+      currentDuration: 0,
17
+      isPlaying: false
18
+    };
19
+  }
20
+
21
+  componentDidMount() {
22
+    if (this.player) {
23
+      const { src } = this.props;
24
+      this.player.oncanplay = event =>
25
+        this.setState({ duration: event.target.duration });
26
+      this.player.onended = () =>
27
+        this.setState({ isPlaying: false, currentDuration: 0 });
28
+      this.player.onpause = () => this.setState({ isPlaying: false });
29
+      this.player.onplay = () => this.setState({ isPlaying: true });
30
+      this.player.ontimeupdate = event =>
31
+        this.setState({ currentDuration: event.target.currentTime });
32
+      this.player.src = src;
33
+    }
34
+  }
35
+
36
+  componentDidUpdate(prevProps) {
37
+    if (this.props.src !== prevProps.src) {
38
+      this.player.oncanplay = event =>
39
+        this.setState({ duration: event.target.duration });
40
+      this.player.onended = () =>
41
+        this.setState({ isPlaying: false, currentDuration: 0 });
42
+      this.player.onpause = () => this.setState({ isPlaying: false });
43
+      this.player.onplay = () => this.setState({ isPlaying: true });
44
+      this.player.ontimeupdate = event =>
45
+        this.setState({ currentDuration: event.target.currentTime });
46
+      this.player.src = this.props.src;
47
+    }
48
+  }
49
+
50
+  clickPlayOrPause(isPlaying) {
51
+    if (!this.player) {
52
+      return;
53
+    }
54
+    if (isPlaying) {
55
+      this.player.pause();
56
+    } else {
57
+      // 如果是播放,先暂停其他的播放
58
+      const playerList = document.getElementsByTagName("audio");
59
+      if (playerList) {
60
+        Array.from(playerList).forEach(player => {
61
+          if (!player.paused) {
62
+            player.pause();
63
+          }
64
+        });
65
+      }
66
+      this.player.play();
67
+    }
68
+  }
69
+
70
+  render() {
71
+    const { currentDuration, isPlaying, duration } = this.state;
72
+    return (
73
+      <div className="comment-item-speak-audio-container">
74
+        {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
75
+        <audio
76
+          ref={ref => {
77
+            this.player = ref;
78
+          }}
79
+          style={{ display: "none" }}
80
+        />
81
+        <span
82
+          className="icon"
83
+          onClick={() => {
84
+            this.clickPlayOrPause(isPlaying);
85
+          }}
86
+        >
87
+          {isPlaying ? (
88
+            <Icon className="pause" type="pause" />
89
+          ) : (
90
+            <i className="schedule schedule-icon_image_audio" />
91
+          )}
92
+        </span>
93
+        <Slider
94
+          step={0.001}
95
+          className="slider"
96
+          tooltipVisible={false}
97
+          value={currentDuration}
98
+          max={duration}
99
+          onChange={value => {
100
+            if (this.player) {
101
+              this.player.currentTime = value;
102
+            }
103
+          }}
104
+        />
105
+        <span className="time">
106
+          {dayjs
107
+            .utc(dayjs.duration(currentDuration, "seconds").asMilliseconds())
108
+            .format("mm:ss")}
109
+          /
110
+          {dayjs
111
+            .utc(dayjs.duration(duration, "seconds").asMilliseconds())
112
+            .format("mm:ss")}
113
+        </span>
114
+      </div>
115
+    );
116
+  }
117
+}
118
+
119
+export default AudioPlayer;

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

@@ -0,0 +1,60 @@
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
+  .slider {
23
+    margin: 0 0 0 16px;
24
+    width: 195px;
25
+
26
+    :global {
27
+      .ant-slider-rail {
28
+        background-color: #dfdfdf;
29
+      }
30
+    }
31
+  }
32
+
33
+  .time {
34
+    margin-left: 14px;
35
+    color: #848484;
36
+    font-size: 12px;
37
+  }
38
+}
39
+
40
+@media screen and (max-width: 616px) and (min-width: 449px) {
41
+  .comment-item-speak-audio-container {
42
+    .slider {
43
+      width: 195px;
44
+    }
45
+  }
46
+}
47
+@media screen and (max-width: 449px) and (min-width: 365px) {
48
+  .comment-item-speak-audio-container {
49
+    .slider {
50
+      width: 114px;
51
+    }
52
+  }
53
+}
54
+@media screen and (max-width: 365px) {
55
+  .comment-item-speak-audio-container {
56
+    .slider {
57
+      width: 60px;
58
+    }
59
+  }
60
+}

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

@@ -17,3 +17,15 @@
17 17
 .comment-list-pagination {
18 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,5 +1,5 @@
1 1
 import React, { Component } from "react";
2
-import { Spin, Pagination } from "antd";
2
+import { Spin, Pagination, Checkbox } from "antd";
3 3
 import intl from "react-intl-universal";
4 4
 import Comment from "../../Comment";
5 5
 import CommentBox from "../CommentBox";
@@ -8,7 +8,9 @@ import "./index.css";
8 8
 class CommentList extends Component {
9 9
   constructor(props) {
10 10
     super(props);
11
-    this.state = {};
11
+    this.state = {
12
+      filterSpeak: 0
13
+    };
12 14
   }
13 15
 
14 16
   componentWillMount() {
@@ -27,6 +29,7 @@ class CommentList extends Component {
27 29
       onPageChange,
28 30
       onGetMoreBtnClick
29 31
     } = this.props.app;
32
+    const { filterSpeak } = this.state;
30 33
     if (pageType === "slice") {
31 34
       // 截断多余评论,通过点击查看更多跳转
32 35
       return (
@@ -40,7 +43,7 @@ class CommentList extends Component {
40 43
           <div
41 44
             className="comment-list-show-more"
42 45
             onClick={() => {
43
-              sGetComment({ page: page + 1 });
46
+              sGetComment({ page: page + 1, filterSpeak });
44 47
               onPageChange(page + 1);
45 48
             }}
46 49
           >
@@ -58,7 +61,7 @@ class CommentList extends Component {
58 61
             current={page}
59 62
             total={total}
60 63
             onChange={p => {
61
-              sGetComment({ page: p });
64
+              sGetComment({ page: p, filterSpeak });
62 65
               onPageChange(p);
63 66
             }}
64 67
           />
@@ -68,7 +71,14 @@ class CommentList extends Component {
68 71
   }
69 72
 
70 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 83
     const spinning = Boolean(
74 84
       loading.sGetComment || loading.sCommentFavor || loading.sReplyFavor
@@ -77,7 +87,26 @@ class CommentList extends Component {
77 87
       <div>
78 88
         <Spin spinning={spinning}>
79 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 110
           {list.map(item => (
82 111
             <CommentBox
83 112
               content={item}

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

@@ -33,6 +33,21 @@
33 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 51
 .comment-item-bottom {
37 52
   display: flex;
38 53
   justify-content: flex-end;
@@ -156,6 +171,10 @@
156 171
     width: 85%;
157 172
     margin-left: 10px;
158 173
   }
174
+
175
+  .comment-item-speak-audio-wrapper {
176
+    width: 350px;
177
+  }
159 178
 }
160 179
 
161 180
 @media screen and (max-width: 449px) and (min-width: 365px) {
@@ -164,6 +183,10 @@
164 183
     width: 80%;
165 184
     margin-left: 10px;
166 185
   }
186
+
187
+  .comment-item-speak-audio-wrapper {
188
+    width: 266px;
189
+  }
167 190
 }
168 191
 
169 192
 @media screen and (max-width: 365px) {
@@ -172,6 +195,10 @@
172 195
     width: 75%;
173 196
     margin-left: 10px;
174 197
   }
198
+
199
+  .comment-item-speak-audio-wrapper {
200
+    width: 218px;
201
+  }
175 202
 }
176 203
 
177 204
 @media (max-width: 575px) {

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

@@ -14,6 +14,7 @@ import { renderContent } from "../../helper";
14 14
 import { IMAGE_SPLIT } from "../../constant";
15 15
 import "./index.css";
16 16
 import ImagePreviewer from "../ImagePreviewer/ImagePreviewer";
17
+import AudioPlayer from "../AudioPlayer";
17 18
 
18 19
 // dayjs.locale("zh-cn");
19 20
 dayjs.extend(relativeTime);
@@ -103,6 +104,7 @@ class CommentItem extends Component {
103 104
       user_id,
104 105
       page
105 106
     } = this.props;
107
+    const { medias, is_speak: isSpeak } = content;
106 108
     const { locale, showHoverCard, showEdit } = this.props.app;
107 109
     const { showInput } = this.state;
108 110
     let newContent = content.content;
@@ -206,54 +208,66 @@ class CommentItem extends Component {
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 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 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 257
                         {/* <img src={item} alt={item} className="comment-img" /> */}
219 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 271
           <div className="comment-item-bottom">
258 272
             {content.reply_count ? (
259 273
               <div>
@@ -264,21 +278,23 @@ class CommentItem extends Component {
264 278
                 </a>
265 279
               </div>
266 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 298
             {app.userId === content.user_id && (
283 299
               <Popconfirm
284 300
                 // title="确定要删除吗?"
@@ -318,7 +334,10 @@ class CommentItem extends Component {
318 334
                 }
319 335
               />
320 336
             </div>
321
-            <span>&nbsp;{content.favor_count}</span>
337
+            <span>
338
+              &nbsp;
339
+              {content.favor_count}
340
+            </span>
322 341
             <div
323 342
               onClick={this.handleToggleInput}
324 343
               className="comment-item-reply"

+ 2
- 2
src/index.js View File

@@ -87,8 +87,8 @@ window.renderComment = renderComment;
87 87
 if (process.env.NODE_ENV !== "production") {
88 88
   renderComment({
89 89
     id: "root-comment",
90
-    type: 1,
91
-    businessId: "test",
90
+    type: 3,
91
+    businessId: "5ea8320dedd68200018e733d",
92 92
     businessUserId: 4,
93 93
     userId: 58297,
94 94
     currentUser: {

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

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

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

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