feat: make gpage
This commit is contained in:
@@ -15,3 +15,10 @@ export const updateCard = async (data) => {
|
|||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCardTemplateList = async (page = 1) => {
|
||||||
|
return request({
|
||||||
|
url: "/api/blessing/card/template/list?page=" + page,
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
<view class="make-page" :style="{ paddingTop: getBavBarHeight() + 'px' }">
|
<view class="make-page" :style="{ paddingTop: getBavBarHeight() + 'px' }">
|
||||||
<!-- 预览卡片 -->
|
<!-- 预览卡片 -->
|
||||||
<view class="card-preview">
|
<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="card-overlay">
|
||||||
<view class="title">
|
<view class="title">
|
||||||
<text class="main">新春快乐</text>
|
<text class="main">新春快乐</text>
|
||||||
@@ -31,6 +35,22 @@
|
|||||||
<view class="editor-panel">
|
<view class="editor-panel">
|
||||||
<view class="drag-handle"></view>
|
<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 class="tools">
|
||||||
<view
|
<view
|
||||||
@@ -49,24 +69,34 @@
|
|||||||
<view v-if="activeTool === 'template'" class="section">
|
<view v-if="activeTool === 'template'" class="section">
|
||||||
<view class="section-title">
|
<view class="section-title">
|
||||||
<text>热门模板</text>
|
<text>热门模板</text>
|
||||||
<text class="more" @tap="showMore">查看更多 ></text>
|
|
||||||
</view>
|
</view>
|
||||||
<scroll-view scroll-x class="tpl-scroll" show-scrollbar="false">
|
<scroll-view
|
||||||
<view class="tpl-wrap">
|
scroll-y
|
||||||
|
class="tpl-scroll"
|
||||||
|
show-scrollbar="false"
|
||||||
|
@scrolltolower="loadMoreTemplates"
|
||||||
|
>
|
||||||
|
<view class="tpl-grid">
|
||||||
<view
|
<view
|
||||||
v-for="(tpl, i) in templates"
|
v-for="(tpl, i) in templates"
|
||||||
:key="i"
|
:key="i"
|
||||||
class="tpl-card"
|
class="tpl-card"
|
||||||
:class="{ selected: tpl.id === currentTemplate.id }"
|
:class="{ selected: tpl?.id === currentTemplate?.id }"
|
||||||
@tap="applyTemplate(tpl)"
|
@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 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>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="loadingTemplates" class="loading-more">加载中...</view>
|
||||||
|
<view
|
||||||
|
v-else-if="!hasMoreTemplates && templates.length > 0"
|
||||||
|
class="no-more"
|
||||||
|
>没有更多了</view
|
||||||
|
>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -154,22 +184,6 @@
|
|||||||
<button class="btn" @tap="toggleAvatarDecor">切换挂饰</button>
|
<button class="btn" @tap="toggleAvatarDecor">切换挂饰</button>
|
||||||
</view>
|
</view>
|
||||||
</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>
|
</view>
|
||||||
|
|
||||||
<canvas
|
<canvas
|
||||||
@@ -183,11 +197,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import { getBavBarHeight, getDeviceInfo } from "@/utils/system";
|
import { getBavBarHeight, getDeviceInfo } from "@/utils/system";
|
||||||
import { createCardTmp, updateCard } from "@/api/make";
|
import { createCardTmp, getCardTemplateList } from "@/api/make";
|
||||||
import { createCardShareToken } from "@/api/card";
|
import { createCardShareToken } from "@/api/card";
|
||||||
import { onShareAppMessage, onLoad } from "@dcloudio/uni-app";
|
import { onShareAppMessage, onLoad } from "@dcloudio/uni-app";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
|
|
||||||
|
const templatePage = ref(1);
|
||||||
|
const loadingTemplates = ref(false);
|
||||||
|
const hasMoreTemplates = ref(true);
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const cardId = ref("");
|
const cardId = ref("");
|
||||||
|
|
||||||
@@ -195,11 +212,11 @@ const targetName = ref("祝您");
|
|||||||
const signatureName = ref(userStore?.userInfo?.nickName || "xxx");
|
const signatureName = ref(userStore?.userInfo?.nickName || "xxx");
|
||||||
const userAvatar = ref(
|
const userAvatar = ref(
|
||||||
userStore?.userInfo?.avatarUrl ||
|
userStore?.userInfo?.avatarUrl ||
|
||||||
"https://file.lihailezzc.com/resource/b48c41054c2633c478463ac1b1f1ca23.png"
|
"https://file.lihailezzc.com/resource/b48c41054c2633c478463ac1b1f1ca23.png",
|
||||||
);
|
);
|
||||||
|
|
||||||
const blessingText = ref(
|
const blessingText = ref(
|
||||||
"岁末将至,敬颂冬绥。平安喜乐,万事胜意。祝您2026年大吉大利!一马当先,前程似锦!龙马精神,阖家安康!"
|
"岁末将至,敬颂冬绥。平安喜乐,万事胜意。祝您2026年大吉大利!一马当先,前程似锦!龙马精神,阖家安康!",
|
||||||
);
|
);
|
||||||
|
|
||||||
const textColors = ["#ffffff", "#ff3b30", "#F5A623", "#8B572A", "#000000"];
|
const textColors = ["#ffffff", "#ff3b30", "#F5A623", "#8B572A", "#000000"];
|
||||||
@@ -217,6 +234,7 @@ const greetingLib = [
|
|||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
cardId.value = "69674f307307beac4519025f";
|
cardId.value = "69674f307307beac4519025f";
|
||||||
// createCard();
|
// createCard();
|
||||||
|
getTemplateList();
|
||||||
});
|
});
|
||||||
|
|
||||||
const createCard = async () => {
|
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 () => {
|
onShareAppMessage(async () => {
|
||||||
const deviceInfo = getDeviceInfo();
|
const deviceInfo = getDeviceInfo();
|
||||||
const shareTokenRes = await createCardShareToken({
|
const shareTokenRes = await createCardShareToken({
|
||||||
@@ -262,33 +327,12 @@ const selectGreeting = (text) => {
|
|||||||
const tools = [
|
const tools = [
|
||||||
{ type: "template", text: "模板", icon: "▦" },
|
{ type: "template", text: "模板", icon: "▦" },
|
||||||
{ type: "text", text: "文字", icon: "文" },
|
{ type: "text", text: "文字", icon: "文" },
|
||||||
{ type: "image", text: "图片/背景", icon: "图" },
|
// { type: "image", text: "图片/背景", icon: "图" },
|
||||||
{ type: "avatar", text: "头像挂饰", icon: "饰" },
|
// { type: "avatar", text: "头像挂饰", icon: "饰" },
|
||||||
];
|
];
|
||||||
const activeTool = ref("template");
|
const activeTool = ref("template");
|
||||||
|
|
||||||
const templates = ref([
|
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 currentTemplate = ref(templates.value[0]);
|
const currentTemplate = ref(templates.value[0]);
|
||||||
|
|
||||||
@@ -359,7 +403,7 @@ const saveByCanvas = async (save = true) => {
|
|||||||
// 1️⃣ 画背景
|
// 1️⃣ 画背景
|
||||||
// ⭐ 先加载背景图
|
// ⭐ 先加载背景图
|
||||||
const [bgPath, avatarPath] = await Promise.all([
|
const [bgPath, avatarPath] = await Promise.all([
|
||||||
loadImage(currentTemplate.value.cover),
|
loadImage(currentTemplate?.value?.imageUrl),
|
||||||
loadImage(userAvatar.value),
|
loadImage(userAvatar.value),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -552,7 +596,7 @@ function drawUserBubble(ctx, options) {
|
|||||||
bubbleWidth,
|
bubbleWidth,
|
||||||
bubbleHeight,
|
bubbleHeight,
|
||||||
bubbleHeight / 2,
|
bubbleHeight / 2,
|
||||||
bubbleColor
|
bubbleColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2️⃣ 绘制头像
|
// 2️⃣ 绘制头像
|
||||||
@@ -566,7 +610,7 @@ function drawUserBubble(ctx, options) {
|
|||||||
avatarY + avatarSize / 2,
|
avatarY + avatarSize / 2,
|
||||||
avatarSize / 2,
|
avatarSize / 2,
|
||||||
0,
|
0,
|
||||||
Math.PI * 2
|
Math.PI * 2,
|
||||||
);
|
);
|
||||||
ctx.clip();
|
ctx.clip();
|
||||||
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
|
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
|
||||||
@@ -767,46 +811,59 @@ function drawRoundRect(ctx, x, y, w, h, r, color) {
|
|||||||
}
|
}
|
||||||
.tpl-scroll {
|
.tpl-scroll {
|
||||||
margin-top: 12rpx;
|
margin-top: 12rpx;
|
||||||
|
height: 600rpx; /* 增加高度以展示纵向列表 */
|
||||||
}
|
}
|
||||||
.tpl-wrap {
|
.tpl-grid {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 16rpx;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
}
|
}
|
||||||
.tpl-card {
|
.tpl-card {
|
||||||
width: 180rpx;
|
width: 100%; /* 自适应 grid 宽度 */
|
||||||
height: 240rpx;
|
border-radius: 12rpx;
|
||||||
border-radius: 18rpx;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
margin-right: 16rpx;
|
|
||||||
position: relative;
|
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 {
|
.tpl-card.selected {
|
||||||
outline: 4rpx solid #ff3b30;
|
outline: 4rpx solid #ff3b30;
|
||||||
}
|
}
|
||||||
.tpl-cover {
|
.tpl-cover {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 160rpx;
|
height: 368rpx;
|
||||||
}
|
}
|
||||||
.tpl-name {
|
.tpl-name {
|
||||||
font-size: 22rpx;
|
font-size: 20rpx;
|
||||||
color: #333;
|
color: #333;
|
||||||
padding: 8rpx 12rpx;
|
padding: 6rpx 8rpx;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.tpl-check {
|
.tpl-check {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10rpx;
|
right: 6rpx;
|
||||||
top: 10rpx;
|
top: 6rpx;
|
||||||
width: 36rpx;
|
width: 32rpx;
|
||||||
height: 36rpx;
|
height: 32rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #ff3b30;
|
background: #ff3b30;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 22rpx;
|
font-size: 20rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
.loading-more,
|
||||||
|
.no-more {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
padding: 10rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 文字编辑区 */
|
/* 文字编辑区 */
|
||||||
.text-edit-section {
|
.text-edit-section {
|
||||||
|
|||||||
Reference in New Issue
Block a user