diff --git a/src/components/ImagePreview/ImagePreview.css b/src/components/ImagePreview/ImagePreview.css
new file mode 100644
index 0000000..02c5342
--- /dev/null
+++ b/src/components/ImagePreview/ImagePreview.css
@@ -0,0 +1,8 @@
+.preview {
+ width: 200px;
+ height: 200px;
+}
+.preview > img {
+ height: 200px;
+ cursor: zoom-in;
+}
\ No newline at end of file
diff --git a/src/components/ImagePreview/ImagePreview.js b/src/components/ImagePreview/ImagePreview.js
new file mode 100644
index 0000000..705399c
--- /dev/null
+++ b/src/components/ImagePreview/ImagePreview.js
@@ -0,0 +1,69 @@
+import './ImagePreview.css';
+
+import { useCallback, useRef, useState, useEffect, Fragment } from 'react';
+import Spinner from '../Spinner/Spinner';
+
+export default function ImagePreview({ image }) {
+ const [isScaled, setIsScaled] = useState(false);
+ const [transform, setTransform] = useState('');
+ const imgRef = useRef(null);
+ const [loading, setLoading] = useState(false);
+ const imgWidth = useRef(0), imgHeight = useRef(0);
+
+ useEffect(() => {
+ setLoading(true);
+ let img = new Image();
+ img.onload = () => {
+ setLoading(false);
+ imgWidth.current = img.width;
+ imgHeight.current = img.height;
+ };
+ img.src = image;
+ }, [image]);
+
+ const scale = useCallback(() => {
+ if (isScaled) return;
+ let el = imgRef.current;
+ let { x, y } = el.getBoundingClientRect();
+ let { innerWidth, innerHeight } = window;
+ let initialScale = 200 / imgHeight.current,
+ terminalScale = Math.min(1, Math.min((innerWidth - 10) / imgWidth.current, (innerHeight - 10) / imgHeight.current));
+ setTransform(`translate(${x + (imgWidth.current * initialScale - innerWidth) / 2}px, ${y + (200 - innerHeight) / 2}px) scale(${initialScale})`);
+ setIsScaled(true);
+ setTimeout(() => {
+ setTransform(`translate(0, 0) scale(${terminalScale})`);
+ }, 0);
+ }, [isScaled]);
+
+ const unscale = useCallback(() => {
+ if (!isScaled) return;
+ let el = imgRef.current;
+ let { x, y } = el.getBoundingClientRect();
+ let { innerWidth, innerHeight } = window;
+ let initialScale = 200 / imgHeight.current;
+ setTransform(`translate(${x + (imgWidth.current * initialScale - innerWidth) / 2}px, ${y + (200 - innerHeight) / 2}px) scale(${initialScale})`);
+ setIsScaled(true);
+ setTimeout(() => {
+ setIsScaled(false);
+ }, 300);
+ }, [isScaled]);
+
+ return (
+
+ {
+ loading ? (
+
+
+
+ ) : (
+
+
+
+

+
+
+ )
+ }
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/Spinner/spinner.css b/src/components/Spinner/spinner.css
index 92481c2..ad430c3 100644
--- a/src/components/Spinner/spinner.css
+++ b/src/components/Spinner/spinner.css
@@ -9,6 +9,14 @@
animation: spin 1s steps(8, start) infinite;
}
+.spinner-wrap {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
@keyframes spin {
from {
transform: rotate(0deg);
diff --git a/src/helper/axios.js b/src/helper/axios.js
index 1f326c9..32d6f44 100644
--- a/src/helper/axios.js
+++ b/src/helper/axios.js
@@ -84,8 +84,9 @@ async function send(xhr, retryConf) {
return failData;
}
- if (!waitToSend.length) return failData;
+ if (waitToSend.length) return failData;
waitToSend.push(retryConf);
+ console.log(err);
// 等待列表不为空时弹框要么出现了要么就是在消失的路上,没有办法给予用户点击重试的机会,所以交由外部逻辑处理
// 注意,理论上带有时间戳的请求是不可以重试的,但是这里不做那方面考虑,如果未来有需要,可以自己实现一个刷新时间戳重试的逻辑
diff --git a/src/index/index.css b/src/index/index.css
index eab8d65..f70d343 100644
--- a/src/index/index.css
+++ b/src/index/index.css
@@ -1,15 +1,20 @@
+.index-container {
+ background-color: #F4F4F4;
+ min-height: 100vh;
+}
+
.poster {
width: 100%;
height: 400px;
background-color: #84000B;
+ background-position: center;
+ background-size: contain;
+ background-repeat: no-repeat;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
-.img-poster {
- height: 100%;
-}
.index-btns {
position: absolute;
right: 40px;
@@ -20,7 +25,6 @@
.index-btns > .user {
margin-right: 10px;
}
-/* 这里的卡片和分栏结构就比较接近bootstrap了,可惜视觉做的太古板,如果愿意的话改成Material Design就更好了 */
.split-lg > .card {
width: 440px;
flex-shrink: 0;
@@ -29,7 +33,7 @@
.card-header {
padding: .5em;
font-size: 20px;
- border-bottom: 1px solid #e6e6e6;
+ font-weight: bold;
}
.card-body {
padding: 10px 5px;
@@ -41,42 +45,47 @@
flex-wrap: wrap;
}
-.img-list {
- width: 100%;
- display: flex;
- flex-wrap: wrap;
- margin: -3px -5px;
- align-items: flex-start;
-}
-.img-list > img {
- width: 200px;
- margin: 3px 5px;
-}
-
-.message-list {
- margin: 0;
- padding-left: 0;
- list-style: none;
-}
-.message-list > li {
- line-height: 20px;
- padding: 8px 0;
- position: relative;
-}
-/* list disk */
-.message-list > li::before {
- content: "";
- width: 8px;
- height: 8px;
- background-color: #DADADA;
- margin-right: 10px;
- border-radius: 50%;
- display: inline-block;
-}
-
@media (max-width: 980px) {
.split-lg > .card {
width: 100%;
margin-bottom: 20px;
}
+}
+
+.posts {
+
+}
+.post {
+ padding: 10px;
+ border-radius: 12px;
+ background-color: white;
+ margin-bottom: 10px;
+ box-shadow: 2px 2px 4px #0001;
+}
+.post > p {
+ padding-left: 40px;
+}
+.post-header {
+ padding: 5px;
+ display: flex;
+ align-items: center;
+ border-bottom: 1px solid #eee;
+}
+.post-body {
+ padding: 5px;
+}
+.post-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 20px;
+ margin-right: 10px;
+}
+.post-username {
+}
+.post-time {
+ color: #888;
+}
+.post-image {
+ width: 200px;
+ height: 200px;
}
\ No newline at end of file
diff --git a/src/index/index.js b/src/index/index.js
index 17972c8..341dd87 100644
--- a/src/index/index.js
+++ b/src/index/index.js
@@ -1,4 +1,5 @@
import { Component } from 'react';
+import ImagePreview from '../components/ImagePreview/ImagePreview';
import { Link } from '../components/SingleRouter/SingleRouter';
import Spinner from '../components/Spinner/Spinner';
import UserControl from '../components/UserControl/UserControl';
@@ -12,8 +13,7 @@ export class AppContainer extends Component {
constructor(props) {
super(props);
this.state = {
- essentialMessages: [],
- essentialImages: [],
+ posts: [],
fetchingEssential: false,
};
}
@@ -25,26 +25,20 @@ export class AppContainer extends Component {
this.setState({ fetchingEssential: false });
if (networkStatus !== 200) return;
if (!status) return alert('获取精选列表失败:' + data + ',请刷新重试');
- let messages = [], images = [];
- data.forEach(post => {
- if (post.content) messages.push(post.content);
- if (post.image) images.push(post.image);
- });
- this.setState({ essentialMessages: messages, essentialImages: images });
+ this.setState({ posts: data });
});
}
render() {
return (
-
-

+
-
+
精选留言
@@ -56,43 +50,33 @@ export class AppContainer extends Component {
)
: (
- this.state.essentialMessages.length === 0
+ this.state.posts.length === 0
? (
暂时没有精选留言
)
: (
-
+
{
- this.state.essentialMessages.map((msg, i) => (
-
- ))
- }
-
- )
- )
- }
-
-
-
-
- 精选图片
-
-
- {
- this.state.fetchingEssential
- ? (
-
- )
- : (
- this.state.essentialImages.length === 0
- ? (
-
暂时没有精选图片
- )
- : (
-
- {
- this.state.essentialImages.map((src, i) => (
-

+ this.state.posts.map((post, i) => (
+
+
+

+
+
{post.realName}
+
于{new Date(post.time * 1000).toLocaleString()}发布
+
+
+
+
+ {
+ post.image && (
+
+
+
+ )
+ }
+
+
))
}