diff --git a/src/components/NTRPEvaluatePopup/index.tsx b/src/components/NTRPEvaluatePopup/index.tsx
index ed3bec4..e15c373 100644
--- a/src/components/NTRPEvaluatePopup/index.tsx
+++ b/src/components/NTRPEvaluatePopup/index.tsx
@@ -192,6 +192,17 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
+
+ {
+ console.log(val);
+ setNtrp(val.values);
+ }}
+ />
+
)}
diff --git a/src/components/Poster/index.tsx b/src/components/Poster/index.tsx
new file mode 100644
index 0000000..be0db94
--- /dev/null
+++ b/src/components/Poster/index.tsx
@@ -0,0 +1,355 @@
+import { useEffect, useImperativeHandle, forwardRef } from "react";
+import { Canvas } from "@tarojs/components";
+import Taro from "@tarojs/taro";
+
+function getImageWh(src): Promise<{ width: number; height: number }> {
+ return new Promise((resolve) => {
+ Taro.getImageInfo({
+ src,
+ success: ({ width, height }) => resolve({ width, height }),
+ });
+ });
+}
+
+const qrCodeUrl =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/5e013195-fc79-4082-bf06-9aa79aea65ae.png";
+
+const ringUrl =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b635164f-ecec-434a-a00b-69614a918f2f.png";
+
+const dateIcon =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1b49476e-0eda-42ff-b08c-002ce510df82.jpg";
+
+const mapIcon =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/06b994fa-9227-4708-8555-8a07af8d0c3b.jpg";
+
+// const logo = "http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/fb732da6-11b9-4022-a524-a377b17635eb.jpg"
+
+const logoText =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png";
+
+const Poster = (props, ref) => {
+ const { data } = props;
+ const {
+ playType,
+ ntrp,
+ mainCoursal,
+ nickname,
+ avatarUrl,
+ title,
+ locationName,
+ date,
+ time,
+ } = data;
+ useEffect(() => {
+ drawCard();
+ }, []);
+
+ useImperativeHandle(ref, () => ({
+ generateImage: () =>
+ new Promise((resolve, reject) => {
+ const query = Taro.createSelectorQuery();
+ query
+ .select("#cardCanvas")
+ .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 drawCard = async () => {
+ const query = Taro.createSelectorQuery();
+ query
+ .select("#cardCanvas")
+ .fields({ node: true, size: true })
+ .exec(async (res) => {
+ const canvas = res[0].node;
+ const ctx = canvas.getContext("2d");
+ const dpr = Taro.getSystemInfoSync().pixelRatio;
+ const width = 600; // px
+ const height = 1000;
+
+ canvas.width = width * dpr;
+ canvas.height = height * dpr;
+ ctx.scale(dpr, dpr);
+
+ // 背景卡片
+ // roundRect(ctx, 0, 0, width, height, 20, "#fff");
+
+ // 整体背景
+ roundRectGradient(ctx, 0, 0, width, height, 24, "#BFFFEF", "#F2FFFC");
+
+ // 顶部图片
+ const img = await loadImage(canvas, mainCoursal);
+ // roundRect(ctx, 20, 20, width - 40, width - 40, 20, "#fff");
+ await drawCoverImage(
+ ctx,
+ mainCoursal,
+ img,
+ 10,
+ 10,
+ width - 20,
+ width - 20,
+ 20
+ );
+
+ // 标签
+ let left = drawTag(ctx, playType, 18, 18);
+ drawTag(ctx, ntrp, left + 4, 18);
+
+ let top = width - 10;
+ left = 16;
+
+ top += 16;
+
+ // 用户头像(圆形)
+ const avatar = await loadImage(canvas, avatarUrl);
+ ctx.save();
+ ctx.beginPath();
+ ctx.arc(left + 30, top + 30, 30, 0, Math.PI * 2);
+ ctx.clip();
+ ctx.drawImage(avatar, left, top, 60, 60);
+ ctx.restore();
+
+ // 头像右边 6
+ left += 66;
+
+ top += 40;
+
+ // 用户名 + 邀请
+ ctx.fillStyle = "#333";
+ ctx.font = "bold 28px sans-serif";
+ // ctx.fillText("华巴轮卡 邀你加入球局", 100, 370);
+ const nickNameText = `${nickname} 邀你加入`;
+ ctx.fillText(nickNameText, left, top);
+ let textW = ctx.measureText(nickNameText).width;
+ left += textW;
+ ctx.fillStyle = "#00B578";
+ ctx.fillText("球局", left, top);
+ const ringImg = await loadImage(canvas, ringUrl);
+ ctx.drawImage(ringImg, left - 10, top - 30, 80, 36);
+
+ left = 16;
+ top += 60;
+
+ // 活动标题
+ ctx.fillStyle = "#333";
+ ctx.font = "bold 34px sans-serif";
+ let r = drawTextWrap(ctx, title, left, top, width - 32, 40);
+
+ top = r.top + 40;
+ left = 16;
+ const dateImg = await loadImage(canvas, dateIcon);
+ await drawCoverImage(ctx, dateIcon, dateImg, left, top, 40, 40, 8);
+
+ left += 40 + 8;
+
+ top += 30;
+
+ // 时间
+ ctx.font = "26px sans-serif";
+ ctx.fillStyle = "#00B578";
+ ctx.fillText(date, left, top);
+ textW = ctx.measureText(date).width;
+ left += 8 + textW;
+ ctx.fillStyle = "#333";
+ ctx.fillText(time, left, top);
+
+ left = 16;
+ top += 24;
+ const mapImg = await loadImage(canvas, mapIcon);
+ await drawCoverImage(ctx, dateIcon, mapImg, left, top, 40, 40, 8);
+
+ left += 40 + 8;
+ top += 30;
+
+ // 地址
+ ctx.fillStyle = "#666";
+ ctx.font = "26px sans-serif";
+ r = drawTextWrap(ctx, locationName, left, top, width - 32 - left, 34);
+
+ left = 16;
+ top = r.top + 60;
+
+ const logoWh = await getImageWh(logoText);
+ console.log(logoWh);
+ const logoTextImg = await loadImage(canvas, logoText);
+ ctx.drawImage(
+ logoTextImg,
+ left,
+ top,
+ 400,
+ // 56
+ 400 / (logoWh.width / logoWh.height)
+ );
+
+ const qrImg = await loadImage(canvas, qrCodeUrl);
+ ctx.drawImage(qrImg, width - 12 - 150, top - 50, 160, 160);
+
+ left = 16;
+ top += 400 / (logoWh.width / logoWh.height) + 30;
+
+ // 底部文字
+ ctx.fillStyle = "#333";
+ ctx.font = "20px sans-serif";
+ ctx.fillText("长按识别二维码,快来加入,有你就有场!", left, top);
+
+ // 小程序码
+ // const qrcode = await loadImage(canvas, "小程序码路径");
+ // ctx.drawImage(qrcode, 480, 880, 100, 100);
+
+ // 导出图片
+ // Taro.canvasToTempFilePath({
+ // canvas,
+ // success: (res) => {
+ // console.log("导出路径", res.tempFilePath);
+ // },
+ // fail: (err) => console.error(err),
+ // });
+ });
+ };
+
+ const roundRectGradient = (ctx, x, y, w, h, r, color1, color2) => {
+ const gradient = ctx.createLinearGradient(x, y, x, y + h);
+ gradient.addColorStop(0, color1);
+ gradient.addColorStop(1, color2);
+
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.lineTo(x + w - r, y);
+ ctx.arcTo(x + w, y, x + w, y + r, r);
+ ctx.lineTo(x + w, y + h - r);
+ ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
+ ctx.lineTo(x + r, y + h);
+ ctx.arcTo(x, y + h, x, y + h - r, r);
+ ctx.lineTo(x, y + r);
+ ctx.arcTo(x, y, x + r, y, r);
+ ctx.closePath();
+
+ ctx.fillStyle = gradient;
+ ctx.fill();
+ };
+
+ const drawCoverImage = async (ctx, src, img, x, y, w, h, r = 0) => {
+ const { width, height } = await getImageWh(src);
+ const imgW = width;
+ const imgH = height;
+
+ // 计算缩放比例,取较大值,保证完全覆盖
+ const scale = Math.max(w / imgW, h / imgH);
+
+ // 缩放后的宽高
+ const newW = imgW * scale;
+ const newH = imgH * scale;
+
+ // 居中偏移
+ const offsetX = x + (w - newW) / 2;
+ const offsetY = y + (h - newH) / 2;
+
+ ctx.save();
+
+ // 如果需要圆角
+ if (r > 0) {
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.lineTo(x + w - r, y);
+ ctx.arcTo(x + w, y, x + w, y + r, r);
+ ctx.lineTo(x + w, y + h - r);
+ ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
+ ctx.lineTo(x + r, y + h);
+ ctx.arcTo(x, y + h, x, y + h - r, r);
+ ctx.lineTo(x, y + r);
+ ctx.arcTo(x, y, x + r, y, r);
+ ctx.closePath();
+ ctx.clip();
+ }
+
+ // 绘制图片 (cover 效果)
+ ctx.drawImage(img, offsetX, offsetY, newW, newH);
+
+ ctx.restore();
+ };
+
+ /** 加载图片 */
+ const loadImage = (canvas, src) => {
+ return new Promise((resolve) => {
+ const img = canvas.createImage();
+ img.onload = () => resolve(img);
+ img.src = src;
+ });
+ };
+
+ /** 圆角矩形 */
+ const roundRect = (ctx, x, y, w, h, r, fillStyle) => {
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.lineTo(x + w - r, y);
+ ctx.arcTo(x + w, y, x + w, y + r, r);
+ ctx.lineTo(x + w, y + h - r);
+ ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
+ ctx.lineTo(x + r, y + h);
+ ctx.arcTo(x, y + h, x, y + h - r, r);
+ ctx.lineTo(x, y + r);
+ ctx.arcTo(x, y, x + r, y, r);
+ ctx.closePath();
+ ctx.fillStyle = fillStyle;
+ ctx.fill();
+ };
+
+ /** 绘制标签 */
+ const drawTag = (ctx, text, x, y) => {
+ ctx.font = "22px sans-serif";
+ const padding = 12;
+ const textWidth = ctx.measureText(text).width;
+ roundRect(ctx, x, y, textWidth + padding * 2, 40, 20, "#fff");
+ ctx.fillStyle = "#333";
+ ctx.fillText(text, x + padding, y + 28);
+ return x + textWidth + padding * 2;
+ };
+
+ /** 文本换行 */
+ const drawTextWrap = (ctx, text, x, y, maxWidth, lineHeight) => {
+ let line = "";
+ const lines = [];
+ for (let char of text) {
+ const testLine = line + char;
+ if (ctx.measureText(testLine).width > maxWidth) {
+ lines.push(line);
+ line = char;
+ } else {
+ line = testLine;
+ }
+ }
+ if (line) lines.push(line);
+ lines.forEach((l, i) => {
+ ctx.fillText(l, x, y + i * lineHeight);
+ });
+ const lastLineText = lines.at(-1);
+ return {
+ left: x + ctx.measureText(lastLineText),
+ top: y + (lines.length - 1) * lineHeight,
+ };
+ };
+
+ return (
+
+ );
+};
+
+export default forwardRef(Poster);
diff --git a/src/components/UploadCover/upload-source-popup.scss b/src/components/UploadCover/upload-source-popup.scss
index bb8fbcd..6e455e3 100644
--- a/src/components/UploadCover/upload-source-popup.scss
+++ b/src/components/UploadCover/upload-source-popup.scss
@@ -1,4 +1,4 @@
-@use '~@/scss/themeColor.scss' as theme;
+@use "~@/scss/themeColor.scss" as theme;
.upload-source-popup-text {
width: 100%;
@@ -131,14 +131,28 @@
align-items: flex-start;
gap: 8px;
- .upload-popup-footer-cancel, .upload-popup-footer-confirm {
- font-feature-settings: 'liga' off, 'clig' off;
+ .upload-popup-footer-cancel,
+ .upload-popup-footer-confirm {
+ font-feature-settings: "liga" off, "clig" off;
font-family: "PingFang SC";
box-sizing: border-box;
height: 44px;
border-radius: 12px;
border: 1px solid rgba(0, 0, 0, 0.12);
flex: 1;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ color: #000;
+ font-feature-settings: "liga" off, "clig" off;
+ font-family: "PingFang SC";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 20px;
+ letter-spacing: -0.23px;
}
.upload-popup-footer-cancel {
@@ -154,4 +168,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/components/index.ts b/src/components/index.ts
index 73f42fe..9f7bb87 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -25,6 +25,7 @@ import GeneralNavbar from "./GeneralNavbar";
import RadarChart from './Radar'
import EmptyState from './EmptyState';
import NTRPTestEntryCard from './NTRPTestEntryCard'
+import Poster from './Poster'
export {
ActivityTypeSwitch,
@@ -55,4 +56,5 @@ export {
RadarChart,
EmptyState,
NTRPTestEntryCard,
+ Poster,
};
diff --git a/src/config/images.js b/src/config/images.js
index 920240f..ff26b3b 100644
--- a/src/config/images.js
+++ b/src/config/images.js
@@ -37,7 +37,6 @@ export default {
ICON_CIRCLE_SELECT_ARROW: require('@/static/publishBall/icon-circle-select-arrow.svg'),
ICON_LOGO: require('@/static/logo.svg'),
ICON_CHANGE: require('@/static/list/icon-change.svg'),
- ICON_WEATHER_SUN: require('@/static/detail/icon-weather-sun.svg'),
ICON_DETAIL_MAP: require('@/static/detail/icon-map.svg'),
ICON_DETAIL_ARROW_RIGHT: require('@/static/detail/icon-arrow-right.svg'),
ICON_DETAIL_NOTICE: require('@/static/detail/icon-notice.svg'),
diff --git a/src/game_pages/detail/config.ts b/src/game_pages/detail/config.ts
new file mode 100644
index 0000000..396f1b1
--- /dev/null
+++ b/src/game_pages/detail/config.ts
@@ -0,0 +1,9 @@
+export const DayOfWeekMap = new Map([
+ [0, "周日"],
+ [1, "周一"],
+ [2, "周二"],
+ [3, "周三"],
+ [4, "周四"],
+ [5, "周五"],
+ [6, "周六"],
+]);
\ No newline at end of file
diff --git a/src/game_pages/detail/index.config.ts b/src/game_pages/detail/index.config.ts
index 3275f5e..6b4a8bb 100644
--- a/src/game_pages/detail/index.config.ts
+++ b/src/game_pages/detail/index.config.ts
@@ -2,4 +2,5 @@ export default definePageConfig({
navigationBarTitleText: '球局详情',
navigationStyle: 'custom',
enableShareAppMessage: true,
+ enableShareTimeline: true,
})
diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx
index f8fa49e..c4866cb 100644
--- a/src/game_pages/detail/index.tsx
+++ b/src/game_pages/detail/index.tsx
@@ -6,7 +6,6 @@ import React, {
forwardRef,
} from "react";
import { View, Text, Image, Map, ScrollView, Button } from "@tarojs/components";
-// import { Avatar } from "@nutui/nutui-react-taro";
import Taro, {
useRouter,
useShareAppMessage,
@@ -23,7 +22,9 @@ import {
NTRPEvaluatePopup,
GameManagePopup,
Comments,
+ Poster,
} from "@/components";
+import { generateShareImage, generatePosterImage } from "@/utils";
import DetailService, {
MATCH_STATUS,
IsSubstituteSupported,
@@ -34,6 +35,12 @@ import { getCurrentLocation, calculateDistance } from "@/utils/locationUtils";
import { useUserInfo, useUserActions } from "@/store/userStore";
import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore";
import img from "@/config/images";
+import DownloadIcon from "@/static/detail/download_icon.svg";
+import WechatLogo from "@/static/detail/wechat_icon.svg";
+import WechatTimeline from "@/static/detail/wechat_timeline.svg";
+import LinkIcon from "@/static/detail/link.svg";
+import CrossIcon from "@/static/detail/cross.svg";
+import { DayOfWeekMap } from "./config";
import styles from "./style.module.scss";
import "./index.scss";
@@ -172,82 +179,281 @@ function Coursel(props) {
);
}
-// 分享弹窗
-const SharePopup = forwardRef(
- ({ id, from }: { id: string; from: string }, ref) => {
- const [visible, setVisible] = useState(false);
-
- useImperativeHandle(ref, () => ({
- show: () => {
- setVisible(true);
- },
- }));
-
- useShareAppMessage((res) => {
- console.log(res, "res");
- return {
- title: "分享",
- imageUrl: "https://img.yzcdn.cn/vant/cat.jpeg",
- path: `/game_pages/detail/index?id=${id}&from=share`,
- };
- });
-
- // function handleShareToWechatMoments() {
- // useShareTimeline(() => {
- // return {
- // title: '分享',
- // path: `/game_pages/detail/index?id=${id}&from=share`,
- // }
- // })
- // }
-
- function handleSaveToLocal() {
- Taro.showToast({ title: "not yet", icon: "error" });
- return;
- Taro.saveImageToPhotosAlbum({
- filePath: "",
- success: () => {
- Taro.showToast({ title: "保存成功", icon: "success" });
- },
- fail: () => {
- Taro.showToast({ title: "保存失败", icon: "none" });
- },
+const PosterPopup = forwardRef((props, ref) => {
+ const [visible, setVisible] = useState(false);
+ const [posterData, setPosterData] = useState();
+ const posterRef = useRef();
+ useImperativeHandle(ref, () => ({
+ show: (detail, user) => {
+ setVisible(true);
+ const {
+ play_type,
+ skill_level_max,
+ skill_level_min,
+ image_list,
+ title,
+ start_time,
+ end_time,
+ location_name,
+ } = detail;
+ const { avatar_url, nickname } = user;
+ const startTime = dayjs(start_time);
+ const endTime = dayjs(end_time);
+ const dayofWeek = DayOfWeekMap.get(startTime.day());
+ const gameLength = `${endTime.diff(startTime, "hour")}小时`;
+ setPosterData({
+ playType: play_type,
+ ntrp: `NTRP ${genNTRPRequirementText(
+ skill_level_min,
+ skill_level_max
+ )}`,
+ mainCoursal:
+ image_list[0] ||
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/0621b8cf-f7d6-43ad-b852-7dc39f29a782.png",
+ nickname,
+ avatarUrl: avatar_url,
+ title,
+ locationName: location_name,
+ date: `${startTime.format("M月D日")} (${dayofWeek})`,
+ time: `${startTime.format("ah")}点 ${gameLength}`,
});
- }
+ },
+ }));
- return (
+ useShareAppMessage(async () => {
+ const tempFilePath = await posterRef.current.generateImage();
+ return {
+ // title: detail.title,
+ imageUrl: tempFilePath,
+ path: `/game_pages/detail/index?id=${props.id}&from=share`,
+ };
+ });
+
+ useShareTimeline(async () => {
+ const tempFilePath = await posterRef.current.generateImage();
+ return {
+ title: "分享",
+ imageUrl: tempFilePath,
+ path: `/game_pages/detail/index?id=${props.id}&from=share`,
+ };
+ });
+
+ function onClose() {
+ setVisible(false);
+ setPosterData(undefined);
+ Taro.updateShareMenu({
+ isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
+ });
+ }
+
+ async function handleShare() {
+ const tempFilePath = await posterRef.current.generateImage();
+ Taro.showShareImageMenu({
+ path: tempFilePath,
+ });
+ }
+ return (
+ visible && (
{
- setVisible(false);
- }}
+ onClose={onClose}
+ showHeader={false}
+ position="center"
hideFooter
+ enableDragToClose={false}
style={{ minHeight: "100px" }}
+ zIndex={2001}
>
-
-
- 分享卡片
+
+
+ {posterData && }
-
-
- );
+ )
+ );
+});
+
+// 分享弹窗
+const SharePopup = forwardRef(({ id, from, detail, userInfo }, ref) => {
+ const [visible, setVisible] = useState(false);
+ const posterRef = useRef();
+
+ useEffect(() => {
+ changeMessageType();
+ }, []);
+
+ async function changeMessageType() {
+ try {
+ const res = await DetailService.getActivityId({
+ business_id: id,
+ business_type: "game",
+ is_private: false,
+ });
+ if (res.code === 0) {
+ Taro.updateShareMenu({
+ withShareTicket: false, // 是否需要返回 shareTicket
+ isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
+ activityId: res.data.activity_id, // 动态消息的活动 id
+ });
+ }
+ } catch (e) {
+ Taro.showToast({ title: e.message, icon: "none" });
+ }
}
-);
+
+ useImperativeHandle(ref, () => ({
+ show: () => {
+ setVisible(true);
+ },
+ }));
+
+ useShareAppMessage(async (res) => {
+ const {
+ play_type,
+ skill_level_max,
+ skill_level_min,
+ start_time,
+ end_time,
+ location_name,
+ venue_image_list,
+ } = detail || {};
+ const startTime = dayjs(start_time);
+ const endTime = dayjs(end_time);
+ const dayofWeek = DayOfWeekMap.get(startTime.day());
+ const gameLength = `${endTime.diff(startTime, "hour")}小时`;
+ const url = await generateShareImage({
+ userAvatar: userInfo.avatar_url,
+ userNickname: userInfo.nickname,
+ gameType: play_type,
+ skillLevel: `NTRP ${genNTRPRequirementText(
+ skill_level_min,
+ skill_level_max
+ )}`,
+ gameDate: `${startTime.format("M月D日")} (${dayofWeek})`,
+ gameTime: `${startTime.format("ah")}点 ${gameLength}`,
+ venueName: location_name,
+ venueImages: venue_image_list ? venue_image_list.map((c) => c.url) : [],
+ });
+ // console.log(res, "res");
+ return {
+ title: detail.title,
+ imageUrl: url || "https://img.yzcdn.cn/vant/cat.jpeg",
+ path: `/game_pages/detail/index?id=${id}&from=share`,
+ };
+ });
+
+ async function handlePost() {
+ const {
+ play_type,
+ skill_level_max,
+ skill_level_min,
+ start_time,
+ end_time,
+ location_name,
+ image_list,
+ title,
+ } = detail || {};
+ const { avatar_url, nickname } = userInfo;
+ const startTime = dayjs(start_time);
+ const endTime = dayjs(end_time);
+ const dayofWeek = DayOfWeekMap.get(startTime.day());
+ const gameLength = `${endTime.diff(startTime, "hour")}小时`;
+ Taro.showLoading({ title: "生成中..." });
+ const url = await generatePosterImage({
+ playType: play_type,
+ ntrp: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`,
+ mainCoursal:
+ image_list[0] ||
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/0621b8cf-f7d6-43ad-b852-7dc39f29a782.png",
+ nickname,
+ avatarUrl: avatar_url,
+ title,
+ locationName: location_name,
+ date: `${startTime.format("M月D日")} (${dayofWeek})`,
+ time: `${startTime.format("ah")}点 ${gameLength}`,
+ });
+ Taro.hideLoading();
+ setVisible(false);
+ Taro.showShareImageMenu({
+ path: url,
+ });
+ }
+
+ function onClose() {
+ setVisible(false);
+ }
+
+ return (
+ <>
+
+
+
+ 分享至
+
+
+
+
+
+
+
+
+
+ 微信好友
+
+
+
+
+
+ 生成分享图
+
+
+
+
+
+ 复制链接
+
+
+
+
+ {/* */}
+ >
+ );
+});
function navto(url) {
Taro.navigateTo({
@@ -336,8 +542,8 @@ function StickyButton(props) {
return;
}
const displayPrice = is_organizer ? 0 : price;
- // user_action_status.can_assess = true;
- // user_action_status.can_join = false;
+ user_action_status.can_assess = true;
+ user_action_status.can_join = false;
// console.log(user_action_status, "user_action");
const {
can_assess,
@@ -1327,6 +1533,8 @@ function Index() {
ref={sharePopupRef}
id={id as string}
from={from as string}
+ detail={detail}
+ userInfo={userInfo}
/>
diff --git a/src/game_pages/detail/style.module.scss b/src/game_pages/detail/style.module.scss
index 5be9551..7772d09 100644
--- a/src/game_pages/detail/style.module.scss
+++ b/src/game_pages/detail/style.module.scss
@@ -2,23 +2,173 @@
.title {
padding: 20px;
color: #000;
- text-align: center;
+ // text-align: center;
font-family: "PingFang SC";
font-size: 18px;
font-style: normal;
font-weight: 600;
line-height: 28px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .closeIconWrap {
+ display: flex;
+ width: 40px;
+ height: 40px;
+ justify-content: center;
+ align-items: center;
+ gap: 6px;
+ flex-shrink: 0;
+ border-radius: 999px;
+ border: 1px solid rgba(0, 0, 0, 0.06);
+ background: #fff;
+ box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06);
+
+ .closeIcon {
+ width: 24px;
+ height: 24px;
+ }
+ }
}
.shareItems {
display: flex;
align-items: center;
- justify-content: space-between;
+ justify-content: space-around;
padding-bottom: 60px;
.button {
- width: 140px;
- height: 40px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ color: rgba(0, 0, 0, 0.85);
+ font-feature-settings: "liga" off, "clig" off;
+ font-family: "PingFang SC";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: normal;
+
+ background-color: #fff;
+ border: none;
+ padding: 0;
+ margin: 0;
+ line-height: normal;
+ font-size: inherit;
+ color: inherit;
+
+ &:after {
+ border: none;
+ background: transparent;
+ }
+
+ .icon {
+ width: 64px;
+ height: 64px;
+ border-radius: 50%;
+ background-color: #fff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid rgba(0, 0, 0, 0.06);
+ box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
+
+ &.wechatIcon {
+ background-color: #07c160;
+ }
+ .download {
+ width: 28px;
+ height: 28px;
+ }
+ .wechat {
+ width: 36px;
+ height: 30px;
+ }
+ .linkIcon {
+ width: 28px;
+ height: 28px;
+ }
+ }
+ }
+ }
+}
+
+.posterContainer {
+ background: linear-gradient(180deg, #fff 0%, #fafafa 100%), #fff;
+ padding: 20px;
+}
+
+.posterWrap {
+ border-radius: 19.067px;
+ border: 1px solid rgba(0, 0, 0, 0.06);
+ background: linear-gradient(180deg, #bfffef 0%, #f2fffc 100%), #fff;
+ box-shadow: 0 6.933px 55.467px 0 rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ box-sizing: border-box;
+}
+
+.sharePoster {
+ margin-top: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+
+ .shareItem {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ color: rgba(0, 0, 0, 0.85);
+ font-feature-settings: "liga" off, "clig" off;
+ font-family: "PingFang SC";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: normal;
+
+ background-color: #fff;
+ border: none;
+ padding: 0;
+ margin: 0;
+ line-height: normal;
+ font-size: inherit;
+ color: inherit;
+
+ &:after {
+ border: none;
+ background: transparent;
+ }
+
+ .icon {
+ width: 64px;
+ height: 64px;
+ border-radius: 50%;
+ background-color: #fff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid rgba(0, 0, 0, 0.06);
+ box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
+
+ &.wechatIcon {
+ background-color: #07c160;
+ }
+ .download {
+ width: 28px;
+ height: 28px;
+ }
+ .wechat {
+ width: 36px;
+ height: 30px;
+ }
+ .timeline {
+ width: 32px;
+ height: 32px;
+ }
}
}
}
diff --git a/src/other_pages/message/index.scss b/src/other_pages/message/index.scss
index b644b3d..026a6a3 100644
--- a/src/other_pages/message/index.scss
+++ b/src/other_pages/message/index.scss
@@ -1,4 +1,4 @@
-@use '~@/scss/images.scss' as img;
+@use "~@/scss/images.scss" as img;
.message-container {
width: 100%;
@@ -6,15 +6,16 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
- background: #FFFFFF;
+ background: #ffffff;
// 顶部导航栏
.navbar {
- height: 100px;
- background: #FFFFFF;
+ height: 56px;
+ background: #ffffff;
position: sticky;
top: 0;
z-index: 100;
+ box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.06);
.navbar-content {
height: 56px;
@@ -34,7 +35,7 @@
}
.navbar-title {
- font-family: 'PingFang SC';
+ font-family: "PingFang SC";
font-weight: 600;
font-size: 20px;
line-height: 1.4;
@@ -51,13 +52,27 @@
align-items: stretch;
gap: 20px;
padding: 6px 24px;
- background: #FFFFFF;
+ background: #ffffff;
.tab-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
+ }
+ }
+ // 消息列表
+ .message-list {
+ flex: 1;
+ overflow: hidden;
+ box-sizing: border-box;
+ // margin-bottom:100px;
+ background-color: none !important;
+
+ .message-list-content {
+ display: flex;
+ flex-direction: column;
+ padding: 12px 12px 112px;
gap: 8px;
padding: 12px 15px;
flex: 1;
@@ -72,7 +87,7 @@
}
.tab-text {
- font-family: 'PingFang SC';
+ font-family: "PingFang SC";
font-weight: 600;
font-size: 16px;
line-height: 1.5;
@@ -98,7 +113,7 @@
.message-scroll {
flex: 1;
overflow: hidden;
- background: #FFFFFF;
+ background: #ffffff;
.message-cards {
padding: 12px;
@@ -110,7 +125,7 @@
// 系统消息卡片
.message-card {
- background: #FFFFFF;
+ background: #ffffff;
border: 0.5px solid rgba(0, 0, 0, 0.08);
border-radius: 20px;
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
@@ -126,7 +141,7 @@
padding: 12px 15px 0;
.card-title {
- font-family: 'PingFang SC';
+ font-family: "PingFang SC";
font-weight: 600;
font-size: 16px;
line-height: 1.5;
@@ -141,7 +156,7 @@
gap: 2px;
.card-time {
- font-family: 'PingFang SC';
+ font-family: "PingFang SC";
font-weight: 400;
font-size: 12px;
line-height: 1.5;
@@ -156,7 +171,7 @@
gap: 2px;
.card-content {
- font-family: 'PingFang SC';
+ font-family: "PingFang SC";
font-weight: 400;
font-size: 14px;
line-height: 1.43;
@@ -182,7 +197,7 @@
cursor: pointer;
.action-text {
- font-family: 'PingFang SC';
+ font-family: "PingFang SC";
font-weight: 600;
font-size: 14px;
line-height: 1.43;
@@ -195,7 +210,7 @@
position: absolute;
left: -16px;
top: -9px;
- width: 14px;;
+ width: 14px;
height: 14px;
}
}
@@ -215,7 +230,7 @@
padding: 24px 0 12px;
.tip-text {
- font-family: 'PingFang SC';
+ font-family: "PingFang SC";
font-weight: 400;
font-size: 14px;
line-height: 1.71;
@@ -232,7 +247,12 @@
width: 60px;
height: 60px;
border-radius: 50%;
- background: radial-gradient(circle at 27% 8%, rgba(189, 255, 74, 1) 17%, rgba(149, 242, 62, 1) 54%, rgba(50, 216, 56, 1) 100%);
+ background: radial-gradient(
+ circle at 27% 8%,
+ rgba(189, 255, 74, 1) 17%,
+ rgba(149, 242, 62, 1) 54%,
+ rgba(50, 216, 56, 1) 100%
+ );
border: 2px solid rgba(0, 0, 0, 0.06);
box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.08);
display: flex;
@@ -253,31 +273,31 @@
// 加号图标 - 竖线
&::before {
- content: '';
+ content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 4px;
height: 25px;
- background: #FFFFFF;
+ background: #ffffff;
border-radius: 2px;
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
}
// 加号图标 - 横线
&::after {
- content: '';
+ content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 25px;
height: 4px;
- background: #FFFFFF;
+ background: #ffffff;
border-radius: 2px;
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/scss/images.scss b/src/scss/images.scss
index 28b8db1..25bc579 100644
--- a/src/scss/images.scss
+++ b/src/scss/images.scss
@@ -1,53 +1,51 @@
// src/scss/images.scss
// 暴露公共API (可选)
-@forward 'sass:map';
-@forward 'sass:meta';
-@use 'sass:map';
+@forward "sass:map";
+@forward "sass:meta";
+@use "sass:map";
// 使用私有变量命名 (前缀加 -)
-$-static-path: '~@/static/' !default;
+$-static-path: "~@/static/" !default;
// 图片映射表
$-images: (
- 'icon-upload': '/publishBall/icon-upload.svg',
- 'icon-add': '/publishBall/icon-add.svg',
- 'icon-location': '/publishBall/icon-location.svg',
- 'icon-gameplay': '/publishBall/icon-gameplay.svg',
- 'icon-personal': '/publishBall/icon-personal.svg',
- 'icon-changda': '/publishBall/icon-changda.svg',
- 'icon-cost': '/publishBall/icon-cost.svg',
- 'icon-remove': '/publishBall/icon-remove.svg',
- 'icon-arrow-left': '/detail/icon-arrow-left.svg',
- 'icon-logo-go': '/detail/icon-logo-go.svg',
- 'icon-search': '/publishBall/icon-search.svg',
- 'icon-map': '/publishBall/icon-map.svg',
- 'icon-stadium': '/publishBall/icon-stadium.svg',
- 'icon-arrow-small': '/publishBall/icon-arrow-small.svg',
- 'icon-map-search': '/publishBall/icon-map-search.svg',
- 'icon-heartcircle': '/publishBall/icon-heartcircle.png',
- 'icon-copy': '/publishBall/icon-arrow-right.svg',
- 'icon-delete': '/publishBall/icon-delete.svg',
- 'icon-circle-unselect': '/publishBall/icon-circle-unselect.svg',
- 'icon-circle-select-ring': '/publishBall/icon-circle-select-ring.svg',
- 'icon-circle-select-arrow': '/publishBall/icon-circle-select-arrow.svg',
- 'icon-weather-sun': '/detail/icon-weather-sun.svg',
- 'icon-detail-map': '/detail/icon-map.svg',
- 'icon-detail-arrow-right': '/detail/icon-arrow-right.svg',
- 'icon-detail-notice': '/detail/icon-notice.svg',
- 'icon-detail-application-add': '/detail/icon-application-add.svg',
- 'icon-detail-comment': '/detail/icon-comment.svg',
- 'icon-detail-comment-dark': '/detail/icon-comment-dark.svg',
- 'icon-detail-share': '/detail/icon-share-dark.svg',
- 'icon-guide-bar-publish': '/common/guide-bar-publish.svg',
- 'icon-navigator-back': '/common/navigator-back.svg',
+ "icon-upload": "/publishBall/icon-upload.svg",
+ "icon-add": "/publishBall/icon-add.svg",
+ "icon-location": "/publishBall/icon-location.svg",
+ "icon-gameplay": "/publishBall/icon-gameplay.svg",
+ "icon-personal": "/publishBall/icon-personal.svg",
+ "icon-changda": "/publishBall/icon-changda.svg",
+ "icon-cost": "/publishBall/icon-cost.svg",
+ "icon-remove": "/publishBall/icon-remove.svg",
+ "icon-arrow-left": "/detail/icon-arrow-left.svg",
+ "icon-logo-go": "/detail/icon-logo-go.svg",
+ "icon-search": "/publishBall/icon-search.svg",
+ "icon-map": "/publishBall/icon-map.svg",
+ "icon-stadium": "/publishBall/icon-stadium.svg",
+ "icon-arrow-small": "/publishBall/icon-arrow-small.svg",
+ "icon-map-search": "/publishBall/icon-map-search.svg",
+ "icon-heartcircle": "/publishBall/icon-heartcircle.png",
+ "icon-copy": "/publishBall/icon-arrow-right.svg",
+ "icon-delete": "/publishBall/icon-delete.svg",
+ "icon-circle-unselect": "/publishBall/icon-circle-unselect.svg",
+ "icon-circle-select-ring": "/publishBall/icon-circle-select-ring.svg",
+ "icon-circle-select-arrow": "/publishBall/icon-circle-select-arrow.svg",
+ "icon-detail-map": "/detail/icon-map.svg",
+ "icon-detail-arrow-right": "/detail/icon-arrow-right.svg",
+ "icon-detail-notice": "/detail/icon-notice.svg",
+ "icon-detail-application-add": "/detail/icon-application-add.svg",
+ "icon-detail-comment": "/detail/icon-comment.svg",
+ "icon-detail-comment-dark": "/detail/icon-comment-dark.svg",
+ "icon-detail-share": "/detail/icon-share-dark.svg",
+ "icon-guide-bar-publish": "/common/guide-bar-publish.svg",
+ "icon-navigator-back": "/common/navigator-back.svg",
) !default;
// 图片获取函数
@function taro-image($name) {
@if not map.has-key($-images, $name) {
@warn "Image `#{$name}` not found in $images map";
- @return url($-static-path + 'default.png');
+ @return url($-static-path + "default.png");
}
@return url($-static-path + map.get($-images, $name));
}
-
diff --git a/src/services/detailService.ts b/src/services/detailService.ts
index 9b3d221..cb89880 100644
--- a/src/services/detailService.ts
+++ b/src/services/detailService.ts
@@ -140,6 +140,17 @@ class GameDetailService {
showLoading: true,
});
}
+
+ async getActivityId(req: { business_type: 'game' | 'order' | 'participant', business_id: number, is_private: boolean }): Promise> {
+ return httpService.post('/user/create_activity_id', req, {
+ showLoading: false
+ })
+ }
}
// 导出认证服务实例
diff --git a/src/static/detail/cross.svg b/src/static/detail/cross.svg
new file mode 100644
index 0000000..9e8c64b
--- /dev/null
+++ b/src/static/detail/cross.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/static/detail/download_icon.svg b/src/static/detail/download_icon.svg
new file mode 100644
index 0000000..1e0704a
--- /dev/null
+++ b/src/static/detail/download_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/static/detail/icon-weather-sun.svg b/src/static/detail/icon-weather-sun.svg
deleted file mode 100644
index 3851486..0000000
--- a/src/static/detail/icon-weather-sun.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-
diff --git a/src/static/detail/link.svg b/src/static/detail/link.svg
new file mode 100644
index 0000000..2e774f6
--- /dev/null
+++ b/src/static/detail/link.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/static/detail/wechat_icon.svg b/src/static/detail/wechat_icon.svg
new file mode 100644
index 0000000..e92b1cd
--- /dev/null
+++ b/src/static/detail/wechat_icon.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/src/static/detail/wechat_timeline.svg b/src/static/detail/wechat_timeline.svg
new file mode 100644
index 0000000..7d5c79b
--- /dev/null
+++ b/src/static/detail/wechat_timeline.svg
@@ -0,0 +1,17 @@
+
diff --git a/src/utils/genPoster.ts b/src/utils/genPoster.ts
new file mode 100644
index 0000000..b25fb0e
--- /dev/null
+++ b/src/utils/genPoster.ts
@@ -0,0 +1,312 @@
+import Taro from "@tarojs/taro";
+
+const qrCodeUrl =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/5e013195-fc79-4082-bf06-9aa79aea65ae.png";
+
+const ringUrl =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b635164f-ecec-434a-a00b-69614a918f2f.png";
+
+const dateIcon =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1b49476e-0eda-42ff-b08c-002ce510df82.jpg";
+
+const mapIcon =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/06b994fa-9227-4708-8555-8a07af8d0c3b.jpg";
+
+// const logo = "http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/fb732da6-11b9-4022-a524-a377b17635eb.jpg"
+
+const logoText =
+ "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png";
+
+/** 获取图片宽高 */
+function getImageWh(src: string): Promise<{ width: number; height: number }> {
+ return new Promise((resolve) => {
+ Taro.getImageInfo({
+ src,
+ success: ({ width, height }) => resolve({ width, height }),
+ });
+ });
+}
+
+/** 加载图片 */
+function loadImage(canvas: any, src: string): Promise {
+ return new Promise((resolve) => {
+ const img = canvas.createImage();
+ img.onload = () => resolve(img);
+ img.src = src;
+ });
+}
+
+/** 圆角矩形渐变 */
+function roundRectGradient(
+ ctx: any,
+ x: number,
+ y: number,
+ w: number,
+ h: number,
+ r: number,
+ color1: string,
+ color2: string
+) {
+ const gradient = ctx.createLinearGradient(x, y, x, y + h);
+ gradient.addColorStop(0, color1);
+ gradient.addColorStop(1, color2);
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.lineTo(x + w - r, y);
+ ctx.arcTo(x + w, y, x + w, y + r, r);
+ ctx.lineTo(x + w, y + h - r);
+ ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
+ ctx.lineTo(x + r, y + h);
+ ctx.arcTo(x, y + h, x, y + h - r, r);
+ ctx.lineTo(x, y + r);
+ ctx.arcTo(x, y, x + r, y, r);
+ ctx.closePath();
+ ctx.fillStyle = gradient;
+ ctx.fill();
+}
+
+/** 绘制 cover 图片(支持圆角) */
+async function drawCoverImage(
+ ctx: any,
+ canvas: any,
+ src: string,
+ img: any,
+ x: number,
+ y: number,
+ w: number,
+ h: number,
+ r = 0
+) {
+ const { width, height } = await getImageWh(src);
+ const scale = Math.max(w / width, h / height);
+ const newW = width * scale;
+ const newH = height * scale;
+ const offsetX = x + (w - newW) / 2;
+ const offsetY = y + (h - newH) / 2;
+
+ ctx.save();
+ if (r > 0) {
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.lineTo(x + w - r, y);
+ ctx.arcTo(x + w, y, x + w, y + r, r);
+ ctx.lineTo(x + w, y + h - r);
+ ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
+ ctx.lineTo(x + r, y + h);
+ ctx.arcTo(x, y + h, x, y + h - r, r);
+ ctx.lineTo(x, y + r);
+ ctx.arcTo(x, y, x + r, y, r);
+ ctx.closePath();
+ ctx.clip();
+ }
+ ctx.drawImage(img, offsetX, offsetY, newW, newH);
+ ctx.restore();
+}
+
+/** 圆角矩形 */
+function roundRect(
+ ctx: any,
+ x: number,
+ y: number,
+ w: number,
+ h: number,
+ r: number,
+ fillStyle: string
+) {
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.lineTo(x + w - r, y);
+ ctx.arcTo(x + w, y, x + w, y + r, r);
+ ctx.lineTo(x + w, y + h - r);
+ ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
+ ctx.lineTo(x + r, y + h);
+ ctx.arcTo(x, y + h, x, y + h - r, r);
+ ctx.lineTo(x, y + r);
+ ctx.arcTo(x, y, x + r, y, r);
+ ctx.closePath();
+ ctx.fillStyle = fillStyle;
+ ctx.fill();
+}
+
+/** 绘制标签 */
+function drawTag(ctx: any, text: string, x: number, y: number) {
+ ctx.font = "22px sans-serif";
+ const padding = 12;
+ const textWidth = ctx.measureText(text).width;
+ roundRect(ctx, x, y, textWidth + padding * 2, 40, 20, "#fff");
+ ctx.fillStyle = "#333";
+ ctx.fillText(text, x + padding, y + 28);
+ return x + textWidth + padding * 2;
+}
+
+/** 文本换行 */
+function drawTextWrap(
+ ctx: any,
+ text: string,
+ x: number,
+ y: number,
+ maxWidth: number,
+ lineHeight: number
+) {
+ let line = "";
+ const lines = [];
+ for (let char of text) {
+ const testLine = line + char;
+ if (ctx.measureText(testLine).width > maxWidth) {
+ lines.push(line);
+ line = char;
+ } else {
+ line = testLine;
+ }
+ }
+ if (line) lines.push(line);
+ lines.forEach((l, i) => {
+ ctx.fillText(l, x, y + i * lineHeight);
+ });
+ const lastLineText = lines.at(-1);
+ return {
+ left: x + ctx.measureText(lastLineText).width,
+ top: y + (lines.length - 1) * lineHeight,
+ };
+}
+
+/** 核心纯函数:生成海报图片 */
+export async function generatePosterImage(data: any): Promise {
+ console.log("start !!!!");
+ const dpr = Taro.getSystemInfoSync().pixelRatio;
+ const width = 600;
+ const height = 1000;
+
+ const canvas = Taro.createOffscreenCanvas({ type: "2d", width: width * dpr, height: height * dpr });
+ const ctx = canvas.getContext("2d");
+ ctx.scale(dpr, dpr);
+
+ // 背景渐变
+ roundRectGradient(ctx, 0, 0, width, height, 24, "#BFFFEF", "#F2FFFC");
+
+ // 顶部图片
+ const mainImg = await loadImage(canvas, data.mainCoursal);
+ console.log(222);
+ await drawCoverImage(
+ ctx,
+ canvas,
+ data.mainCoursal,
+ mainImg,
+ 10,
+ 10,
+ width - 20,
+ width - 20,
+ 20
+ );
+
+ // 标签
+ let left = drawTag(ctx, data.playType, 18, 18);
+ drawTag(ctx, data.ntrp, left + 4, 18);
+
+ let top = width - 10 + 16;
+ left = 16;
+
+ // 用户头像
+ const avatarImg = await loadImage(canvas, data.avatarUrl);
+ ctx.save();
+ ctx.beginPath();
+ ctx.arc(left + 30, top + 30, 30, 0, Math.PI * 2);
+ ctx.clip();
+ ctx.drawImage(avatarImg, left, top, 60, 60);
+ ctx.restore();
+
+ left += 66;
+ top += 40;
+
+ // 用户名 + 邀请
+ ctx.fillStyle = "#333";
+ ctx.font = "bold 28px sans-serif";
+ const nickNameText = `${data.nickname} 邀你加入`;
+ ctx.fillText(nickNameText, left, top);
+ let textW = ctx.measureText(nickNameText).width;
+ left += textW;
+ ctx.fillStyle = "#00B578";
+ ctx.fillText("球局", left, top);
+
+ const ringImg = await loadImage(canvas, ringUrl);
+ ctx.drawImage(ringImg, left - 10, top - 30, 80, 36);
+
+ left = 16;
+ top += 60;
+
+ // 活动标题
+ ctx.fillStyle = "#333";
+ ctx.font = "bold 34px sans-serif";
+ let r = drawTextWrap(ctx, data.title, left, top, width - 32, 40);
+
+ top = r.top + 40;
+ left = 16;
+
+ const dateImg = await loadImage(canvas, dateIcon);
+ await drawCoverImage(
+ ctx,
+ canvas,
+ dateIcon,
+ dateImg,
+ left,
+ top,
+ 40,
+ 40,
+ 8
+ );
+
+ left += 40 + 8;
+ top += 30;
+
+ ctx.font = "26px sans-serif";
+ ctx.fillStyle = "#00B578";
+ ctx.fillText(data.date, left, top);
+ textW = ctx.measureText(data.date).width;
+ left += 8 + textW;
+ ctx.fillStyle = "#333";
+ ctx.fillText(data.time, left, top);
+
+ left = 16;
+ top += 24;
+
+ const mapImg = await loadImage(canvas, mapIcon);
+ await drawCoverImage(ctx, canvas, mapIcon, mapImg, left, top, 40, 40, 8);
+
+ left += 40 + 8;
+ top += 30;
+
+ ctx.fillStyle = "#666";
+ ctx.font = "26px sans-serif";
+ r = drawTextWrap(ctx, data.locationName, left, top, width - 32 - left, 34);
+
+ left = 16;
+ top = r.top + 60;
+
+ const logoWh = await getImageWh(logoText);
+ const logoTextImg = await loadImage(canvas, logoText);
+ ctx.drawImage(
+ logoTextImg,
+ left,
+ top,
+ 400,
+ 400 / (logoWh.width / logoWh.height)
+ );
+
+ const qrImg = await loadImage(canvas, qrCodeUrl);
+ ctx.drawImage(qrImg, width - 12 - 150, top - 50, 160, 160);
+
+ left = 16;
+ top += 400 / (logoWh.width / logoWh.height) + 30;
+
+ ctx.fillStyle = "#333";
+ ctx.font = "20px sans-serif";
+ ctx.fillText("长按识别二维码,快来加入,有你就有场!", left, top);
+
+ // 导出图片
+ const { tempFilePath } = await Taro.canvasToTempFilePath({
+ canvas,
+ fileType: 'png',
+ quality: 1,
+ });
+ return tempFilePath;
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 08ea333..198e242 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -7,3 +7,5 @@ export * from "./tokenManager";
export * from "./order.pay";
export * from './orderActions';
export * from './routeUtil';
+export * from './share'
+export * from './genPoster'
diff --git a/types/taro-canvas.d.ts b/types/taro-canvas.d.ts
new file mode 100644
index 0000000..67055a0
--- /dev/null
+++ b/types/taro-canvas.d.ts
@@ -0,0 +1,71 @@
+declare module "@tarojs/taro" {
+ interface RenderingContext {
+ // 变换
+ scale(x: number, y: number): void;
+ rotate(angle: number): void;
+ translate(x: number, y: number): void;
+ save(): void;
+ restore(): void;
+
+ // 路径
+ beginPath(): void;
+ closePath(): void;
+ moveTo(x: number, y: number): void;
+ lineTo(x: number, y: number): void;
+ arc(
+ x: number,
+ y: number,
+ radius: number,
+ startAngle: number,
+ endAngle: number,
+ anticlockwise?: boolean
+ ): void;
+ arcTo(
+ x1: number,
+ y1: number,
+ x2: number,
+ y2: number,
+ radius: number
+ ): void;
+ clip(): void;
+
+ // 绘制
+ drawImage(
+ image: any,
+ dx: number,
+ dy: number,
+ dWidth?: number,
+ dHeight?: number
+ ): void;
+ fill(): void;
+ stroke(): void;
+
+ // 样式
+ fillStyle: string | CanvasGradient | CanvasPattern;
+ strokeStyle: string | CanvasGradient | CanvasPattern;
+ font: string;
+ textAlign: CanvasTextAlign;
+ textBaseline: CanvasTextBaseline;
+
+ // 文本
+ fillText(text: string, x: number, y: number, maxWidth?: number): void;
+ strokeText(text: string, x: number, y: number, maxWidth?: number): void;
+ measureText(text: string): TextMetrics;
+
+ // 渐变
+ createLinearGradient(
+ x0: number,
+ y0: number,
+ x1: number,
+ y1: number
+ ): CanvasGradient;
+ createRadialGradient(
+ x0: number,
+ y0: number,
+ r0: number,
+ x1: number,
+ y1: number,
+ r1: number
+ ): CanvasGradient;
+ }
+}