Browse Source

增加图片预览器

narro 5 years ago
parent
commit
3ba30a7941

+ 68
- 37
lib/components/ContentItem/index.css View File

@@ -1,74 +1,105 @@
1 1
 .comment-item-box {
2
-  margin: 10px 0 0 0;
3
-  padding: 15px 5px 0 5px;
4
-  border-top: 1px solid #eee;
2
+    margin: 10px 0 0 0;
3
+    padding: 15px 5px 0 5px;
4
+    border-top: 1px solid #eee;
5 5
 }
6 6
 
7 7
 .comment-item-left {
8
-  display: inline-block;
9
-  vertical-align: top;
10
-  width: 40px;
8
+    display: inline-block;
9
+    vertical-align: top;
10
+    width: 40px;
11 11
 }
12 12
 
13 13
 .comment-item-right {
14
-  display: inline-block;
15
-  width: 90%;
16
-  margin-left: 10px;
17
-  margin-bottom: 20px;
14
+    display: inline-block;
15
+    width: 90%;
16
+    margin-left: 10px;
17
+    margin-bottom: 20px;
18 18
 }
19 19
 
20 20
 .comment-item-content {
21
-  margin: 10px 0;
22
-  word-break: break-all;
21
+    margin: 10px 0;
22
+    word-break: break-all;
23 23
 }
24 24
 
25 25
 .comment-item-bottom {
26
-  margin: 20px auto;
26
+    margin: 20px auto;
27 27
 }
28 28
 
29 29
 .comment-item-bottom-left {
30
-  float: left;
31
-  user-select: none;
30
+    float: left;
31
+    user-select: none;
32 32
 }
33 33
 
34 34
 .comment-item-bottom-right {
35
-  float: right;
36
-  margin-left: 5px;
37
-  cursor: pointer;
35
+    float: right;
36
+    margin-left: 5px;
37
+    cursor: pointer;
38 38
 }
39 39
 
40 40
 .comment-favor {
41
-  font-size: 20px;
41
+    font-size: 20px;
42 42
 }
43 43
 
44 44
 .comment-favored {
45
-  color: #4a90e2;
45
+    color: #4a90e2;
46
+}
47
+
48
+.comment-item-image {
49
+    width: 400px;
50
+    max-width: 100%;
51
+}
52
+
53
+.comment-item-image .comment-item-image-wrapper {
54
+    float: left;
55
+    width: 25%;
56
+    box-sizing: border-box;
57
+    padding: 5px;
58
+}
59
+
60
+.comment-item-image .comment-item-image-wrapper .comment-img-thumbnail {
61
+    background-position: center;
62
+    background-size: cover;
63
+    display: inline-block;
64
+    width: 100%;
65
+    height: 0;
66
+    padding-bottom: 100%;
67
+    border-radius: 5px;
68
+    cursor: url('//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/QPI84fxmD.undefined'), auto;
69
+}
70
+
71
+.comment-item-image .comment-item-image-wrapper .comment-img-divider {
72
+    display: inline-block;
73
+    width: 100%;
74
+    height: 0;
75
+    padding-bottom: 100%;
76
+    border-radius: 5px;
46 77
 }
47 78
 
48 79
 .comment-item-image .comment-img {
49
-  margin-right: 10px;
80
+    margin-right: 10px;
50 81
 }
51 82
 
52 83
 @media screen and (max-width: 616px) and (min-width: 449px) {
53
-  .comment-item-right {
54
-    display: inline-block;
55
-    width: 85%;
56
-    margin-left: 10px;
57
-  }
84
+    .comment-item-right {
85
+        display: inline-block;
86
+        width: 85%;
87
+        margin-left: 10px;
88
+    }
58 89
 }
59 90
 
60 91
 @media screen and (max-width: 449px) and (min-width: 365px) {
61
-  .comment-item-right {
62
-    display: inline-block;
63
-    width: 80%;
64
-    margin-left: 10px;
65
-  }
92
+    .comment-item-right {
93
+        display: inline-block;
94
+        width: 80%;
95
+        margin-left: 10px;
96
+    }
66 97
 }
67 98
 
68 99
 @media screen and (max-width: 365px) {
69
-  .comment-item-right {
70
-    display: inline-block;
71
-    width: 75%;
72
-    margin-left: 10px;
73
-  }
74
-}
100
+    .comment-item-right {
101
+        display: inline-block;
102
+        width: 75%;
103
+        margin-left: 10px;
104
+    }
105
+}

+ 74
- 9
lib/components/ContentItem/index.js View File

@@ -66,8 +66,14 @@ var _constant = require("../../constant");
66 66
 
67 67
 require("./index.css");
68 68
 
69
+var _ImagePreviewer = require("../ImagePreviewer/ImagePreviewer");
70
+
71
+var _ImagePreviewer2 = _interopRequireDefault(_ImagePreviewer);
72
+
69 73
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
70 74
 
75
+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); } }
76
+
71 77
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
72 78
 
73 79
 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; }
@@ -86,14 +92,33 @@ var CommentItem = function (_Component) {
86 92
     var _this = _possibleConstructorReturn(this, (CommentItem.__proto__ || Object.getPrototypeOf(CommentItem)).call(this, props));
87 93
 
88 94
     _this.state = {
89
-      showInput: false
95
+      showInput: false,
96
+      showPreviewer: false,
97
+      previewerIndex: 0
90 98
     };
91 99
     _this.handleToggleInput = _this.handleToggleInput.bind(_this);
92 100
     _this.renderTextWithReply = _this.renderTextWithReply.bind(_this);
101
+    _this.showPreviewer = _this.showPreviewer.bind(_this);
102
+    _this.hidePreviewer = _this.hidePreviewer.bind(_this);
93 103
     return _this;
94 104
   }
95 105
 
96 106
   _createClass(CommentItem, [{
107
+    key: "showPreviewer",
108
+    value: function showPreviewer(index) {
109
+      this.setState({
110
+        showPreviewer: true,
111
+        previewerIndex: index
112
+      });
113
+    }
114
+  }, {
115
+    key: "hidePreviewer",
116
+    value: function hidePreviewer() {
117
+      this.setState({
118
+        showPreviewer: false
119
+      });
120
+    }
121
+  }, {
97 122
     key: "handleToggleInput",
98 123
     value: function handleToggleInput() {
99 124
       this.setState({ showInput: !this.state.showInput });
@@ -124,6 +149,8 @@ var CommentItem = function (_Component) {
124 149
   }, {
125 150
     key: "render",
126 151
     value: function render() {
152
+      var _this2 = this;
153
+
127 154
       var _props = this.props,
128 155
           commentId = _props.commentId,
129 156
           replyId = _props.replyId,
@@ -145,6 +172,18 @@ var CommentItem = function (_Component) {
145 172
 
146 173
       var imageList = images.split(",");
147 174
 
175
+      // 在3, 7前需要换行
176
+      var needClear = imageList.length === 5 || imageList.length === 6 || imageList.length === 9;
177
+      var imgs = [].concat(_toConsumableArray(imageList));
178
+      if (needClear) {
179
+        if (imgs.length > 6) {
180
+          imgs.splice(3, 0, { type: 'divider' });
181
+          imgs.splice(7, 0, { type: 'divider' });
182
+        } else {
183
+          imgs.splice(3, 0, { type: 'divider' });
184
+        }
185
+      }
186
+
148 187
       return _react2.default.createElement(
149 188
         "div",
150 189
         { className: "comment-item-box" },
@@ -187,18 +226,44 @@ var CommentItem = function (_Component) {
187 226
           imageList.length > 0 && imageList[0] !== "" && _react2.default.createElement(
188 227
             "div",
189 228
             { className: "comment-item-image" },
190
-            imageList.map(function (item, index) {
229
+            !this.state.showPreviewer && imgs.map(function (item, index) {
230
+              if (item.type === 'divider') {
231
+                return _react2.default.createElement(
232
+                  "div",
233
+                  {
234
+                    className: "comment-item-image-wrapper",
235
+                    key: index
236
+                  },
237
+                  _react2.default.createElement("div", { className: "comment-img-divider" })
238
+                );
239
+              }
191 240
               return _react2.default.createElement(
192
-                "a",
241
+                "div",
193 242
                 {
194
-                  href: item,
195
-                  target: "_blank",
196
-                  rel: "noopener noreferrer",
197
-                  key: index
243
+                  className: "comment-item-image-wrapper",
244
+                  key: index,
245
+                  onClick: function onClick() {
246
+                    var i = index;
247
+                    if (needClear) {
248
+                      if (index > 3) {
249
+                        i -= 1;
250
+                      }
251
+                      if (index > 7) {
252
+                        i -= 1;
253
+                      }
254
+                    }
255
+                    _this2.showPreviewer(i);
256
+                  }
198 257
                 },
199
-                _react2.default.createElement("img", { src: item, alt: item, className: "comment-img" })
258
+                _react2.default.createElement("div", { style: { backgroundImage: "url(" + item + ")" }, className: "comment-img-thumbnail" })
200 259
               );
201
-            })
260
+            }),
261
+            this.state.showPreviewer && _react2.default.createElement(_ImagePreviewer2.default, {
262
+              list: imageList,
263
+              index: this.state.previewerIndex,
264
+              onFold: this.hidePreviewer
265
+            }),
266
+            _react2.default.createElement("div", { className: "clearfix" })
202 267
           ),
203 268
           _react2.default.createElement(
204 269
             "div",

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


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

@@ -1,4 +1,4 @@
1
-.item {
1
+.emoji .item {
2 2
   float: left;
3 3
   width: 40px;
4 4
   height: 40px;
@@ -9,19 +9,19 @@
9 9
   margin: 0;
10 10
 }
11 11
 
12
-.item .helper {
12
+.emoji .item .helper {
13 13
   display: inline-block;
14 14
   height: 100%;
15 15
   vertical-align: middle;
16 16
 }
17 17
 
18
-.item img {
18
+.emoji .item img {
19 19
   margin: 0 auto;
20 20
   vertical-align: middle;
21 21
   padding: 3px;
22 22
 }
23 23
 
24
-.item img:hover {
24
+.emoji .item img:hover {
25 25
   border: 1px solid #40a9ff;
26 26
 }
27 27
 

+ 1
- 1
lib/components/Editor/Emoji.js View File

@@ -49,7 +49,7 @@ var Emoji = function Emoji(_ref) {
49 49
     content.map(function (page, index) {
50 50
       return _react2.default.createElement(
51 51
         "div",
52
-        { key: index },
52
+        { key: index, className: "emoji" },
53 53
         page.map(function (item, index) {
54 54
           return _react2.default.createElement(
55 55
             "div",

+ 1
- 1
lib/components/Editor/Emoji.js.map View File

@@ -1 +1 @@
1
-{"version":3,"sources":["../../../src/components/Editor/Emoji.js"],"names":["Emoji","onClick","content","curPage","i","length","push","emoji","map","page","index","item","value","prefixUrl","ext","title","display"],"mappings":";;;;;;;;;;;;AAAA;;;;AAEA;;;;AACA;;;;AACA;AACA;;AAEA;;AAEA,IAAMA,QAAQ,SAARA,KAAQ,OAAiB;AAAA,MAAdC,QAAc,QAAdA,OAAc;;AAC7B,MAAMC,UAAU,EAAhB;AACA,MAAIC,UAAU,EAAd;AACA,OAAK,IAAIC,IAAI,CAAb,EAAgBA,IAAI,GAApB,EAAyBA,GAAzB,EAA8B;AAC5B,QAAID,QAAQE,MAAR,GAAiB,EAArB,EAAyB;AACvBF,cAAQG,IAAR,CAAaC,gBAAMH,CAAN,CAAb;AACD,KAFD,MAEO;AACLF,cAAQI,IAAR,CAAaH,OAAb;AACAA,gBAAU,EAAV;AACD;AACF;AACD,MAAIA,QAAQE,MAAR,GAAiB,CAArB,EAAwB;AACtBH,YAAQI,IAAR,CAAaH,OAAb;AACD;AACD,SACE;AAAA;AAAA;AACGD,YAAQM,GAAR,CAAY,UAACC,IAAD,EAAOC,KAAP;AAAA,aACX;AAAA;AAAA,UAAK,KAAKA,KAAV;AACGD,aAAKD,GAAL,CAAS,UAACG,IAAD,EAAOD,KAAP;AAAA,iBACR;AAAA;AAAA,cAAK,WAAU,MAAf,EAAsB,KAAKC,KAAKC,KAAhC;AACE,oDAAM,WAAU,QAAhB,GADF;AAEE;AACE,wBAAQC,gBAAR,GAAoBF,KAAKC,KAAzB,SAAkCD,KAAKG,GADzC;AAEE,mBAAKH,KAAKI,KAFZ;AAGE,qBAAO,EAAEC,SAAS,cAAX,EAHT;AAIE,uBAAS;AAAA,uBAAMf,SAAQU,KAAKI,KAAb,CAAN;AAAA;AAJX;AAFF,WADQ;AAAA,SAAT;AADH,OADW;AAAA,KAAZ;AADH,GADF;AAmBD,CAjCD;;kBAmCef,K","file":"Emoji.js","sourcesContent":["import React from \"react\";\r\nimport { Carousel } from \"antd\";\r\nimport emoji, { prefixUrl } from \"../../emoji\";\r\nimport \"./Emoji.css\";\r\n// 每页 20  5*4\r\n// 共 20 * 3 = 60 (实际是 54)\r\n\r\n// class Emoji\r\n\r\nconst Emoji = ({ onClick }) => {\r\n  const content = [];\r\n  let curPage = [];\r\n  for (let i = 0; i < 115; i++) {\r\n    if (curPage.length < 20) {\r\n      curPage.push(emoji[i]);\r\n    } else {\r\n      content.push(curPage);\r\n      curPage = [];\r\n    }\r\n  }\r\n  if (curPage.length > 0) {\r\n    content.push(curPage);\r\n  }\r\n  return (\r\n    <Carousel>\r\n      {content.map((page, index) => (\r\n        <div key={index}>\r\n          {page.map((item, index) => (\r\n            <div className=\"item\" key={item.value}>\r\n              <span className=\"helper\" />\r\n              <img\r\n                src={`${prefixUrl}${item.value}.${item.ext}`}\r\n                alt={item.title}\r\n                style={{ display: \"inline-block\" }}\r\n                onClick={() => onClick(item.title)}\r\n              />\r\n            </div>\r\n          ))}\r\n        </div>\r\n      ))}\r\n    </Carousel>\r\n  );\r\n};\r\n\r\nexport default Emoji;\r\n"]}
1
+{"version":3,"sources":["../../../src/components/Editor/Emoji.js"],"names":["Emoji","onClick","content","curPage","i","length","push","emoji","map","page","index","item","value","prefixUrl","ext","title","display"],"mappings":";;;;;;;;;;;;AAAA;;;;AAEA;;;;AACA;;;;AACA;AACA;;AAEA;;AAEA,IAAMA,QAAQ,SAARA,KAAQ,OAAiB;AAAA,MAAdC,QAAc,QAAdA,OAAc;;AAC7B,MAAMC,UAAU,EAAhB;AACA,MAAIC,UAAU,EAAd;AACA,OAAK,IAAIC,IAAI,CAAb,EAAgBA,IAAI,GAApB,EAAyBA,GAAzB,EAA8B;AAC5B,QAAID,QAAQE,MAAR,GAAiB,EAArB,EAAyB;AACvBF,cAAQG,IAAR,CAAaC,gBAAMH,CAAN,CAAb;AACD,KAFD,MAEO;AACLF,cAAQI,IAAR,CAAaH,OAAb;AACAA,gBAAU,EAAV;AACD;AACF;AACD,MAAIA,QAAQE,MAAR,GAAiB,CAArB,EAAwB;AACtBH,YAAQI,IAAR,CAAaH,OAAb;AACD;AACD,SACE;AAAA;AAAA;AACGD,YAAQM,GAAR,CAAY,UAACC,IAAD,EAAOC,KAAP;AAAA,aACX;AAAA;AAAA,UAAK,KAAKA,KAAV,EAAiB,WAAU,OAA3B;AACGD,aAAKD,GAAL,CAAS,UAACG,IAAD,EAAOD,KAAP;AAAA,iBACR;AAAA;AAAA,cAAK,WAAU,MAAf,EAAsB,KAAKC,KAAKC,KAAhC;AACE,oDAAM,WAAU,QAAhB,GADF;AAEE;AACE,wBAAQC,gBAAR,GAAoBF,KAAKC,KAAzB,SAAkCD,KAAKG,GADzC;AAEE,mBAAKH,KAAKI,KAFZ;AAGE,qBAAO,EAAEC,SAAS,cAAX,EAHT;AAIE,uBAAS;AAAA,uBAAMf,SAAQU,KAAKI,KAAb,CAAN;AAAA;AAJX;AAFF,WADQ;AAAA,SAAT;AADH,OADW;AAAA,KAAZ;AADH,GADF;AAmBD,CAjCD;;kBAmCef,K","file":"Emoji.js","sourcesContent":["import React from \"react\";\r\nimport { Carousel } from \"antd\";\r\nimport emoji, { prefixUrl } from \"../../emoji\";\r\nimport \"./Emoji.css\";\r\n// 每页 20  5*4\r\n// 共 20 * 3 = 60 (实际是 54)\r\n\r\n// class Emoji\r\n\r\nconst Emoji = ({ onClick }) => {\r\n  const content = [];\r\n  let curPage = [];\r\n  for (let i = 0; i < 115; i++) {\r\n    if (curPage.length < 20) {\r\n      curPage.push(emoji[i]);\r\n    } else {\r\n      content.push(curPage);\r\n      curPage = [];\r\n    }\r\n  }\r\n  if (curPage.length > 0) {\r\n    content.push(curPage);\r\n  }\r\n  return (\r\n    <Carousel>\r\n      {content.map((page, index) => (\r\n        <div key={index} className=\"emoji\">\r\n          {page.map((item, index) => (\r\n            <div className=\"item\" key={item.value}>\r\n              <span className=\"helper\" />\r\n              <img\r\n                src={`${prefixUrl}${item.value}.${item.ext}`}\r\n                alt={item.title}\r\n                style={{ display: \"inline-block\" }}\r\n                onClick={() => onClick(item.title)}\r\n              />\r\n            </div>\r\n          ))}\r\n        </div>\r\n      ))}\r\n    </Carousel>\r\n  );\r\n};\r\n\r\nexport default Emoji;\r\n"]}

+ 4
- 4
lib/components/Editor/Upload.css View File

@@ -1,9 +1,9 @@
1 1
 .ant-upload-select-picture-card i {
2
-    font-size: 32px;
3
-    color: #999;
2
+  font-size: 32px;
3
+  color: #999;
4 4
 }
5 5
 
6 6
 .ant-upload-select-picture-card .ant-upload-text {
7
-    margin-top: 8px;
8
-    color: #666;
7
+  margin-top: 8px;
8
+  color: #666;
9 9
 }

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

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

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


+ 109
- 0
lib/components/ImagePreviewer/ImagePreviewer.css View File

@@ -0,0 +1,109 @@
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'), auto;
33
+}
34
+.container .pictureWrapper .tools .item.shrink {
35
+  cursor: url('//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/oGPpbJ7Rk.undefined'), auto;
36
+}
37
+.container .pictureWrapper .tools .item.right {
38
+  cursor: url('//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined'), auto;
39
+}
40
+.container .pictureWrapper .picture {
41
+  max-width: 100%;
42
+  max-height: 400px;
43
+}
44
+.container .pictureWrapper .prev {
45
+  position: absolute;
46
+  width: 50px;
47
+  background-color: transparent;
48
+  z-index: 100;
49
+  opacity: 0.5;
50
+  font-size: 50px;
51
+  text-align: left;
52
+  cursor: pointer;
53
+  left: 5px;
54
+  top: 0;
55
+  bottom: 0;
56
+}
57
+.container .pictureWrapper .prev:hover {
58
+  background-color: #fff;
59
+}
60
+.container .pictureWrapper .next {
61
+  position: absolute;
62
+  width: 50px;
63
+  background-color: transparent;
64
+  z-index: 100;
65
+  opacity: 0.5;
66
+  font-size: 50px;
67
+  text-align: left;
68
+  cursor: pointer;
69
+  right: 5px;
70
+  top: 0;
71
+  bottom: 0;
72
+}
73
+.container .pictureWrapper .next:hover {
74
+  background-color: #fff;
75
+}
76
+.container .pictureWrapper .middle {
77
+  position: absolute;
78
+  top: 50%;
79
+  transform: translateY(-50%);
80
+}
81
+.container .list {
82
+  font-size: 0;
83
+  margin-top: 25px;
84
+}
85
+.container .list .wrapper {
86
+  width: 11.1%;
87
+  height: 0;
88
+  padding-bottom: 11.1%;
89
+  position: relative;
90
+  display: inline-block;
91
+  border-radius: 5px;
92
+  border: 1px solid transparent;
93
+  opacity: 0.5;
94
+}
95
+.container .list .wrapper.active {
96
+  border: 1px solid #fc4747;
97
+  opacity: 1;
98
+}
99
+.container .list .wrapper .thumbnail {
100
+  position: absolute;
101
+  top: 5px;
102
+  bottom: 5px;
103
+  left: 5px;
104
+  right: 5px;
105
+  background-size: cover;
106
+  background-position: center;
107
+  border-radius: 5px;
108
+  cursor: pointer;
109
+}

+ 243
- 0
lib/components/ImagePreviewer/ImagePreviewer.js View File

@@ -0,0 +1,243 @@
1
+'use strict';
2
+
3
+Object.defineProperty(exports, "__esModule", {
4
+    value: true
5
+});
6
+
7
+var _spin = require('antd/es/spin');
8
+
9
+var _spin2 = _interopRequireDefault(_spin);
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/spin/style/css');
18
+
19
+require('antd/es/icon/style/css');
20
+
21
+var _react = require('react');
22
+
23
+var _react2 = _interopRequireDefault(_react);
24
+
25
+var _classnames = require('classnames');
26
+
27
+var _classnames2 = _interopRequireDefault(_classnames);
28
+
29
+require('./ImagePreviewer.css');
30
+
31
+var _helper = require('../../helper');
32
+
33
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
34
+
35
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
36
+
37
+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; }
38
+
39
+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; }
40
+
41
+var ImagePreviewer = function (_React$Component) {
42
+    _inherits(ImagePreviewer, _React$Component);
43
+
44
+    function ImagePreviewer(props) {
45
+        _classCallCheck(this, ImagePreviewer);
46
+
47
+        var _this = _possibleConstructorReturn(this, (ImagePreviewer.__proto__ || Object.getPrototypeOf(ImagePreviewer)).call(this, props));
48
+
49
+        _this.setIndex = _this.setIndex.bind(_this);
50
+        _this.onPrev = _this.onPrev.bind(_this);
51
+        _this.onNext = _this.onNext.bind(_this);
52
+        _this.onOrigin = _this.onOrigin.bind(_this);
53
+        _this.rotateLeft = _this.rotateLeft.bind(_this);
54
+        _this.rotateRight = _this.rotateRight.bind(_this);
55
+        _this.onImgLoad = _this.onImgLoad.bind(_this);
56
+        _this.onImageError = _this.onImageError.bind(_this);
57
+        _this.state = {
58
+            cIndex: props.index,
59
+            direction: 0, // 0- 0deg 1- 90deg 2- 180deg 3- 270deg
60
+            loading: false
61
+        };
62
+        return _this;
63
+    }
64
+
65
+    _createClass(ImagePreviewer, [{
66
+        key: 'componentWillReceiveProps',
67
+        value: function componentWillReceiveProps(nextProps) {
68
+            if (nextProps.index !== this.props.index) {
69
+                this.setIndex(nextProps.index);
70
+            }
71
+        }
72
+    }, {
73
+        key: 'onPrev',
74
+        value: function onPrev() {
75
+            var prev = this.state.cIndex === 0 ? this.props.list.length - 1 : this.state.cIndex - 1;
76
+            this.setIndex(prev);
77
+        }
78
+    }, {
79
+        key: 'onNext',
80
+        value: function onNext() {
81
+            var next = this.state.cIndex === this.props.list.length - 1 ? 0 : this.state.cIndex + 1;
82
+            this.setIndex(next);
83
+        }
84
+    }, {
85
+        key: 'onOrigin',
86
+        value: function onOrigin() {
87
+            window.open(this.props.list[this.state.cIndex]);
88
+        }
89
+    }, {
90
+        key: 'onImgLoad',
91
+        value: function onImgLoad() {
92
+            this.setState({
93
+                loading: false
94
+            });
95
+        }
96
+    }, {
97
+        key: 'onImageError',
98
+        value: function onImageError() {
99
+            this.setState({
100
+                loading: false
101
+            });
102
+        }
103
+    }, {
104
+        key: 'setIndex',
105
+        value: function setIndex(index) {
106
+            this.setState({
107
+                cIndex: index,
108
+                direction: 0,
109
+                loading: true
110
+            });
111
+        }
112
+    }, {
113
+        key: 'rotateLeft',
114
+        value: function rotateLeft() {
115
+            var direction = this.state.direction - 1;
116
+            if (direction === -1) {
117
+                direction = 3;
118
+            }
119
+            this.setState({
120
+                direction: direction
121
+            });
122
+        }
123
+    }, {
124
+        key: 'rotateRight',
125
+        value: function rotateRight() {
126
+            var direction = this.state.direction + 1;
127
+            if (direction === 4) {
128
+                direction = 0;
129
+            }
130
+            this.setState({
131
+                direction: direction
132
+            });
133
+        }
134
+
135
+        // calcHeight(node) {
136
+        //     const { naturalHeight, naturalWidth } = node;
137
+        // }
138
+
139
+    }, {
140
+        key: 'render',
141
+        value: function render() {
142
+            var _this2 = this;
143
+
144
+            var _props = this.props,
145
+                list = _props.list,
146
+                onFold = _props.onFold;
147
+            var cIndex = this.state.cIndex;
148
+
149
+            return _react2.default.createElement(
150
+                'div',
151
+                { className: 'container' },
152
+                _react2.default.createElement(
153
+                    'div',
154
+                    { className: 'toolbar' },
155
+                    _react2.default.createElement(
156
+                        'span',
157
+                        { className: 'button', onClick: onFold },
158
+                        _react2.default.createElement(_icon2.default, { type: 'to-top' }),
159
+                        '\u6536\u8D77'
160
+                    ),
161
+                    _react2.default.createElement(
162
+                        'span',
163
+                        { className: 'button', onClick: this.onOrigin },
164
+                        _react2.default.createElement(_icon2.default, { type: 'search' }),
165
+                        ' \u67E5\u770B\u539F\u56FE'
166
+                    )
167
+                ),
168
+                _react2.default.createElement(
169
+                    'div',
170
+                    { className: 'pictureWrapper' },
171
+                    _react2.default.createElement(
172
+                        _spin2.default,
173
+                        { spinning: this.state.loading },
174
+                        _react2.default.createElement('img', {
175
+                            // ref={node => {
176
+                            //     if (node) {
177
+                            //         this.calcHeight(node);
178
+                            //     }
179
+                            // }}
180
+                            className: 'picture',
181
+                            src: (0, _helper.addImageProcess)(list[cIndex], { large: true }),
182
+                            alt: list[cIndex],
183
+                            style: {
184
+                                transform: 'rotate(' + this.state.direction * 90 + 'deg)',
185
+                                opacity: this.state.loading ? 0 : 1,
186
+                                transition: '0.3s opacity'
187
+                            },
188
+                            onLoad: this.onImgLoad,
189
+                            onError: this.onImageError
190
+                        })
191
+                    ),
192
+                    _react2.default.createElement(
193
+                        'div',
194
+                        { className: 'tools' },
195
+                        list.length > 1 && _react2.default.createElement('div', {
196
+                            onClick: this.onPrev,
197
+                            className: (0, _classnames2.default)("item", "left")
198
+                        }),
199
+                        _react2.default.createElement('div', { onClick: onFold, className: (0, _classnames2.default)("item", "shrink") }),
200
+                        list.length > 1 && _react2.default.createElement('div', {
201
+                            onClick: this.onNext,
202
+                            className: (0, _classnames2.default)("item", "right")
203
+                        })
204
+                    )
205
+                ),
206
+                list.length > 1 && _react2.default.createElement(
207
+                    'div',
208
+                    { className: 'list' },
209
+                    list.map(function (item, index) {
210
+                        return _react2.default.createElement(
211
+                            'div',
212
+                            {
213
+                                className: (0, _classnames2.default)({
214
+                                    "wrapper": true,
215
+                                    "active": index === cIndex
216
+                                }),
217
+                                key: item
218
+                            },
219
+                            _react2.default.createElement('div', {
220
+                                className: (0, _classnames2.default)({
221
+                                    "thumbnail": true
222
+                                }),
223
+                                style: {
224
+                                    backgroundImage: 'url(' + (0, _helper.addImageProcess)(item, {
225
+                                        small: true
226
+                                    }) + ')'
227
+                                },
228
+                                onClick: function onClick() {
229
+                                    _this2.setIndex(index);
230
+                                }
231
+                            })
232
+                        );
233
+                    })
234
+                )
235
+            );
236
+        }
237
+    }]);
238
+
239
+    return ImagePreviewer;
240
+}(_react2.default.Component);
241
+
242
+exports.default = ImagePreviewer;
243
+//# sourceMappingURL=ImagePreviewer.js.map

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


+ 114
- 0
lib/components/ImagePreviewer/ImagePreviewer.less View File

@@ -0,0 +1,114 @@
1
+.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'), auto;
33
+                }
34
+                &.shrink {
35
+                    cursor: url('//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/oGPpbJ7Rk.undefined'), auto;
36
+                }
37
+                &.right {
38
+                    cursor: url('//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/S8obecP1d.undefined'), auto;
39
+                }
40
+            }
41
+        }
42
+        .picture {
43
+            max-width: 100%;
44
+            max-height: 400px;
45
+        }
46
+        .tool() {
47
+            position: absolute;
48
+            width: 50px;
49
+            background-color: transparent;
50
+            z-index: 100;
51
+            opacity: 0.5;
52
+            font-size: 50px;
53
+            text-align: left;
54
+            cursor: pointer;
55
+            &:hover {
56
+                background-color: #fff;
57
+            }
58
+        }
59
+        .prev {
60
+            .tool();
61
+            left: 5px;
62
+            top: 0;
63
+            bottom: 0;
64
+        }
65
+        .next {
66
+            .tool();
67
+            right: 5px;
68
+            top: 0;
69
+            bottom: 0;
70
+        }
71
+        .middle {
72
+            position: absolute;
73
+            top: 50%;
74
+            transform: translateY(-50%);
75
+        } // .picture {
76
+        //     position: absolute;
77
+        //     bottom: 0;
78
+        //     top: 0;
79
+        //     left: 5px;
80
+        //     right: 5px;
81
+        //     background-size: contain;
82
+        //     background-position: center;
83
+        // }
84
+    }
85
+    .list {
86
+        font-size: 0;
87
+        margin-top: 25px;
88
+        .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
+            &.active {
98
+                border: 1px solid #fc4747;
99
+                opacity: 1;
100
+            }
101
+            .thumbnail {
102
+                position: absolute;
103
+                top: 5px;
104
+                bottom: 5px;
105
+                left: 5px;
106
+                right: 5px;
107
+                background-size: cover;
108
+                background-position: center;
109
+                border-radius: 5px;
110
+                cursor: pointer;
111
+            }
112
+        }
113
+    }
114
+}

+ 4
- 0
lib/components/ImagePreviewer/README.md View File

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

+ 4
- 0
lib/constant.js View File

@@ -22,4 +22,8 @@ var REGEXP = exports.REGEXP = /\[[^[\]]+?\]/g;
22 22
 var AVATAR = exports.AVATAR = "";
23 23
 
24 24
 var IMAGE_SPLIT = exports.IMAGE_SPLIT = "IMAGE_SPLIT";
25
+
26
+var IMAGE_PROCESS = exports.IMAGE_PROCESS = '?x-oss-process=image/resize,h_350';
27
+var IMAGE_PROCESS_SMALL = exports.IMAGE_PROCESS_SMALL = '?x-oss-process=image/resize,h_100';
28
+var IMAGE_PROCESS_LARGE = exports.IMAGE_PROCESS_LARGE = '?x-oss-process=image/resize,h_500';
25 29
 //# sourceMappingURL=constant.js.map

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

@@ -1 +1 @@
1
-{"version":3,"sources":["../src/constant.js"],"names":["ERROR_DEFAULT","LIMIT","OSS_ENDPOINT","OSS_BUCKET","DRIVER_LICENSE_PATH","OSS_LINK","MAX_UPLOAD_NUMBER","URL_REGEXP","REGEXP","AVATAR","IMAGE_SPLIT"],"mappings":";;;;;AAAO,IAAMA,wCAAgB,MAAtB;;AAEA,IAAMC,wBAAQ,EAAd,C,CAAkB;;AAElB,IAAMC,sCAAe,6BAArB;AACA,IAAMC,kCAAa,eAAnB;AACA,IAAMC,oDAAsB,UAA5B;;AAEA,IAAMC,8BAAW,6CAAjB;;AAEA,IAAMC,gDAAoB,CAA1B;;AAEA,IAAMC,kCAAa,oGAAnB;;AAEA,IAAMC,0BAAS,eAAf;;AAEA,IAAMC,0BAAS,EAAf;;AAEA,IAAMC,oCAAc,aAApB","file":"constant.js","sourcesContent":["export const ERROR_DEFAULT = \"出错了!\";\r\n\r\nexport const LIMIT = 10; // 默认 limit\r\n\r\nexport const OSS_ENDPOINT = \"oss-cn-beijing.aliyuncs.com\";\r\nexport const OSS_BUCKET = \"links-comment\";\r\nexport const DRIVER_LICENSE_PATH = \"/comment\";\r\n\r\nexport const OSS_LINK = \"//links-comment.oss-cn-beijing.aliyuncs.com\";\r\n\r\nexport const MAX_UPLOAD_NUMBER = 4;\r\n\r\nexport const URL_REGEXP = /((http(s)?:)?\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g;\r\n\r\nexport const REGEXP = /\\[[^[\\]]+?\\]/g;\r\n\r\nexport const AVATAR = \"\";\r\n\r\nexport const IMAGE_SPLIT = \"IMAGE_SPLIT\";\r\n"]}
1
+{"version":3,"sources":["../src/constant.js"],"names":["ERROR_DEFAULT","LIMIT","OSS_ENDPOINT","OSS_BUCKET","DRIVER_LICENSE_PATH","OSS_LINK","MAX_UPLOAD_NUMBER","URL_REGEXP","REGEXP","AVATAR","IMAGE_SPLIT","IMAGE_PROCESS","IMAGE_PROCESS_SMALL","IMAGE_PROCESS_LARGE"],"mappings":";;;;;AAAO,IAAMA,wCAAgB,MAAtB;;AAEA,IAAMC,wBAAQ,EAAd,C,CAAkB;;AAElB,IAAMC,sCAAe,6BAArB;AACA,IAAMC,kCAAa,eAAnB;AACA,IAAMC,oDAAsB,UAA5B;;AAEA,IAAMC,8BAAW,6CAAjB;;AAEA,IAAMC,gDAAoB,CAA1B;;AAEA,IAAMC,kCAAa,oGAAnB;;AAEA,IAAMC,0BAAS,eAAf;;AAEA,IAAMC,0BAAS,EAAf;;AAEA,IAAMC,oCAAc,aAApB;;AAEA,IAAMC,wCAAgB,mCAAtB;AACA,IAAMC,oDAAsB,mCAA5B;AACA,IAAMC,oDAAsB,mCAA5B","file":"constant.js","sourcesContent":["export const ERROR_DEFAULT = \"出错了!\";\r\n\r\nexport const LIMIT = 10; // 默认 limit\r\n\r\nexport const OSS_ENDPOINT = \"oss-cn-beijing.aliyuncs.com\";\r\nexport const OSS_BUCKET = \"links-comment\";\r\nexport const DRIVER_LICENSE_PATH = \"/comment\";\r\n\r\nexport const OSS_LINK = \"//links-comment.oss-cn-beijing.aliyuncs.com\";\r\n\r\nexport const MAX_UPLOAD_NUMBER = 4;\r\n\r\nexport const URL_REGEXP = /((http(s)?:)?\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g;\r\n\r\nexport const REGEXP = /\\[[^[\\]]+?\\]/g;\r\n\r\nexport const AVATAR = \"\";\r\n\r\nexport const IMAGE_SPLIT = \"IMAGE_SPLIT\";\r\n\r\nexport const IMAGE_PROCESS = '?x-oss-process=image/resize,h_350';\r\nexport const IMAGE_PROCESS_SMALL = '?x-oss-process=image/resize,h_100';\r\nexport const IMAGE_PROCESS_LARGE = '?x-oss-process=image/resize,h_500';\r\n"]}

+ 13
- 0
lib/helper.js View File

@@ -8,6 +8,7 @@ exports.isUrl = isUrl;
8 8
 exports.arrayToObject = arrayToObject;
9 9
 exports.htmlEncode = htmlEncode;
10 10
 exports.renderContent = renderContent;
11
+exports.addImageProcess = addImageProcess;
11 12
 
12 13
 var _constant = require("./constant");
13 14
 
@@ -92,4 +93,16 @@ function renderContent(content, onClick) {
92 93
   }).replace(/\n/g, "<br />");
93 94
   return data;
94 95
 }
96
+
97
+function addImageProcess(url) {
98
+  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
99
+
100
+  if (options.small) {
101
+    return url + _constant.IMAGE_PROCESS_SMALL;
102
+  }
103
+  if (options.large) {
104
+    return url + _constant.IMAGE_PROCESS_LARGE;
105
+  }
106
+  return url + _constant.IMAGE_PROCESS;
107
+}
95 108
 //# sourceMappingURL=helper.js.map

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


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

@@ -45,6 +45,38 @@
45 45
   color: #4a90e2;
46 46
 }
47 47
 
48
+.comment-item-image {
49
+  width: 400px;
50
+  max-width: 100%;
51
+}
52
+
53
+.comment-item-image .comment-item-image-wrapper {
54
+  float: left;
55
+  width: 25%;
56
+  box-sizing: border-box;
57
+  padding: 5px;
58
+}
59
+
60
+.comment-item-image .comment-item-image-wrapper .comment-img-thumbnail {
61
+  background-position: center;
62
+  background-size: cover;
63
+  display: inline-block;
64
+  width: 100%;
65
+  height: 0;
66
+  padding-bottom: 100%;
67
+  border-radius: 5px;
68
+  cursor: url("//links-comment.oss-cn-beijing.aliyuncs.com/comment/20180928/QPI84fxmD.undefined"),
69
+    auto;
70
+}
71
+
72
+.comment-item-image .comment-item-image-wrapper .comment-img-divider {
73
+  display: inline-block;
74
+  width: 100%;
75
+  height: 0;
76
+  padding-bottom: 100%;
77
+  border-radius: 5px;
78
+}
79
+
48 80
 .comment-item-image .comment-img {
49 81
   margin-right: 10px;
50 82
 }

+ 77
- 13
src/components/ContentItem/index.js View File

@@ -10,6 +10,7 @@ import avatar from "../../avatar";
10 10
 import { renderContent } from "../../helper";
11 11
 import { IMAGE_SPLIT } from "../../constant";
12 12
 import "./index.css";
13
+import ImagePreviewer from "../ImagePreviewer/ImagePreviewer";
13 14
 
14 15
 dayjs.locale("zh-cn");
15 16
 dayjs.extend(relativeTime);
@@ -18,10 +19,27 @@ class CommentItem extends Component {
18 19
   constructor(props) {
19 20
     super(props);
20 21
     this.state = {
21
-      showInput: false
22
+      showInput: false,
23
+      showPreviewer: false,
24
+      previewerIndex: 0
22 25
     };
23 26
     this.handleToggleInput = this.handleToggleInput.bind(this);
24 27
     this.renderTextWithReply = this.renderTextWithReply.bind(this);
28
+    this.showPreviewer = this.showPreviewer.bind(this);
29
+    this.hidePreviewer = this.hidePreviewer.bind(this);
30
+  }
31
+
32
+  showPreviewer(index) {
33
+    this.setState({
34
+      showPreviewer: true,
35
+      previewerIndex: index
36
+    });
37
+  }
38
+
39
+  hidePreviewer() {
40
+    this.setState({
41
+      showPreviewer: false
42
+    });
25 43
   }
26 44
 
27 45
   handleToggleInput() {
@@ -72,6 +90,21 @@ class CommentItem extends Component {
72 90
 
73 91
     const imageList = images.split(",");
74 92
 
93
+    // 在3, 7前需要换行
94
+    const needClear =
95
+      imageList.length === 5 ||
96
+      imageList.length === 6 ||
97
+      imageList.length === 9;
98
+    const imgs = [...imageList];
99
+    if (needClear) {
100
+      if (imgs.length > 6) {
101
+        imgs.splice(3, 0, { type: "divider" });
102
+        imgs.splice(7, 0, { type: "divider" });
103
+      } else {
104
+        imgs.splice(3, 0, { type: "divider" });
105
+      }
106
+    }
107
+
75 108
     return (
76 109
       <div className="comment-item-box">
77 110
         <div className="comment-item-left">
@@ -106,18 +139,49 @@ class CommentItem extends Component {
106 139
           imageList.length > 0 &&
107 140
             imageList[0] !== "" && (
108 141
               <div className="comment-item-image">
109
-                {imageList.map((item, index) => {
110
-                  return (
111
-                    <a
112
-                      href={item}
113
-                      target="_blank"
114
-                      rel="noopener noreferrer"
115
-                      key={index}
116
-                    >
117
-                      <img src={item} alt={item} className="comment-img" />
118
-                    </a>
119
-                  );
120
-                })}
142
+                {!this.state.showPreviewer &&
143
+                  imgs.map((item, index) => {
144
+                    if (item.type === "divider") {
145
+                      return (
146
+                        <div className="comment-item-image-wrapper" key={index}>
147
+                          <div className="comment-img-divider" />
148
+                          {/* <img src={item} alt={item} className="comment-img" /> */}
149
+                        </div>
150
+                      );
151
+                    }
152
+                    return (
153
+                      <div
154
+                        className="comment-item-image-wrapper"
155
+                        key={index}
156
+                        onClick={() => {
157
+                          let i = index;
158
+                          if (needClear) {
159
+                            if (index > 3) {
160
+                              i -= 1;
161
+                            }
162
+                            if (index > 7) {
163
+                              i -= 1;
164
+                            }
165
+                          }
166
+                          this.showPreviewer(i);
167
+                        }}
168
+                      >
169
+                        <div
170
+                          style={{ backgroundImage: `url(${item})` }}
171
+                          className="comment-img-thumbnail"
172
+                        />
173
+                        {/* <img src={item} alt={item} className="comment-img" /> */}
174
+                      </div>
175
+                    );
176
+                  })}
177
+                {this.state.showPreviewer && (
178
+                  <ImagePreviewer
179
+                    list={imageList}
180
+                    index={this.state.previewerIndex}
181
+                    onFold={this.hidePreviewer}
182
+                  />
183
+                )}
184
+                <div className="clearfix" />
121 185
               </div>
122 186
             )}
123 187
           <div className="comment-item-bottom">

+ 4
- 4
src/components/Editor/Emoji.css View File

@@ -1,4 +1,4 @@
1
-.item {
1
+.emoji .item {
2 2
   float: left;
3 3
   width: 40px;
4 4
   height: 40px;
@@ -9,19 +9,19 @@
9 9
   margin: 0;
10 10
 }
11 11
 
12
-.item .helper {
12
+.emoji .item .helper {
13 13
   display: inline-block;
14 14
   height: 100%;
15 15
   vertical-align: middle;
16 16
 }
17 17
 
18
-.item img {
18
+.emoji .item img {
19 19
   margin: 0 auto;
20 20
   vertical-align: middle;
21 21
   padding: 3px;
22 22
 }
23 23
 
24
-.item img:hover {
24
+.emoji .item img:hover {
25 25
   border: 1px solid #40a9ff;
26 26
 }
27 27
 

+ 1
- 1
src/components/Editor/Emoji.js View File

@@ -24,7 +24,7 @@ const Emoji = ({ onClick }) => {
24 24
   return (
25 25
     <Carousel>
26 26
       {content.map((page, index) => (
27
-        <div key={index}>
27
+        <div key={index} className="emoji">
28 28
           {page.map((item, index) => (
29 29
             <div className="item" key={item.value}>
30 30
               <span className="helper" />

+ 112
- 0
src/components/ImagePreviewer/ImagePreviewer.css View File

@@ -0,0 +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
+}

+ 189
- 0
src/components/ImagePreviewer/ImagePreviewer.jsx View File

@@ -0,0 +1,189 @@
1
+import React from "react";
2
+import classnames from "classnames";
3
+import { Icon, Spin } from "antd";
4
+
5
+import "./ImagePreviewer.css";
6
+import { addImageProcess } from "../../helper";
7
+
8
+export default class ImagePreviewer extends React.Component {
9
+  constructor(props) {
10
+    super(props);
11
+    this.setIndex = this.setIndex.bind(this);
12
+    this.onPrev = this.onPrev.bind(this);
13
+    this.onNext = this.onNext.bind(this);
14
+    this.onOrigin = this.onOrigin.bind(this);
15
+    this.rotateLeft = this.rotateLeft.bind(this);
16
+    this.rotateRight = this.rotateRight.bind(this);
17
+    this.onImgLoad = this.onImgLoad.bind(this);
18
+    this.onImageError = this.onImageError.bind(this);
19
+    this.state = {
20
+      cIndex: props.index,
21
+      direction: 0, // 0- 0deg 1- 90deg 2- 180deg 3- 270deg
22
+      loading: false
23
+    };
24
+  }
25
+
26
+  componentWillReceiveProps(nextProps) {
27
+    if (nextProps.index !== this.props.index) {
28
+      this.setIndex(nextProps.index);
29
+    }
30
+  }
31
+
32
+  onPrev() {
33
+    const prev =
34
+      this.state.cIndex === 0
35
+        ? this.props.list.length - 1
36
+        : this.state.cIndex - 1;
37
+    this.setIndex(prev);
38
+  }
39
+
40
+  onNext() {
41
+    const next =
42
+      this.state.cIndex === this.props.list.length - 1
43
+        ? 0
44
+        : this.state.cIndex + 1;
45
+    this.setIndex(next);
46
+  }
47
+
48
+  onOrigin() {
49
+    window.open(this.props.list[this.state.cIndex]);
50
+  }
51
+
52
+  onImgLoad() {
53
+    this.setState({
54
+      loading: false
55
+    });
56
+  }
57
+
58
+  onImageError() {
59
+    this.setState({
60
+      loading: false
61
+    });
62
+  }
63
+
64
+  setIndex(index) {
65
+    this.setState({
66
+      cIndex: index,
67
+      direction: 0,
68
+      loading: true
69
+    });
70
+  }
71
+
72
+  rotateLeft() {
73
+    let direction = this.state.direction - 1;
74
+    if (direction === -1) {
75
+      direction = 3;
76
+    }
77
+    this.setState({
78
+      direction
79
+    });
80
+  }
81
+
82
+  rotateRight() {
83
+    let direction = this.state.direction + 1;
84
+    if (direction === 4) {
85
+      direction = 0;
86
+    }
87
+    this.setState({
88
+      direction
89
+    });
90
+  }
91
+
92
+  // calcHeight(node) {
93
+  //     const { naturalHeight, naturalWidth } = node;
94
+  // }
95
+
96
+  render() {
97
+    const { list, onFold } = this.props;
98
+    const { cIndex } = this.state;
99
+    return (
100
+      <div className="container">
101
+        <div className="toolbar">
102
+          <span className="button" onClick={onFold}>
103
+            <Icon type="to-top" />收起
104
+          </span>
105
+          <span className="button" onClick={this.onOrigin}>
106
+            <Icon type="search" /> 查看原图
107
+          </span>
108
+          {/* <span className="button" onClick={this.rotateRight}>
109
+                        <Icon type="reload" /> 向右旋转
110
+                    </span>
111
+                    <span className={classnames("button", "reversal")} onClick={this.rotateLeft}>
112
+                        <Icon type="reload" /> 向左旋转
113
+                    </span> */}
114
+        </div>
115
+        <div className="pictureWrapper">
116
+          <Spin spinning={this.state.loading}>
117
+            <img
118
+              // ref={node => {
119
+              //     if (node) {
120
+              //         this.calcHeight(node);
121
+              //     }
122
+              // }}
123
+              className="picture"
124
+              src={addImageProcess(list[cIndex], { large: true })}
125
+              alt={list[cIndex]}
126
+              style={{
127
+                transform: `rotate(${this.state.direction * 90}deg)`,
128
+                opacity: this.state.loading ? 0 : 1,
129
+                transition: "0.3s opacity"
130
+              }}
131
+              onLoad={this.onImgLoad}
132
+              onError={this.onImageError}
133
+            />
134
+          </Spin>
135
+          <div className="tools">
136
+            {list.length > 1 && (
137
+              <div
138
+                onClick={this.onPrev}
139
+                className={classnames("item", "left")}
140
+              />
141
+            )}
142
+
143
+            <div onClick={onFold} className={classnames("item", "shrink")} />
144
+            {list.length > 1 && (
145
+              <div
146
+                onClick={this.onNext}
147
+                className={classnames("item", "right")}
148
+              />
149
+            )}
150
+          </div>
151
+          {/* <div className="prev" onClick={this.onPrev}>
152
+                        <Icon className="middle" type="left" />
153
+                    </div>
154
+                    <div className="next" onClick={this.onNext}>
155
+                        <Icon className="middle" type="right" />
156
+                    </div> */}
157
+          {/* <div className="picture" style={{ backgroundImage: `url(${addImageProcess(list[cIndex])})` }} /> */}
158
+        </div>
159
+        {list.length > 1 && (
160
+          <div className="list">
161
+            {list.map((item, index) => (
162
+              <div
163
+                className={classnames({
164
+                  wrapper: true,
165
+                  active: index === cIndex
166
+                })}
167
+                key={item}
168
+              >
169
+                <div
170
+                  className={classnames({
171
+                    thumbnail: true
172
+                  })}
173
+                  style={{
174
+                    backgroundImage: `url(${addImageProcess(item, {
175
+                      small: true
176
+                    })})`
177
+                  }}
178
+                  onClick={() => {
179
+                    this.setIndex(index);
180
+                  }}
181
+                />
182
+              </div>
183
+            ))}
184
+          </div>
185
+        )}
186
+      </div>
187
+    );
188
+  }
189
+}

+ 117
- 0
src/components/ImagePreviewer/ImagePreviewer.less View File

@@ -0,0 +1,117 @@
1
+.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
+}

+ 4
- 0
src/components/ImagePreviewer/README.md View File

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

+ 4
- 0
src/constant.js View File

@@ -17,3 +17,7 @@ export const REGEXP = /\[[^[\]]+?\]/g;
17 17
 export const AVATAR = "";
18 18
 
19 19
 export const IMAGE_SPLIT = "IMAGE_SPLIT";
20
+
21
+export const IMAGE_PROCESS = "?x-oss-process=image/resize,h_350";
22
+export const IMAGE_PROCESS_SMALL = "?x-oss-process=image/resize,h_100";
23
+export const IMAGE_PROCESS_LARGE = "?x-oss-process=image/resize,h_500";

+ 17
- 1
src/helper.js View File

@@ -1,4 +1,10 @@
1
-import { REGEXP, IMAGE_SPLIT } from "./constant";
1
+import {
2
+  REGEXP,
3
+  IMAGE_SPLIT,
4
+  IMAGE_PROCESS_SMALL,
5
+  IMAGE_PROCESS_LARGE,
6
+  IMAGE_PROCESS
7
+} from "./constant";
2 8
 import emoji, { prefixUrl } from "./emoji";
3 9
 
4 10
 const emojiObejct = arrayToObject(emoji, "title");
@@ -84,3 +90,13 @@ export function renderContent(content, onClick) {
84 90
     .replace(/\n/g, "<br />");
85 91
   return data;
86 92
 }
93
+
94
+export function addImageProcess(url, options = {}) {
95
+  if (options.small) {
96
+    return url + IMAGE_PROCESS_SMALL;
97
+  }
98
+  if (options.large) {
99
+    return url + IMAGE_PROCESS_LARGE;
100
+  }
101
+  return url + IMAGE_PROCESS;
102
+}