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
 
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,

+ 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
-.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
-{"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
-{"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

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

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
-.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
 .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


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

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
-.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
-.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
-+ 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
 + next //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined
4
 + next //links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined

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

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
 if (process.env.NODE_ENV !== "production") {
122
 if (process.env.NODE_ENV !== "production") {
123
   renderComment({
123
   renderComment({
124
     id: "root-comment",
124
     id: "root-comment",
125
-    type: 1,
126
-    businessId: "test",
125
+    type: 3,
126
+    businessId: "5ea8320dedd68200018e733d",
127
     businessUserId: 4,
127
     businessUserId: 4,
128
     userId: 58297,
128
     userId: 58297,
129
     currentUser: {
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
   "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"]}

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


+ 3
- 3
lib/version.json View File

1
 {
1
 {
2
     "name":       "comment",
2
     "name":       "comment",
3
-    "buildDate":  1582467062540,
3
+    "buildDate":  1588149501351,
4
     "version":    "1.0.4",
4
     "version":    "1.0.4",
5
-    "numCommits": 204,
6
-    "hash":       "016964f",
5
+    "numCommits": 211,
6
+    "hash":       "7a67067",
7
     "dirty":      true
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",

+ 2
- 2
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;

+ 119
- 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
+
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

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
 .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"

+ 2
- 2
src/index.js View File

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

+ 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": "更多回复",