Merge branch 'feat/liujie'
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { Component, ReactNode } from "react";
|
import { Component, ReactNode } from "react";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
import "./nutui-theme.scss";
|
import "./nutui-theme.scss";
|
||||||
import "./app.scss";
|
import "./app.scss";
|
||||||
import "./scss/qweather-icons/qweather-icons.css";
|
import "./scss/qweather-icons/qweather-icons.css";
|
||||||
@@ -19,6 +20,11 @@ class App extends Component<AppProps> {
|
|||||||
console.log('launch options: ', options)
|
console.log('launch options: ', options)
|
||||||
console.log("小程序启动,初始化逻辑写这里");
|
console.log("小程序启动,初始化逻辑写这里");
|
||||||
clearSpecStorage()
|
clearSpecStorage()
|
||||||
|
Taro.onNeedPrivacyAuthorization((resolve) => {
|
||||||
|
// 这里可以自定义弹窗(或直接默认同意)
|
||||||
|
resolve({ event: 'agree' }); // 同意隐私协议
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
padding: 20px 20px 0;
|
padding: 20px 20px 0;
|
||||||
|
|
||||||
.commentCount {
|
.commentCount {
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.10);
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
color: #FFF;
|
color: #fff;
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
background: rgba(255, 255, 255, 0.20);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
|
||||||
.addCommentImage {
|
.addCommentImage {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
.addCommentText {
|
.addCommentText {
|
||||||
color: rgba(255, 255, 255, 0.65);
|
color: rgba(255, 255, 255, 0.65);
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -55,6 +55,45 @@
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
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 {
|
.avatar {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -83,7 +122,7 @@
|
|||||||
|
|
||||||
.nickname {
|
.nickname {
|
||||||
color: rgba(255, 255, 255, 0.65);
|
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-family: "PingFang SC";
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -95,9 +134,9 @@
|
|||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background: rgba(255, 255, 255, 0.10);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
color: rgba(255, 255, 255, 0.65);
|
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-family: "PingFang SC";
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -107,8 +146,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
color: #FFF;
|
color: #fff;
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -126,19 +165,24 @@
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
.time, .location, .reply, .delete {
|
.time,
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
.location,
|
||||||
|
.reply,
|
||||||
|
.delete {
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
|
||||||
&.time, &.location {
|
&.time,
|
||||||
|
&.location {
|
||||||
color: rgba(255, 255, 255, 0.45);
|
color: rgba(255, 255, 255, 0.45);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.reply, &.delete {
|
&.reply,
|
||||||
|
&.delete {
|
||||||
color: rgba(255, 255, 255, 0.85);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@@ -147,8 +191,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.viewMore {
|
.viewMore {
|
||||||
color: #FFF;
|
color: #fff;
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -204,7 +248,7 @@
|
|||||||
|
|
||||||
.emptyTip {
|
.emptyTip {
|
||||||
color: rgba(255, 255, 255, 0.85);
|
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-family: "PingFang SC";
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import React, {
|
|||||||
import { View, Text, Image, Input } from "@tarojs/components";
|
import { View, Text, Image, Input } from "@tarojs/components";
|
||||||
import Taro from "@tarojs/taro";
|
import Taro from "@tarojs/taro";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import classnames from "classnames";
|
||||||
import CommentServices from "@/services/commentServices";
|
import CommentServices from "@/services/commentServices";
|
||||||
|
import { delay } from "@/utils";
|
||||||
import type {
|
import type {
|
||||||
BaseComment,
|
BaseComment,
|
||||||
Comment,
|
Comment,
|
||||||
@@ -133,6 +135,7 @@ function CommentItem(props: {
|
|||||||
level: number;
|
level: number;
|
||||||
publisher_id: number;
|
publisher_id: number;
|
||||||
comment: Comment | ReplyComment;
|
comment: Comment | ReplyComment;
|
||||||
|
blink_id: number | undefined;
|
||||||
loadMore: (c: Comment) => void;
|
loadMore: (c: Comment) => void;
|
||||||
handleReply: (options: CommentInputReplyParamsType) => void;
|
handleReply: (options: CommentInputReplyParamsType) => void;
|
||||||
handleDelete: (options: { parent_id: number | null; id: number }) => void;
|
handleDelete: (options: { parent_id: number | null; id: number }) => void;
|
||||||
@@ -144,12 +147,20 @@ function CommentItem(props: {
|
|||||||
loadMore: handleLoadMore,
|
loadMore: handleLoadMore,
|
||||||
handleReply,
|
handleReply,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
|
blink_id,
|
||||||
} = props;
|
} = props;
|
||||||
const currentUserInfo = useUserInfo();
|
const currentUserInfo = useUserInfo();
|
||||||
const isGamePublisher = publisher_id === comment.user.id;
|
const isGamePublisher = publisher_id === comment.user.id;
|
||||||
const isCommentPublisher = currentUserInfo.id === comment.user.id;
|
const isCommentPublisher = currentUserInfo.id === comment.user.id;
|
||||||
return (
|
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" }}>
|
<View style={{ width: level === 1 ? "36px" : "28px" }}>
|
||||||
<Image
|
<Image
|
||||||
className={styles.avatar}
|
className={styles.avatar}
|
||||||
@@ -218,6 +229,7 @@ function CommentItem(props: {
|
|||||||
{!isReplyComment(comment) &&
|
{!isReplyComment(comment) &&
|
||||||
comment.replies.map((item: ReplyComment) => (
|
comment.replies.map((item: ReplyComment) => (
|
||||||
<CommentItem
|
<CommentItem
|
||||||
|
blink_id={blink_id}
|
||||||
key={comment.id}
|
key={comment.id}
|
||||||
publisher_id={publisher_id}
|
publisher_id={publisher_id}
|
||||||
comment={item}
|
comment={item}
|
||||||
@@ -242,24 +254,74 @@ function CommentItem(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default forwardRef(function Comments(
|
export default forwardRef(function Comments(
|
||||||
props: { game_id: number; publisher_id: number },
|
props: { game_id: number; publisher_id: number; message_id?: number },
|
||||||
ref
|
ref
|
||||||
) {
|
) {
|
||||||
const { game_id, publisher_id } = props;
|
const { game_id, publisher_id, message_id } = props;
|
||||||
const [comments, setComments] = useState<Comment[]>([]);
|
const [comments, setComments] = useState<Comment[]>([]);
|
||||||
const inputRef = useRef<CommentInputRef>(null);
|
const inputRef = useRef<CommentInputRef>(null);
|
||||||
|
const [blink_id, setBlinkId] = useState<number | undefined>();
|
||||||
|
|
||||||
const commentCountUpdateRef = useRef()
|
const commentCountUpdateRef = useRef();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getComments(1);
|
init();
|
||||||
}, [game_id]);
|
}, [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, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
addComment: handleReply,
|
addComment: handleReply,
|
||||||
getCommentCount: (onUpdate) => {
|
getCommentCount: (onUpdate) => {
|
||||||
commentCountUpdateRef.current = onUpdate
|
commentCountUpdateRef.current = onUpdate;
|
||||||
onUpdate(comments.length)
|
onUpdate(comments.length);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -275,7 +337,7 @@ export default forwardRef(function Comments(
|
|||||||
setComments((prev) => {
|
setComments((prev) => {
|
||||||
const res = [...prev];
|
const res = [...prev];
|
||||||
res.splice(page * PAGESIZE - 1, newComments.length, ...newComments);
|
res.splice(page * PAGESIZE - 1, newComments.length, ...newComments);
|
||||||
commentCountUpdateRef.current?.(res.length)
|
commentCountUpdateRef.current?.(res.length);
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -303,7 +365,7 @@ export default forwardRef(function Comments(
|
|||||||
item.reply_count = res.data.count;
|
item.reply_count = res.data.count;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
commentCountUpdateRef.current?.(newComments.length)
|
commentCountUpdateRef.current?.(newComments.length);
|
||||||
return newComments;
|
return newComments;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -325,7 +387,7 @@ export default forwardRef(function Comments(
|
|||||||
const res = await CommentServices.createComment({ game_id, content: val });
|
const res = await CommentServices.createComment({ game_id, content: val });
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
setComments((prev) => {
|
setComments((prev) => {
|
||||||
commentCountUpdateRef.current?.(prev.length + 1)
|
commentCountUpdateRef.current?.(prev.length + 1);
|
||||||
return [{ ...res.data, replies: [] }, ...prev];
|
return [{ ...res.data, replies: [] }, ...prev];
|
||||||
});
|
});
|
||||||
toast("发布成功");
|
toast("发布成功");
|
||||||
@@ -375,7 +437,7 @@ export default forwardRef(function Comments(
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setComments((prev) => {
|
setComments((prev) => {
|
||||||
commentCountUpdateRef.current?.(prev.length - 1)
|
commentCountUpdateRef.current?.(prev.length - 1);
|
||||||
return prev.filter((item) => item.id !== id);
|
return prev.filter((item) => item.id !== id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -400,6 +462,7 @@ export default forwardRef(function Comments(
|
|||||||
return (
|
return (
|
||||||
<CommentItem
|
<CommentItem
|
||||||
key={comment.id}
|
key={comment.id}
|
||||||
|
blink_id={blink_id}
|
||||||
publisher_id={publisher_id}
|
publisher_id={publisher_id}
|
||||||
level={1}
|
level={1}
|
||||||
comment={comment}
|
comment={comment}
|
||||||
|
|||||||
@@ -192,17 +192,6 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.picker}>
|
|
||||||
<Picker
|
|
||||||
visible={visible}
|
|
||||||
options={options}
|
|
||||||
value={ntrp}
|
|
||||||
onChange={(val) => {
|
|
||||||
console.log(val);
|
|
||||||
setNtrp(val.values);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</CommonPopup>
|
</CommonPopup>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const Poster = (props, ref) => {
|
|||||||
.exec(async (res) => {
|
.exec(async (res) => {
|
||||||
const canvas = res[0].node;
|
const canvas = res[0].node;
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext("2d");
|
||||||
const dpr = Taro.getSystemInfoSync().pixelRatio;
|
const dpr = Taro.getWindowInfo().pixelRatio;
|
||||||
const width = 600; // px
|
const width = 600; // px
|
||||||
const height = 1000;
|
const height = 1000;
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { View, Canvas, Button } from "@tarojs/components";
|
|||||||
import { useEffect, useRef, forwardRef, useImperativeHandle } from "react";
|
import { useEffect, useRef, forwardRef, useImperativeHandle } from "react";
|
||||||
|
|
||||||
const RadarChart: React.FC = forwardRef((props, ref) => {
|
const RadarChart: React.FC = forwardRef((props, ref) => {
|
||||||
const { data } = props
|
const { data } = props;
|
||||||
|
|
||||||
const renderFnRef = useRef()
|
const renderFnRef = useRef();
|
||||||
// const labels = [
|
// const labels = [
|
||||||
// "正手球质",
|
// "正手球质",
|
||||||
// "正手控制",
|
// "正手控制",
|
||||||
@@ -25,22 +25,25 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
const {texts, vals} = data.reduce((res, item) => {
|
const { texts, vals } = data.reduce(
|
||||||
const [text, val] = item
|
(res, item) => {
|
||||||
|
const [text, val] = item;
|
||||||
return {
|
return {
|
||||||
texts: [...res.texts, text],
|
texts: [...res.texts, text],
|
||||||
vals: [...res.vals, val]
|
vals: [...res.vals, val],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ texts: [], vals: [] }
|
||||||
|
);
|
||||||
|
renderFnRef.current && renderFnRef.current(texts, vals);
|
||||||
}
|
}
|
||||||
}, { texts: [], vals: [] })
|
}, [data]);
|
||||||
renderFnRef.current && renderFnRef.current(texts, vals)
|
|
||||||
}
|
|
||||||
}, [data])
|
|
||||||
|
|
||||||
useReady(() => {
|
useReady(() => {
|
||||||
renderFnRef.current = renderCanvas
|
renderFnRef.current = renderCanvas;
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderCanvas (labels, values) {
|
function renderCanvas(labels, values) {
|
||||||
const query = Taro.createSelectorQuery();
|
const query = Taro.createSelectorQuery();
|
||||||
query
|
query
|
||||||
.select("#radarCanvas")
|
.select("#radarCanvas")
|
||||||
@@ -48,7 +51,7 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
|||||||
.exec((res) => {
|
.exec((res) => {
|
||||||
const canvas = res[0].node as HTMLCanvasElement;
|
const canvas = res[0].node as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
|
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
|
||||||
const dpr = Taro.getSystemInfoSync().pixelRatio;
|
const dpr = Taro.getWindowInfo().pixelRatio;
|
||||||
canvas.width = res[0].width * dpr;
|
canvas.width = res[0].width * dpr;
|
||||||
canvas.height = res[0].height * dpr;
|
canvas.height = res[0].height * dpr;
|
||||||
ctx.scale(dpr, dpr);
|
ctx.scale(dpr, dpr);
|
||||||
@@ -88,7 +91,10 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
|||||||
ctx.fillStyle = "#333";
|
ctx.fillStyle = "#333";
|
||||||
ctx.textBaseline = "middle";
|
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";
|
ctx.textAlign = "center";
|
||||||
} else if (angle > -Math.PI / 2 && angle < Math.PI / 2) {
|
} else if (angle > -Math.PI / 2 && angle < Math.PI / 2) {
|
||||||
ctx.textAlign = "left";
|
ctx.textAlign = "left";
|
||||||
@@ -119,22 +125,23 @@ const RadarChart: React.FC = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
generateImage: () => new Promise((resolve, reject) => {
|
generateImage: () =>
|
||||||
const query = Taro.createSelectorQuery()
|
new Promise((resolve, reject) => {
|
||||||
query.select("#radarCanvas")
|
const query = Taro.createSelectorQuery();
|
||||||
|
query
|
||||||
|
.select("#radarCanvas")
|
||||||
.fields({ node: true, size: true })
|
.fields({ node: true, size: true })
|
||||||
.exec((res) => {
|
.exec((res) => {
|
||||||
const canvas = res[0].node
|
const canvas = res[0].node;
|
||||||
// ⚠️ 关键:传 canvas,而不是 canvasId
|
// ⚠️ 关键:传 canvas,而不是 canvasId
|
||||||
Taro.canvasToTempFilePath({
|
Taro.canvasToTempFilePath({
|
||||||
canvas,
|
canvas,
|
||||||
success: (res) => resolve(res.tempFilePath),
|
success: (res) => resolve(res.tempFilePath),
|
||||||
fail: (err) => reject(err),
|
fail: (err) => reject(err),
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
}),
|
||||||
}))
|
}));
|
||||||
|
|
||||||
|
|
||||||
// 保存为图片
|
// 保存为图片
|
||||||
const saveImage = () => {
|
const saveImage = () => {
|
||||||
|
|||||||
@@ -24,7 +24,12 @@ import {
|
|||||||
Comments,
|
Comments,
|
||||||
Poster,
|
Poster,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { generateShareImage, generatePosterImage } from "@/utils";
|
import {
|
||||||
|
generateShareImage,
|
||||||
|
generatePosterImage,
|
||||||
|
base64ToTempFilePath,
|
||||||
|
delay,
|
||||||
|
} from "@/utils";
|
||||||
import DetailService, {
|
import DetailService, {
|
||||||
MATCH_STATUS,
|
MATCH_STATUS,
|
||||||
IsSubstituteSupported,
|
IsSubstituteSupported,
|
||||||
@@ -179,130 +184,130 @@ function Coursel(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PosterPopup = forwardRef((props, ref) => {
|
// const PosterPopup = forwardRef((props, ref) => {
|
||||||
const [visible, setVisible] = useState(false);
|
// const [visible, setVisible] = useState(false);
|
||||||
const [posterData, setPosterData] = useState();
|
// const [posterData, setPosterData] = useState();
|
||||||
const posterRef = useRef();
|
// const posterRef = useRef();
|
||||||
useImperativeHandle(ref, () => ({
|
// useImperativeHandle(ref, () => ({
|
||||||
show: (detail, user) => {
|
// show: (detail, user) => {
|
||||||
setVisible(true);
|
// setVisible(true);
|
||||||
const {
|
// const {
|
||||||
play_type,
|
// play_type,
|
||||||
skill_level_max,
|
// skill_level_max,
|
||||||
skill_level_min,
|
// skill_level_min,
|
||||||
image_list,
|
// image_list,
|
||||||
title,
|
// title,
|
||||||
start_time,
|
// start_time,
|
||||||
end_time,
|
// end_time,
|
||||||
location_name,
|
// location_name,
|
||||||
} = detail;
|
// } = detail;
|
||||||
const { avatar_url, nickname } = user;
|
// const { avatar_url, nickname } = user;
|
||||||
const startTime = dayjs(start_time);
|
// const startTime = dayjs(start_time);
|
||||||
const endTime = dayjs(end_time);
|
// const endTime = dayjs(end_time);
|
||||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
// const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
// const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||||
setPosterData({
|
// setPosterData({
|
||||||
playType: play_type,
|
// playType: play_type,
|
||||||
ntrp: `NTRP ${genNTRPRequirementText(
|
// ntrp: `NTRP ${genNTRPRequirementText(
|
||||||
skill_level_min,
|
// skill_level_min,
|
||||||
skill_level_max
|
// skill_level_max
|
||||||
)}`,
|
// )}`,
|
||||||
mainCoursal:
|
// mainCoursal:
|
||||||
image_list[0] ||
|
// image_list[0] ||
|
||||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/0621b8cf-f7d6-43ad-b852-7dc39f29a782.png",
|
// "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/0621b8cf-f7d6-43ad-b852-7dc39f29a782.png",
|
||||||
nickname,
|
// nickname,
|
||||||
avatarUrl: avatar_url,
|
// avatarUrl: avatar_url,
|
||||||
title,
|
// title,
|
||||||
locationName: location_name,
|
// locationName: location_name,
|
||||||
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
// date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||||
time: `${startTime.format("ah")}点 ${gameLength}`,
|
// time: `${startTime.format("ah")}点 ${gameLength}`,
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
useShareAppMessage(async () => {
|
// useShareAppMessage(async () => {
|
||||||
const tempFilePath = await posterRef.current.generateImage();
|
// const tempFilePath = await posterRef.current.generateImage();
|
||||||
return {
|
// return {
|
||||||
// title: detail.title,
|
// // title: detail.title,
|
||||||
imageUrl: tempFilePath,
|
// imageUrl: tempFilePath,
|
||||||
path: `/game_pages/detail/index?id=${props.id}&from=share`,
|
// path: `/game_pages/detail/index?id=${props.id}&from=share`,
|
||||||
};
|
// };
|
||||||
});
|
// });
|
||||||
|
|
||||||
useShareTimeline(async () => {
|
// useShareTimeline(async () => {
|
||||||
const tempFilePath = await posterRef.current.generateImage();
|
// const tempFilePath = await posterRef.current.generateImage();
|
||||||
return {
|
// return {
|
||||||
title: "分享",
|
// title: "分享",
|
||||||
imageUrl: tempFilePath,
|
// imageUrl: tempFilePath,
|
||||||
path: `/game_pages/detail/index?id=${props.id}&from=share`,
|
// path: `/game_pages/detail/index?id=${props.id}&from=share`,
|
||||||
};
|
// };
|
||||||
});
|
// });
|
||||||
|
|
||||||
function onClose() {
|
// function onClose() {
|
||||||
setVisible(false);
|
// setVisible(false);
|
||||||
setPosterData(undefined);
|
// setPosterData(undefined);
|
||||||
Taro.updateShareMenu({
|
// Taro.updateShareMenu({
|
||||||
isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
|
// isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function handleShare() {
|
// async function handleShare() {
|
||||||
const tempFilePath = await posterRef.current.generateImage();
|
// const tempFilePath = await posterRef.current.generateImage();
|
||||||
Taro.showShareImageMenu({
|
// Taro.showShareImageMenu({
|
||||||
path: tempFilePath,
|
// path: tempFilePath,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
return (
|
// return (
|
||||||
visible && (
|
// visible && (
|
||||||
<CommonPopup
|
// <CommonPopup
|
||||||
title="分享至"
|
// title="分享至"
|
||||||
visible={visible}
|
// visible={visible}
|
||||||
onClose={onClose}
|
// onClose={onClose}
|
||||||
showHeader={false}
|
// showHeader={false}
|
||||||
position="center"
|
// position="center"
|
||||||
hideFooter
|
// hideFooter
|
||||||
enableDragToClose={false}
|
// enableDragToClose={false}
|
||||||
style={{ minHeight: "100px" }}
|
// style={{ minHeight: "100px" }}
|
||||||
zIndex={2001}
|
// zIndex={2001}
|
||||||
>
|
// >
|
||||||
<View className={styles.posterContainer}>
|
// <View className={styles.posterContainer}>
|
||||||
<View className={styles.posterWrap}>
|
// <View className={styles.posterWrap}>
|
||||||
{posterData && <Poster ref={posterRef} data={posterData} />}
|
// {posterData && <Poster ref={posterRef} data={posterData} />}
|
||||||
</View>
|
// </View>
|
||||||
<View className={styles.sharePoster}>
|
// <View className={styles.sharePoster}>
|
||||||
<Button className={styles.shareItem} plain={true}>
|
// <Button className={styles.shareItem} plain={true}>
|
||||||
<View className={styles.icon}>
|
// <View className={styles.icon}>
|
||||||
<Image className={styles.download} src={DownloadIcon} />
|
// <Image className={styles.download} src={DownloadIcon} />
|
||||||
</View>
|
// </View>
|
||||||
<Text>保存至手机</Text>
|
// <Text>保存至手机</Text>
|
||||||
</Button>
|
// </Button>
|
||||||
<Button
|
// <Button
|
||||||
className={styles.shareItem}
|
// className={styles.shareItem}
|
||||||
plain={true}
|
// plain={true}
|
||||||
onClick={handleShare}
|
// onClick={handleShare}
|
||||||
>
|
// >
|
||||||
<View className={classnames(styles.icon, styles.wechatIcon)}>
|
// <View className={classnames(styles.icon, styles.wechatIcon)}>
|
||||||
<Image className={styles.wechat} src={WechatLogo} />
|
// <Image className={styles.wechat} src={WechatLogo} />
|
||||||
</View>
|
// </View>
|
||||||
<Text>微信好友</Text>
|
// <Text>微信好友</Text>
|
||||||
</Button>
|
// </Button>
|
||||||
<Button className={styles.shareItem} plain={true} openType="share">
|
// <Button className={styles.shareItem} plain={true} openType="share">
|
||||||
<View className={styles.icon}>
|
// <View className={styles.icon}>
|
||||||
<Image className={styles.timeline} src={WechatTimeline} />
|
// <Image className={styles.timeline} src={WechatTimeline} />
|
||||||
</View>
|
// </View>
|
||||||
<Text>朋友圈</Text>
|
// <Text>朋友圈</Text>
|
||||||
</Button>
|
// </Button>
|
||||||
</View>
|
// </View>
|
||||||
</View>
|
// </View>
|
||||||
</CommonPopup>
|
// </CommonPopup>
|
||||||
)
|
// )
|
||||||
);
|
// );
|
||||||
});
|
// });
|
||||||
|
|
||||||
// 分享弹窗
|
// 分享弹窗
|
||||||
const SharePopup = forwardRef(({ id, from, detail, userInfo }, ref) => {
|
const SharePopup = forwardRef(({ id, from, detail, userInfo }, ref) => {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const posterRef = useRef();
|
// const posterRef = useRef();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
changeMessageType();
|
changeMessageType();
|
||||||
@@ -370,6 +375,7 @@ const SharePopup = forwardRef(({ id, from, detail, userInfo }, ref) => {
|
|||||||
|
|
||||||
async function handlePost() {
|
async function handlePost() {
|
||||||
const {
|
const {
|
||||||
|
id,
|
||||||
play_type,
|
play_type,
|
||||||
skill_level_max,
|
skill_level_max,
|
||||||
skill_level_min,
|
skill_level_min,
|
||||||
@@ -385,6 +391,15 @@ const SharePopup = forwardRef(({ id, from, detail, userInfo }, ref) => {
|
|||||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||||
Taro.showLoading({ title: "生成中..." });
|
Taro.showLoading({ title: "生成中..." });
|
||||||
|
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
||||||
|
page: "game_pages/detail/index",
|
||||||
|
scene: `id=${id}`,
|
||||||
|
});
|
||||||
|
const qrCodeUrl = await base64ToTempFilePath(
|
||||||
|
qrCodeUrlRes.data.qr_code_base64
|
||||||
|
);
|
||||||
|
console.log(qrCodeUrl, "qrCodeUrl");
|
||||||
|
await delay(100);
|
||||||
const url = await generatePosterImage({
|
const url = await generatePosterImage({
|
||||||
playType: play_type,
|
playType: play_type,
|
||||||
ntrp: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`,
|
ntrp: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`,
|
||||||
@@ -397,6 +412,7 @@ const SharePopup = forwardRef(({ id, from, detail, userInfo }, ref) => {
|
|||||||
locationName: location_name,
|
locationName: location_name,
|
||||||
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||||
time: `${startTime.format("ah")}点 ${gameLength}`,
|
time: `${startTime.format("ah")}点 ${gameLength}`,
|
||||||
|
qrCodeUrl,
|
||||||
});
|
});
|
||||||
Taro.hideLoading();
|
Taro.hideLoading();
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
@@ -1512,8 +1528,8 @@ function Index() {
|
|||||||
/>
|
/>
|
||||||
<Comments
|
<Comments
|
||||||
ref={commentRef}
|
ref={commentRef}
|
||||||
game_id={Number(id)}
|
game_id={Number(detail.id)}
|
||||||
message_id={message_id}
|
message_id={message_id ? Number(message_id) : undefined}
|
||||||
publisher_id={Number(detail.publisher_id)}
|
publisher_id={Number(detail.publisher_id)}
|
||||||
/>
|
/>
|
||||||
{/* sticky bottom action bar */}
|
{/* sticky bottom action bar */}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useRef, useId } from "react";
|
import { useState, useEffect, useRef, useId, useMemo } from "react";
|
||||||
import { View, Text, Image, Button, Canvas } from "@tarojs/components";
|
import { View, Text, Image, Button, Canvas } from "@tarojs/components";
|
||||||
import Taro, { useRouter, useShareAppMessage } from "@tarojs/taro";
|
import Taro, { useRouter, useShareAppMessage } from "@tarojs/taro";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
@@ -289,10 +289,15 @@ function Test() {
|
|||||||
setDisabled(questions[index]?.choosen === -1);
|
setDisabled(questions[index]?.choosen === -1);
|
||||||
}, [index, questions]);
|
}, [index, questions]);
|
||||||
|
|
||||||
|
const doneFlag = useMemo(() => {
|
||||||
|
const [q1, q2, q3] = questions;
|
||||||
|
return [q1, q2, q3].every((q) => q?.choosen === 0);
|
||||||
|
}, [questions]);
|
||||||
|
|
||||||
async function getQUestions() {
|
async function getQUestions() {
|
||||||
const res = await evaluateService.getQuestions();
|
const res = await evaluateService.getQuestions();
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
setQuestions(res.data.map((item) => ({ ...item, choosen: 3 })));
|
setQuestions(res.data.map((item) => ({ ...item, choosen: -1 })));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +314,9 @@ function Test() {
|
|||||||
setDisabled(true);
|
setDisabled(true);
|
||||||
try {
|
try {
|
||||||
const res = await evaluateService.submit({
|
const res = await evaluateService.submit({
|
||||||
answers: questions.map((item) => ({
|
answers: questions
|
||||||
|
.filter((item) => item.choosen >= 0)
|
||||||
|
.map((item) => ({
|
||||||
question_id: item.id,
|
question_id: item.id,
|
||||||
answer_index: item.choosen,
|
answer_index: item.choosen,
|
||||||
})),
|
})),
|
||||||
@@ -332,7 +339,7 @@ function Test() {
|
|||||||
if (disabled && direction > 0) {
|
if (disabled && direction > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (index === questions.length - 1 && direction > 0) {
|
if ((index === questions.length - 1 || doneFlag) && direction > 0) {
|
||||||
handleSubmit();
|
handleSubmit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -384,7 +391,7 @@ function Test() {
|
|||||||
onClick={() => handIndexChange(1)}
|
onClick={() => handIndexChange(1)}
|
||||||
>
|
>
|
||||||
<Button className={styles.nextBtn} type="primary">
|
<Button className={styles.nextBtn} type="primary">
|
||||||
{index === questions.length - 1 ? "完成测试" : "继续"}
|
{index === questions.length - 1 || doneFlag ? "完成测试" : "继续"}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
{index !== 0 && (
|
{index !== 0 && (
|
||||||
@@ -476,7 +483,7 @@ function Result() {
|
|||||||
.exec((res2) => {
|
.exec((res2) => {
|
||||||
const canvas = res2[0].node;
|
const canvas = res2[0].node;
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext("2d");
|
||||||
const dpr = Taro.getSystemInfoSync().pixelRatio;
|
const dpr = Taro.getWindowInfo().pixelRatio;
|
||||||
const width = 300;
|
const width = 300;
|
||||||
const height = 400;
|
const height = 400;
|
||||||
canvas.width = width * dpr;
|
canvas.width = width * dpr;
|
||||||
@@ -515,11 +522,33 @@ function Result() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleSaveImage() {
|
async function handleSaveImage() {
|
||||||
|
console.log(userInfo);
|
||||||
if (!userInfo.id) {
|
if (!userInfo.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Taro.getSetting().then(async (res) => {
|
||||||
|
if (!res.authSetting["scope.writePhotosAlbum"]) {
|
||||||
|
Taro.authorize({
|
||||||
|
scope: "scope.writePhotosAlbum",
|
||||||
|
success: async () => {
|
||||||
const url = await genCardImage();
|
const url = await genCardImage();
|
||||||
Taro.saveImageToPhotosAlbum({ filePath: url });
|
Taro.saveImageToPhotosAlbum({ filePath: url });
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: "提示",
|
||||||
|
content: "需要开启相册权限才能保存图片",
|
||||||
|
success: (r) => {
|
||||||
|
if (r.confirm) Taro.openSetting();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const url = await genCardImage();
|
||||||
|
Taro.saveImageToPhotosAlbum({ filePath: url });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useShareAppMessage(async (res) => {
|
useShareAppMessage(async (res) => {
|
||||||
@@ -553,7 +582,7 @@ function Result() {
|
|||||||
<Image
|
<Image
|
||||||
className={styles.avatarUrl}
|
className={styles.avatarUrl}
|
||||||
src={userInfo.avatar_url}
|
src={userInfo.avatar_url}
|
||||||
mode="aspectFit"
|
mode="aspectFill"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{/* avatar side */}
|
{/* avatar side */}
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ class CommentService {
|
|||||||
async getReplies(req: { comment_id: number, page: number, pageSize: number }): Promise<ApiResponse<ReplyCommentResponse>> {
|
async getReplies(req: { comment_id: number, page: number, pageSize: number }): Promise<ApiResponse<ReplyCommentResponse>> {
|
||||||
return httpService.post("/comments/replies", req, { showLoading: true });
|
return httpService.post("/comments/replies", req, { showLoading: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取评论的所有回复
|
||||||
|
async getCommentDetail(req: { comment_id: number }): Promise<ApiResponse<BaseComment>> {
|
||||||
|
return httpService.post("/comments/detail", req, { showLoading: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new CommentService();
|
export default new CommentService();
|
||||||
|
|||||||
@@ -151,6 +151,18 @@ class GameDetailService {
|
|||||||
showLoading: false
|
showLoading: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getQrCodeUrl(req: { page: string, scene: string }): Promise<ApiResponse<{
|
||||||
|
qr_code_base64: string,
|
||||||
|
image_size: number,
|
||||||
|
page: string,
|
||||||
|
scene: string,
|
||||||
|
width: number
|
||||||
|
}>> {
|
||||||
|
return httpService.post('/user/generate_qrcode', req, {
|
||||||
|
showLoading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出认证服务实例
|
// 导出认证服务实例
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Taro from "@tarojs/taro";
|
import Taro from "@tarojs/taro";
|
||||||
|
|
||||||
const qrCodeUrl =
|
// const qrCodeUrl =
|
||||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/5e013195-fc79-4082-bf06-9aa79aea65ae.png";
|
// "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/5e013195-fc79-4082-bf06-9aa79aea65ae.png";
|
||||||
|
|
||||||
const ringUrl =
|
const ringUrl =
|
||||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b635164f-ecec-434a-a00b-69614a918f2f.png";
|
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b635164f-ecec-434a-a00b-69614a918f2f.png";
|
||||||
@@ -17,6 +17,58 @@ const mapIcon =
|
|||||||
const logoText =
|
const logoText =
|
||||||
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png";
|
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png";
|
||||||
|
|
||||||
|
/** 将 base64 图片转换为临时文件路径 */
|
||||||
|
// export function base64ToTempFilePath(base64Data: string): Promise<string> {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// const fsm = Taro.getFileSystemManager();
|
||||||
|
// // 生成唯一文件名
|
||||||
|
// const filePath = `${Taro.env.USER_DATA_PATH}/temp_qrcode_${Date.now()}.png`;
|
||||||
|
|
||||||
|
// // 去掉 data:image/png;base64, 前缀(如果有)
|
||||||
|
// const base64 = base64Data.replace(/^data:image\/\w+;base64,/, '');
|
||||||
|
|
||||||
|
// fsm.writeFile({
|
||||||
|
// filePath,
|
||||||
|
// data: base64,
|
||||||
|
// encoding: 'base64',
|
||||||
|
// success: () => fsm.access({
|
||||||
|
// path: filePath,
|
||||||
|
// success: () => resolve(filePath),
|
||||||
|
// fail: (e) => reject(e),
|
||||||
|
// }),
|
||||||
|
// fail: reject,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function base64ToTempFilePath(base64Data: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const fsm = Taro.getFileSystemManager();
|
||||||
|
const filePath = `${Taro.env.USER_DATA_PATH}/temp_qrcode_${Date.now()}.png`;
|
||||||
|
|
||||||
|
// 去掉 data:image/png;base64, 前缀(如果有)
|
||||||
|
const base64 = base64Data.replace(/^data:image\/\w+;base64,/, '');
|
||||||
|
|
||||||
|
// 将base64转换成ArrayBuffer
|
||||||
|
const arrayBuffer = Taro.base64ToArrayBuffer(base64);
|
||||||
|
|
||||||
|
fsm.writeFile({
|
||||||
|
filePath,
|
||||||
|
data: arrayBuffer,
|
||||||
|
encoding: 'binary', // 这里使用'binary'
|
||||||
|
success: () => {
|
||||||
|
fsm.access({
|
||||||
|
path: filePath,
|
||||||
|
success: () => resolve(filePath),
|
||||||
|
fail: (e) => reject(e),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: reject,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** 获取图片宽高 */
|
/** 获取图片宽高 */
|
||||||
function getImageWh(src: string): Promise<{ width: number; height: number }> {
|
function getImageWh(src: string): Promise<{ width: number; height: number }> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@@ -173,7 +225,7 @@ function drawTextWrap(
|
|||||||
/** 核心纯函数:生成海报图片 */
|
/** 核心纯函数:生成海报图片 */
|
||||||
export async function generatePosterImage(data: any): Promise<string> {
|
export async function generatePosterImage(data: any): Promise<string> {
|
||||||
console.log("start !!!!");
|
console.log("start !!!!");
|
||||||
const dpr = Taro.getSystemInfoSync().pixelRatio;
|
const dpr = Taro.getWindowInfo().pixelRatio;
|
||||||
const width = 600;
|
const width = 600;
|
||||||
const height = 1000;
|
const height = 1000;
|
||||||
|
|
||||||
@@ -292,7 +344,7 @@ export async function generatePosterImage(data: any): Promise<string> {
|
|||||||
400 / (logoWh.width / logoWh.height)
|
400 / (logoWh.width / logoWh.height)
|
||||||
);
|
);
|
||||||
|
|
||||||
const qrImg = await loadImage(canvas, qrCodeUrl);
|
const qrImg = await loadImage(canvas, data.qrCodeUrl);
|
||||||
ctx.drawImage(qrImg, width - 12 - 150, top - 50, 160, 160);
|
ctx.drawImage(qrImg, width - 12 - 150, top - 50, 160, 160);
|
||||||
|
|
||||||
left = 16;
|
left = 16;
|
||||||
|
|||||||
Reference in New Issue
Block a user