feat: ntrp逻辑变更 & 替换海报二维码
This commit is contained in:
@@ -3,10 +3,10 @@
|
||||
padding: 20px 20px 0;
|
||||
|
||||
.commentCount {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.10);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding-bottom: 8px;
|
||||
color: #FFF;
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
color: #fff;
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
text-overflow: ellipsis;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
@@ -24,7 +24,7 @@
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.20);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
|
||||
.addCommentImage {
|
||||
width: 18px;
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
.addCommentText {
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
text-overflow: ellipsis;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
@@ -55,6 +55,45 @@
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
|
||||
&.blink::after {
|
||||
content: "";
|
||||
--base: transparent;
|
||||
--alert: rgba(255, 255, 255, 0.2);
|
||||
--dur: 2000ms;
|
||||
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
left: -10px;
|
||||
top: -10px;
|
||||
background: var(--base);
|
||||
// border: 1px solid #ccc;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
animation: bg-blink var(--dur) infinite steps(10);
|
||||
}
|
||||
|
||||
@keyframes bg-blink {
|
||||
0% {
|
||||
background: var(--base);
|
||||
}
|
||||
50% {
|
||||
background: var(--alert);
|
||||
}
|
||||
100% {
|
||||
background: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
/* 无障碍:用户偏好减少动画则关闭 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.blink {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
@@ -83,7 +122,7 @@
|
||||
|
||||
.nickname {
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
@@ -95,9 +134,9 @@
|
||||
padding: 0 4px;
|
||||
height: 18px;
|
||||
border-radius: 3px;
|
||||
background: rgba(255, 255, 255, 0.10);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
@@ -107,8 +146,8 @@
|
||||
}
|
||||
|
||||
.content {
|
||||
color: #FFF;
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
color: #fff;
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
@@ -126,19 +165,24 @@
|
||||
justify-content: flex-start;
|
||||
gap: 6px;
|
||||
|
||||
.time, .location, .reply, .delete {
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
.time,
|
||||
.location,
|
||||
.reply,
|
||||
.delete {
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
line-height: 18px;
|
||||
|
||||
&.time, &.location {
|
||||
&.time,
|
||||
&.location {
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&.reply, &.delete {
|
||||
&.reply,
|
||||
&.delete {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -147,8 +191,8 @@
|
||||
}
|
||||
|
||||
.viewMore {
|
||||
color: #FFF;
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
color: #fff;
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
@@ -204,11 +248,11 @@
|
||||
|
||||
.emptyTip {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-feature-settings: 'liga' off, 'clig' off;
|
||||
font-feature-settings: "liga" off, "clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import React, {
|
||||
import { View, Text, Image, Input } from "@tarojs/components";
|
||||
import Taro from "@tarojs/taro";
|
||||
import dayjs from "dayjs";
|
||||
import classnames from "classnames";
|
||||
import CommentServices from "@/services/commentServices";
|
||||
import { delay } from "@/utils";
|
||||
import type {
|
||||
BaseComment,
|
||||
Comment,
|
||||
@@ -133,6 +135,7 @@ function CommentItem(props: {
|
||||
level: number;
|
||||
publisher_id: number;
|
||||
comment: Comment | ReplyComment;
|
||||
blink_id: number | undefined;
|
||||
loadMore: (c: Comment) => void;
|
||||
handleReply: (options: CommentInputReplyParamsType) => void;
|
||||
handleDelete: (options: { parent_id: number | null; id: number }) => void;
|
||||
@@ -144,12 +147,20 @@ function CommentItem(props: {
|
||||
loadMore: handleLoadMore,
|
||||
handleReply,
|
||||
handleDelete,
|
||||
blink_id,
|
||||
} = props;
|
||||
const currentUserInfo = useUserInfo();
|
||||
const isGamePublisher = publisher_id === comment.user.id;
|
||||
const isCommentPublisher = currentUserInfo.id === comment.user.id;
|
||||
return (
|
||||
<View className={styles.commentItem} key={comment.id}>
|
||||
<View
|
||||
className={classnames(
|
||||
styles.commentItem,
|
||||
blink_id === comment.id && styles.blink
|
||||
)}
|
||||
key={comment.id}
|
||||
id={`comment_id_${comment.id}`}
|
||||
>
|
||||
<View style={{ width: level === 1 ? "36px" : "28px" }}>
|
||||
<Image
|
||||
className={styles.avatar}
|
||||
@@ -218,6 +229,7 @@ function CommentItem(props: {
|
||||
{!isReplyComment(comment) &&
|
||||
comment.replies.map((item: ReplyComment) => (
|
||||
<CommentItem
|
||||
blink_id={blink_id}
|
||||
key={comment.id}
|
||||
publisher_id={publisher_id}
|
||||
comment={item}
|
||||
@@ -242,24 +254,74 @@ function CommentItem(props: {
|
||||
}
|
||||
|
||||
export default forwardRef(function Comments(
|
||||
props: { game_id: number; publisher_id: number },
|
||||
props: { game_id: number; publisher_id: number; message_id?: number },
|
||||
ref
|
||||
) {
|
||||
const { game_id, publisher_id } = props;
|
||||
const { game_id, publisher_id, message_id } = props;
|
||||
const [comments, setComments] = useState<Comment[]>([]);
|
||||
const inputRef = useRef<CommentInputRef>(null);
|
||||
const [blink_id, setBlinkId] = useState<number | undefined>();
|
||||
|
||||
const commentCountUpdateRef = useRef()
|
||||
const commentCountUpdateRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
getComments(1);
|
||||
init();
|
||||
}, [game_id]);
|
||||
|
||||
async function init() {
|
||||
if (!game_id) return;
|
||||
await getComments(1);
|
||||
if (message_id) {
|
||||
scrollToComment();
|
||||
}
|
||||
}
|
||||
|
||||
async function scrollToComment() {
|
||||
const res = await CommentServices.getCommentDetail({
|
||||
comment_id: message_id as number,
|
||||
});
|
||||
if (res.code === 0) {
|
||||
// 判断当前评论是否渲染到页面上,有的话直接跳转,没有的话先插入再跳转
|
||||
const query = Taro.createSelectorQuery();
|
||||
query
|
||||
.select(`#comment_id_${res.data.id}`)
|
||||
.boundingClientRect() // 或 .fields({id:true}) 根据需求
|
||||
.exec(async (resArr) => {
|
||||
// resArr 是数组,长度为选择器数量
|
||||
const nodeInfo = resArr[0];
|
||||
if (!nodeInfo) {
|
||||
// 节点不存在,执行插入逻辑
|
||||
const parent_id = res.data.parent_id;
|
||||
if (parent_id) {
|
||||
setComments((prev) => {
|
||||
return prev.map((item) => {
|
||||
if (item.id !== parent_id) return item;
|
||||
return {
|
||||
...item,
|
||||
replies: [res.data, ...item.replies],
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
await delay(100);
|
||||
Taro.pageScrollTo({
|
||||
selector: `#comment_id_${res.data.id}`,
|
||||
duration: 300,
|
||||
});
|
||||
setBlinkId(res.data.id);
|
||||
setTimeout(() => {
|
||||
setBlinkId(undefined);
|
||||
}, 3300);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
addComment: handleReply,
|
||||
getCommentCount: (onUpdate) => {
|
||||
commentCountUpdateRef.current = onUpdate
|
||||
onUpdate(comments.length)
|
||||
commentCountUpdateRef.current = onUpdate;
|
||||
onUpdate(comments.length);
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -275,7 +337,7 @@ export default forwardRef(function Comments(
|
||||
setComments((prev) => {
|
||||
const res = [...prev];
|
||||
res.splice(page * PAGESIZE - 1, newComments.length, ...newComments);
|
||||
commentCountUpdateRef.current?.(res.length)
|
||||
commentCountUpdateRef.current?.(res.length);
|
||||
return res;
|
||||
});
|
||||
}
|
||||
@@ -303,7 +365,7 @@ export default forwardRef(function Comments(
|
||||
item.reply_count = res.data.count;
|
||||
}
|
||||
});
|
||||
commentCountUpdateRef.current?.(newComments.length)
|
||||
commentCountUpdateRef.current?.(newComments.length);
|
||||
return newComments;
|
||||
});
|
||||
}
|
||||
@@ -325,7 +387,7 @@ export default forwardRef(function Comments(
|
||||
const res = await CommentServices.createComment({ game_id, content: val });
|
||||
if (res.code === 0) {
|
||||
setComments((prev) => {
|
||||
commentCountUpdateRef.current?.(prev.length + 1)
|
||||
commentCountUpdateRef.current?.(prev.length + 1);
|
||||
return [{ ...res.data, replies: [] }, ...prev];
|
||||
});
|
||||
toast("发布成功");
|
||||
@@ -375,7 +437,7 @@ export default forwardRef(function Comments(
|
||||
});
|
||||
} else {
|
||||
setComments((prev) => {
|
||||
commentCountUpdateRef.current?.(prev.length - 1)
|
||||
commentCountUpdateRef.current?.(prev.length - 1);
|
||||
return prev.filter((item) => item.id !== id);
|
||||
});
|
||||
}
|
||||
@@ -400,6 +462,7 @@ export default forwardRef(function Comments(
|
||||
return (
|
||||
<CommentItem
|
||||
key={comment.id}
|
||||
blink_id={blink_id}
|
||||
publisher_id={publisher_id}
|
||||
level={1}
|
||||
comment={comment}
|
||||
|
||||
@@ -192,17 +192,6 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
<View className={styles.picker}>
|
||||
<Picker
|
||||
visible={visible}
|
||||
options={options}
|
||||
value={ntrp}
|
||||
onChange={(val) => {
|
||||
console.log(val);
|
||||
setNtrp(val.values);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</CommonPopup>
|
||||
|
||||
@@ -72,7 +72,7 @@ const Poster = (props, ref) => {
|
||||
.exec(async (res) => {
|
||||
const canvas = res[0].node;
|
||||
const ctx = canvas.getContext("2d");
|
||||
const dpr = Taro.getSystemInfoSync().pixelRatio;
|
||||
const dpr = Taro.getWindowInfo().pixelRatio;
|
||||
const width = 600; // px
|
||||
const height = 1000;
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import { View, Canvas, Button } from "@tarojs/components";
|
||||
import { useEffect, useRef, forwardRef, useImperativeHandle } from "react";
|
||||
|
||||
const RadarChart: React.FC = forwardRef((props, ref) => {
|
||||
const { data } = props
|
||||
const { data } = props;
|
||||
|
||||
const renderFnRef = useRef()
|
||||
const renderFnRef = useRef();
|
||||
// const labels = [
|
||||
// "正手球质",
|
||||
// "正手控制",
|
||||
@@ -25,22 +25,25 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (data.length > 0) {
|
||||
const {texts, vals} = data.reduce((res, item) => {
|
||||
const [text, val] = item
|
||||
return {
|
||||
texts: [...res.texts, text],
|
||||
vals: [...res.vals, val]
|
||||
}
|
||||
}, { texts: [], vals: [] })
|
||||
renderFnRef.current && renderFnRef.current(texts, vals)
|
||||
const { texts, vals } = data.reduce(
|
||||
(res, item) => {
|
||||
const [text, val] = item;
|
||||
return {
|
||||
texts: [...res.texts, text],
|
||||
vals: [...res.vals, val],
|
||||
};
|
||||
},
|
||||
{ texts: [], vals: [] }
|
||||
);
|
||||
renderFnRef.current && renderFnRef.current(texts, vals);
|
||||
}
|
||||
}, [data])
|
||||
}, [data]);
|
||||
|
||||
useReady(() => {
|
||||
renderFnRef.current = renderCanvas
|
||||
renderFnRef.current = renderCanvas;
|
||||
});
|
||||
|
||||
function renderCanvas (labels, values) {
|
||||
function renderCanvas(labels, values) {
|
||||
const query = Taro.createSelectorQuery();
|
||||
query
|
||||
.select("#radarCanvas")
|
||||
@@ -48,7 +51,7 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
||||
.exec((res) => {
|
||||
const canvas = res[0].node as HTMLCanvasElement;
|
||||
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
|
||||
const dpr = Taro.getSystemInfoSync().pixelRatio;
|
||||
const dpr = Taro.getWindowInfo().pixelRatio;
|
||||
canvas.width = res[0].width * dpr;
|
||||
canvas.height = res[0].height * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
@@ -88,7 +91,10 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.textBaseline = "middle";
|
||||
|
||||
if (Math.abs(angle) < 0.01 || Math.abs(Math.abs(angle) - Math.PI) < 0.01) {
|
||||
if (
|
||||
Math.abs(angle) < 0.01 ||
|
||||
Math.abs(Math.abs(angle) - Math.PI) < 0.01
|
||||
) {
|
||||
ctx.textAlign = "center";
|
||||
} else if (angle > -Math.PI / 2 && angle < Math.PI / 2) {
|
||||
ctx.textAlign = "left";
|
||||
@@ -119,22 +125,23 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
generateImage: () => new Promise((resolve, reject) => {
|
||||
const query = Taro.createSelectorQuery()
|
||||
query.select("#radarCanvas")
|
||||
.fields({ node: true, size: true })
|
||||
.exec((res) => {
|
||||
const canvas = res[0].node
|
||||
// ⚠️ 关键:传 canvas,而不是 canvasId
|
||||
Taro.canvasToTempFilePath({
|
||||
canvas,
|
||||
success: (res) => resolve(res.tempFilePath),
|
||||
fail: (err) => reject(err),
|
||||
})
|
||||
})
|
||||
})
|
||||
}))
|
||||
|
||||
generateImage: () =>
|
||||
new Promise((resolve, reject) => {
|
||||
const query = Taro.createSelectorQuery();
|
||||
query
|
||||
.select("#radarCanvas")
|
||||
.fields({ node: true, size: true })
|
||||
.exec((res) => {
|
||||
const canvas = res[0].node;
|
||||
// ⚠️ 关键:传 canvas,而不是 canvasId
|
||||
Taro.canvasToTempFilePath({
|
||||
canvas,
|
||||
success: (res) => resolve(res.tempFilePath),
|
||||
fail: (err) => reject(err),
|
||||
});
|
||||
});
|
||||
}),
|
||||
}));
|
||||
|
||||
// 保存为图片
|
||||
const saveImage = () => {
|
||||
|
||||
Reference in New Issue
Block a user