diff --git a/src/components/Header.vue b/src/components/Header.vue
index 2b19086..5c9c481 100644
--- a/src/components/Header.vue
+++ b/src/components/Header.vue
@@ -71,7 +71,10 @@ const updateHot = (value) => {
onMounted(() => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
- if (currentPage.route === "pages/point-book-edit") {
+ if (
+ currentPage.route === "pages/point-book-edit" ||
+ currentPage.route === "pages/point-book-detail"
+ ) {
pointBook.value = uni.getStorageSync("point-book");
}
if (
diff --git a/src/pages/point-book-detail.vue b/src/pages/point-book-detail.vue
index b7c8530..e8ada3b 100644
--- a/src/pages/point-book-detail.vue
+++ b/src/pages/point-book-detail.vue
@@ -7,19 +7,26 @@ import ScreenHint2 from "@/components/ScreenHint2.vue";
import RingBarChart from "@/components/RingBarChart.vue";
import { getPointBookDetailAPI, addNoteAPI } from "@/apis";
+import { generateShareCardImage } from "@/util";
+
+import useStore from "@/store";
+import { storeToRefs } from "pinia";
+const store = useStore();
+const { user, device } = storeToRefs(store);
const selectedIndex = ref(0);
const showTip = ref(false);
const showTip2 = ref(false);
const showTip3 = ref(false);
-const groups = ref([]);
const data = ref({});
const targetId = ref(0);
const targetSrc = ref("");
const arrows = ref([]);
const notes = ref("");
const draftNotes = ref("");
-const recordId = ref("");
+const record = ref({
+ groups: [],
+});
const openTip = (index) => {
if (index === 1) showTip.value = true;
@@ -37,23 +44,31 @@ const saveNote = async () => {
notes.value = draftNotes.value;
draftNotes.value = "";
showTip3.value = false;
- if (recordId.value) {
- await addNoteAPI(recordId.value, notes.value);
+ if (record.value.id) {
+ await addNoteAPI(record.value.id, notes.value);
}
};
const onSelect = (index) => {
selectedIndex.value = index;
- data.value = groups.value[index];
- arrows.value = groups.value[index].list.filter((item) => item.x && item.y);
+ data.value = record.value.groups[index];
+ arrows.value = record.value.groups[index].list.filter(
+ (item) => item.x && item.y
+ );
};
const goBack = () => {
const pages = getCurrentPages();
- const currentPage = pages[pages.length - 2];
- uni.navigateBack({
- delta: currentPage.route === "pages/point-book" ? 1 : 2,
- });
+ if (pages.length > 1) {
+ const currentPage = pages[pages.length - 2];
+ uni.navigateBack({
+ delta: currentPage.route === "pages/point-book" ? 1 : 2,
+ });
+ } else {
+ uni.redirectTo({
+ url: "/pages/index",
+ });
+ }
};
const ringRates = computed(() => {
@@ -68,7 +83,7 @@ const ringRates = computed(() => {
onLoad(async (options) => {
if (options.id) {
const result = await getPointBookDetailAPI(options.id || 194);
- recordId.value = result.id;
+ record.value = result;
notes.value = result.remark || "";
const config = uni.getStorageSync("point-book-config");
config.targetOption.some((item) => {
@@ -78,27 +93,36 @@ onLoad(async (options) => {
}
});
if (result.groups) {
- groups.value = result.groups;
data.value = result.groups[0];
arrows.value = result.groups[0].list;
}
}
});
-onShareAppMessage(() => {
+onShareAppMessage(async () => {
+ const imageUrl = await generateShareCardImage(
+ "shareCanvas",
+ record.value.recordDate,
+ data.value.userTotalRing,
+ data.value.totalRing
+ );
return {
title: "射箭打卡,今日又精进了一些~",
- path: "/pages/point-book-detail?id=" + recordId.value,
- imageUrl:
- "https://static.shelingxingqiu.com/attachment/2025-09-12/dcqoz26q0268wxmzjg.png",
+ path: "/pages/point-book-detail?id=" + record.value.id,
+ imageUrl,
};
});
-onShareTimeline(() => {
+onShareTimeline(async () => {
+ const imageUrl = await generateShareCardImage(
+ "shareCanvas",
+ record.value.recordDate,
+ data.value.userTotalRing,
+ data.value.totalRing
+ );
return {
title: "射箭打卡,今日又精进了一些~",
- query: "from=timeline",
- imageUrl:
- "https://static.shelingxingqiu.com/attachment/2025-09-12/dcqoz26q0268wxmzjg.png",
+ query: "id=" + record.value.id,
+ imageUrl,
};
});
@@ -108,7 +132,7 @@ onShareTimeline(() => {
:bgType="2"
bgColor="#F5F5F5"
:whiteBackArrow="false"
- title="分析"
+ title=""
:onBack="goBack"
>
@@ -129,6 +153,11 @@ onShareTimeline(() => {
>
-->
+
{
总环数
{{ data.userTotalRing }}/{{ data.totalRing }}
- -->
-
+
{{ index }}:
{{
@@ -231,9 +264,22 @@ onShareTimeline(() => {
-
+
关闭
- 分享
+
+ 分享
+
{
展示用户某次练习中射箭的点位
- 笔记
+ 备注
{{ notes }}
-
+
-
+
暂无数据
diff --git a/src/pages/point-book.vue b/src/pages/point-book.vue
index da2b05e..7a31f98 100644
--- a/src/pages/point-book.vue
+++ b/src/pages/point-book.vue
@@ -332,9 +332,12 @@ onShareTimeline(() => {
-
+
-
+
{
}
return 0;
};
+
+export const generateShareCardImage = (canvasId, date, actual, total) => {
+ try {
+ const ctx = uni.createCanvasContext(canvasId);
+ const imgUrl =
+ "https://static.shelingxingqiu.com/attachment/2025-11-04/ddzpm4tyh5vunyacsr.png";
+ const drawWidth = 375;
+ const drawHeight = 300;
+ // 兼容第三个参数传入 "actual/total" 的情况
+ let a = actual;
+ let t = total;
+ if (
+ (t === undefined || t === null) &&
+ typeof a === "string" &&
+ a.includes("/")
+ ) {
+ const parts = a.split("/");
+ a = parts[0];
+ t = parts[1] || "";
+ }
+
+ return new Promise((resolve, reject) => {
+ uni.getImageInfo({
+ src: imgUrl,
+ success: (res) => {
+ const path = res.path || res.tempFilePath || imgUrl;
+ if (typeof ctx.clearRect === "function") {
+ ctx.clearRect(0, 0, drawWidth, drawHeight);
+ }
+ ctx.drawImage(path, 0, 0, drawWidth, drawHeight);
+ // 绘制左上角日期(白色,22px)
+ renderText(ctx, String(date || ""), 18, "#FFFFFF", 6, 20, "left");
+ // 居中绘制第三个参数为圆角黑底标签
+ const rectW = 200;
+ const rectH = 78;
+ const radius = 39;
+ const rectX = (drawWidth - rectW) / 2;
+ const rectY = (drawHeight - rectH) / 2;
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(rectX + radius, rectY);
+ ctx.lineTo(rectX + rectW - radius, rectY);
+ ctx.quadraticCurveTo(
+ rectX + rectW,
+ rectY,
+ rectX + rectW,
+ rectY + radius
+ );
+ ctx.lineTo(rectX + rectW, rectY + rectH - radius);
+ ctx.quadraticCurveTo(
+ rectX + rectW,
+ rectY + rectH,
+ rectX + rectW - radius,
+ rectY + rectH
+ );
+ ctx.lineTo(rectX + radius, rectY + rectH);
+ ctx.quadraticCurveTo(
+ rectX,
+ rectY + rectH,
+ rectX,
+ rectY + rectH - radius
+ );
+ ctx.lineTo(rectX, rectY + radius);
+ ctx.quadraticCurveTo(rectX, rectY, rectX + radius, rectY);
+ ctx.closePath();
+ ctx.setFillStyle("rgba(0,0,0,0.5)");
+ ctx.fill();
+ ctx.restore();
+
+ // 居中排版:左侧显示 actual/,右侧显示 total,再追加“环”
+ ctx.save();
+ if (typeof ctx.setTextBaseline === "function")
+ ctx.setTextBaseline("middle");
+ const centerX = rectX + rectW / 2;
+ const centerY = rectY + rectH / 2;
+ // 左半部:右对齐 actual/
+ renderText(ctx, `${a}/`, 40, "#FFFFFF", centerX, centerY, "right");
+ // 右半部:左对齐 total
+ renderText(
+ ctx,
+ `${t || ""}`,
+ 30,
+ "#FFFFFF",
+ centerX,
+ centerY,
+ "left"
+ );
+ // 追加单位“环”:放在 total 文本之后 16px
+ const totalWidth = ctx.measureText(`${t || ""}`).width || 0;
+ renderText(
+ ctx,
+ "环",
+ 20,
+ "#FFFFFF",
+ centerX + totalWidth + 5,
+ centerY,
+ "left"
+ );
+ ctx.restore();
+ ctx.draw(false, () => {
+ try {
+ uni.canvasToTempFilePath({
+ canvasId,
+ width: drawWidth,
+ height: drawHeight,
+ destWidth: drawWidth,
+ destHeight: drawHeight,
+ fileType: "png",
+ quality: 1,
+ success: (r) => {
+ const path = r.tempFilePath || r.apFilePath || r.filePath;
+ resolve(path);
+ },
+ fail: (err) => reject(err),
+ });
+ } catch (err) {
+ reject(err);
+ }
+ });
+ },
+ fail: () => {
+ try {
+ ctx.drawImage(imgUrl, 0, 0, drawWidth, drawHeight);
+ // 绘制左上角日期(白色,22px)
+ renderText(ctx, String(date || ""), 22, "#FFFFFF", 10, 22, "left");
+ // 居中绘制第三个参数为圆角黑底标签
+ const rectW = 200;
+ const rectH = 78;
+ const radius = 39;
+ const rectX = (drawWidth - rectW) / 2;
+ const rectY = (drawHeight - rectH) / 2;
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(rectX + radius, rectY);
+ ctx.lineTo(rectX + rectW - radius, rectY);
+ ctx.quadraticCurveTo(
+ rectX + rectW,
+ rectY,
+ rectX + rectW,
+ rectY + radius
+ );
+ ctx.lineTo(rectX + rectW, rectY + rectH - radius);
+ ctx.quadraticCurveTo(
+ rectX + rectW,
+ rectY + rectH,
+ rectX + rectW - radius,
+ rectY + rectH
+ );
+ ctx.lineTo(rectX + radius, rectY + rectH);
+ ctx.quadraticCurveTo(
+ rectX,
+ rectY + rectH,
+ rectX,
+ rectY + rectH - radius
+ );
+ ctx.lineTo(rectX, rectY + radius);
+ ctx.quadraticCurveTo(rectX, rectY, rectX + radius, rectY);
+ ctx.closePath();
+ ctx.setFillStyle("rgba(0,0,0,0.5)");
+ ctx.fill();
+ ctx.restore();
+ // 居中排版:左侧显示 actual/,右侧显示 total,再追加“环”
+ ctx.save();
+ if (typeof ctx.setTextBaseline === "function")
+ ctx.setTextBaseline("middle");
+ const centerX = rectX + rectW / 2;
+ const centerY = rectY + rectH / 2;
+ // 左半部:右对齐 actual/
+ renderText(ctx, `${a}/`, 40, "#FFFFFF", centerX, centerY, "right");
+ // 右半部:左对齐 total
+ renderText(
+ ctx,
+ `${t || ""}`,
+ 35,
+ "#FFFFFF",
+ centerX,
+ centerY,
+ "left"
+ );
+ // 追加单位“环”:放在 total 文本之后 16px
+ const totalWidth = ctx.measureText(`${t || ""}`).width || 0;
+ renderText(
+ ctx,
+ "环",
+ 20,
+ "#FFFFFF",
+ centerX + totalWidth + 5,
+ centerY,
+ "left"
+ );
+ ctx.restore();
+ ctx.draw(false, () => {
+ try {
+ uni.canvasToTempFilePath({
+ canvasId,
+ width: drawWidth,
+ height: drawHeight,
+ destWidth: drawWidth,
+ destHeight: drawHeight,
+ fileType: "png",
+ quality: 1,
+ success: (r) => {
+ const path = r.tempFilePath || r.apFilePath || r.filePath;
+ resolve(path);
+ },
+ fail: (err) => reject(err),
+ });
+ } catch (err) {
+ reject(err);
+ }
+ });
+ } catch (e) {
+ reject(e);
+ }
+ },
+ });
+ });
+ } catch (e) {
+ console.error("generateShareCardImage 绘制失败:", e);
+ }
+};