feat: make gpage

This commit is contained in:
zzc
2026-01-21 23:58:21 +08:00
parent 25de38a1f5
commit 6742776e64
2 changed files with 132 additions and 68 deletions

View File

@@ -2,7 +2,11 @@
<view class="make-page" :style="{ paddingTop: getBavBarHeight() + 'px' }">
<!-- 预览卡片 -->
<view class="card-preview">
<image class="card-bg" :src="currentTemplate.cover" mode="aspectFill" />
<image
class="card-bg"
:src="currentTemplate?.imageUrl"
mode="aspectFill"
/>
<view class="card-overlay">
<view class="title">
<text class="main">新春快乐</text>
@@ -31,6 +35,22 @@
<view class="editor-panel">
<view class="drag-handle"></view>
<!-- 底部操作 -->
<view class="bottom-actions">
<button class="btn secondary" @tap="preview">
<uni-icons type="cloud-download" size="20" color="#888"></uni-icons>
<view>保存</view>
</button>
<button open-type="share" class="btn primary" @tap="shareOrSave">
<uni-icons
type="paperplane-filled"
size="20"
color="#fff"
></uni-icons>
<view>分享给好友</view>
</button>
</view>
<!-- 功能入口 -->
<view class="tools">
<view
@@ -49,24 +69,34 @@
<view v-if="activeTool === 'template'" class="section">
<view class="section-title">
<text>热门模板</text>
<text class="more" @tap="showMore">查看更多 ></text>
</view>
<scroll-view scroll-x class="tpl-scroll" show-scrollbar="false">
<view class="tpl-wrap">
<scroll-view
scroll-y
class="tpl-scroll"
show-scrollbar="false"
@scrolltolower="loadMoreTemplates"
>
<view class="tpl-grid">
<view
v-for="(tpl, i) in templates"
:key="i"
class="tpl-card"
:class="{ selected: tpl.id === currentTemplate.id }"
:class="{ selected: tpl?.id === currentTemplate?.id }"
@tap="applyTemplate(tpl)"
>
<image :src="tpl.cover" class="tpl-cover" mode="aspectFill" />
<image :src="tpl.imageUrl" class="tpl-cover" mode="aspectFill" />
<view class="tpl-name">{{ tpl.name }}</view>
<view v-if="tpl.id === currentTemplate.id" class="tpl-check"
<view v-if="tpl?.id === currentTemplate?.id" class="tpl-check"
></view
>
</view>
</view>
<view v-if="loadingTemplates" class="loading-more">加载中...</view>
<view
v-else-if="!hasMoreTemplates && templates.length > 0"
class="no-more"
>没有更多了</view
>
</scroll-view>
</view>
@@ -154,22 +184,6 @@
<button class="btn" @tap="toggleAvatarDecor">切换挂饰</button>
</view>
</view>
<!-- 底部操作 -->
<view class="bottom-actions">
<button class="btn secondary" @tap="preview">
<uni-icons type="cloud-download" size="20" color="#888"></uni-icons>
<view>保存</view>
</button>
<button open-type="share" class="btn primary" @tap="shareOrSave">
<uni-icons
type="paperplane-filled"
size="20"
color="#fff"
></uni-icons>
<view>分享给好友</view>
</button>
</view>
</view>
<canvas
@@ -183,11 +197,14 @@
<script setup>
import { ref, onMounted } from "vue";
import { getBavBarHeight, getDeviceInfo } from "@/utils/system";
import { createCardTmp, updateCard } from "@/api/make";
import { createCardTmp, getCardTemplateList } from "@/api/make";
import { createCardShareToken } from "@/api/card";
import { onShareAppMessage, onLoad } from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user";
const templatePage = ref(1);
const loadingTemplates = ref(false);
const hasMoreTemplates = ref(true);
const userStore = useUserStore();
const cardId = ref("");
@@ -195,11 +212,11 @@ const targetName = ref("祝您");
const signatureName = ref(userStore?.userInfo?.nickName || "xxx");
const userAvatar = ref(
userStore?.userInfo?.avatarUrl ||
"https://file.lihailezzc.com/resource/b48c41054c2633c478463ac1b1f1ca23.png"
"https://file.lihailezzc.com/resource/b48c41054c2633c478463ac1b1f1ca23.png",
);
const blessingText = ref(
"岁末将至敬颂冬绥。平安喜乐万事胜意。祝您2026年大吉大利一马当先前程似锦龙马精神阖家安康"
"岁末将至敬颂冬绥。平安喜乐万事胜意。祝您2026年大吉大利一马当先前程似锦龙马精神阖家安康",
);
const textColors = ["#ffffff", "#ff3b30", "#F5A623", "#8B572A", "#000000"];
@@ -217,6 +234,7 @@ const greetingLib = [
onLoad((options) => {
cardId.value = "69674f307307beac4519025f";
// createCard();
getTemplateList();
});
const createCard = async () => {
@@ -230,6 +248,53 @@ const createCard = async () => {
}
};
const getTemplateList = async (isLoadMore = false) => {
if (loadingTemplates.value || (!hasMoreTemplates.value && isLoadMore)) return;
loadingTemplates.value = true;
try {
const res = await getCardTemplateList(templatePage.value);
// 兼容数组或对象列表格式
const list = Array.isArray(res) ? res : res.list || [];
if (list.length > 0) {
if (isLoadMore) {
templates.value = [...templates.value, ...list];
} else {
templates.value = list;
// 初始加载时设置第一个为当前选中
if (list.length > 0 && !currentTemplate.value) {
currentTemplate.value = list[0];
}
}
// 判断是否还有更多
if (typeof res.hasNext !== "undefined") {
hasMoreTemplates.value = res.hasNext;
} else {
// 如果没有 hasNext 字段,根据返回数量简单判断
hasMoreTemplates.value = list.length >= 8; // 假设每页 10 条
}
if (hasMoreTemplates.value) {
templatePage.value++;
}
} else {
if (!isLoadMore) templates.value = [];
hasMoreTemplates.value = false;
}
} catch (error) {
console.error("加载模板失败:", error);
} finally {
loadingTemplates.value = false;
}
};
const loadMoreTemplates = () => {
getTemplateList(true);
};
onShareAppMessage(async () => {
const deviceInfo = getDeviceInfo();
const shareTokenRes = await createCardShareToken({
@@ -262,33 +327,12 @@ const selectGreeting = (text) => {
const tools = [
{ type: "template", text: "模板", icon: "▦" },
{ type: "text", text: "文字", icon: "文" },
{ type: "image", text: "图片/背景", icon: "图" },
{ type: "avatar", text: "头像挂饰", icon: "饰" },
// { type: "image", text: "图片/背景", icon: "图" },
// { type: "avatar", text: "头像挂饰", icon: "饰" },
];
const activeTool = ref("template");
const templates = ref([
{
id: 1,
name: "金典红金",
cover: "https://file.lihailezzc.com/20260109082842_666_1.jpg",
},
{
id: 2,
name: "富贵花开",
cover: "https://file.lihailezzc.com/20260108222141_644_1.jpg",
},
{
id: 3,
name: "大气字法",
cover: "https://file.lihailezzc.com/20260108222141_644_1.jpg",
},
{
id: 4,
name: "萌趣马年",
cover: "https://file.lihailezzc.com/20260109082842_666_1.jpg",
},
]);
const templates = ref([]);
const currentTemplate = ref(templates.value[0]);
@@ -359,7 +403,7 @@ const saveByCanvas = async (save = true) => {
// 1⃣ 画背景
// ⭐ 先加载背景图
const [bgPath, avatarPath] = await Promise.all([
loadImage(currentTemplate.value.cover),
loadImage(currentTemplate?.value?.imageUrl),
loadImage(userAvatar.value),
]);
@@ -552,7 +596,7 @@ function drawUserBubble(ctx, options) {
bubbleWidth,
bubbleHeight,
bubbleHeight / 2,
bubbleColor
bubbleColor,
);
// 2⃣ 绘制头像
@@ -566,7 +610,7 @@ function drawUserBubble(ctx, options) {
avatarY + avatarSize / 2,
avatarSize / 2,
0,
Math.PI * 2
Math.PI * 2,
);
ctx.clip();
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
@@ -767,46 +811,59 @@ function drawRoundRect(ctx, x, y, w, h, r, color) {
}
.tpl-scroll {
margin-top: 12rpx;
height: 600rpx; /* 增加高度以展示纵向列表 */
}
.tpl-wrap {
display: flex;
.tpl-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
padding-bottom: 20rpx;
}
.tpl-card {
width: 180rpx;
height: 240rpx;
border-radius: 18rpx;
width: 100%; /* 自适应 grid 宽度 */
border-radius: 12rpx;
overflow: hidden;
background: #fff;
margin-right: 16rpx;
position: relative;
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.06);
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
}
.tpl-card.selected {
outline: 4rpx solid #ff3b30;
}
.tpl-cover {
width: 100%;
height: 160rpx;
height: 368rpx;
}
.tpl-name {
font-size: 22rpx;
font-size: 20rpx;
color: #333;
padding: 8rpx 12rpx;
padding: 6rpx 8rpx;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tpl-check {
position: absolute;
right: 10rpx;
top: 10rpx;
width: 36rpx;
height: 36rpx;
right: 6rpx;
top: 6rpx;
width: 32rpx;
height: 32rpx;
border-radius: 50%;
background: #ff3b30;
color: #fff;
font-size: 22rpx;
font-size: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.loading-more,
.no-more {
text-align: center;
font-size: 22rpx;
color: #999;
padding: 10rpx 0;
}
/* 文字编辑区 */
.text-edit-section {