add user invite

This commit is contained in:
Timi
2026-08-15 01:33:52 +08:00
parent e4d02a714f
commit c810162926
8 changed files with 764 additions and 13 deletions
+121
View File
@@ -0,0 +1,121 @@
<template>
<div class="poster-page">
<section class="poster-wrap">
<img v-if="posterImage" class="poster" :src="posterImage" alt="邀请注册海报" />
<div v-else class="empty" v-text="message" />
</section>
<div v-if="posterImage" class="actions">
<t-button class="bg-white" variant="outline" theme="primary" block @click="copyLink">复制链接</t-button>
<t-button theme="primary" block :loading="isDownloading" @click="savePoster">保存海报</t-button>
</div>
</div>
</template>
<script setup lang="ts">
import { Toast } from "tdesign-mobile-vue";
import { createInvitePosterImage, downloadInvitePoster } from "@/utils/qrCode";
defineOptions({ name: "UserInvitePoster" });
const route = useRoute();
const posterImage = ref("");
const isDownloading = ref(false);
const message = ref("邀请海报参数无效");
const code = String(route.query.code || "");
const expireAt = Number(route.query.expireAt || 0);
const storeName = String(route.query.storeName || "");
const roleName = String(route.query.roleName || "");
const registerLink = `${window.location.origin}/gao/invite/register?code=${encodeURIComponent(code)}`;
const posterOptions = computed(() => ({
code: registerLink,
storeName,
roleName,
expireAt
}));
function createPoster(): void {
if (!/^\w{10}$/.test(code) || !expireAt) {
return;
}
posterImage.value = createInvitePosterImage(posterOptions.value);
}
async function savePoster(): Promise<void> {
if (isDownloading.value) {
return;
}
isDownloading.value = true;
try {
await downloadInvitePoster(posterOptions.value, "GAO-邀请注册海报.png");
Toast.success("海报已保存");
} catch {
Toast.error("海报保存失败");
} finally {
isDownloading.value = false;
}
}
async function copyLink(): Promise<void> {
try {
await navigator.clipboard.writeText(registerLink);
Toast.success("注册链接已复制");
} catch {
Toast.error("复制失败,请检查浏览器权限");
}
}
onMounted(() => {
try {
createPoster();
} catch {
message.value = "海报生成失败,请重新生成";
}
});
</script>
<style scoped lang="less">
.poster-page {
gap: 1rem;
min-height: 100vh;
padding: calc(var(--app-nav-height) + 1rem) 1rem 6rem;
display: flex;
align-items: center;
flex-direction: column;
.poster-wrap {
width: min(100%, 28rem);
padding: .5rem;
background: #fff;
box-shadow: 0 .75rem 2rem rgba(15, 118, 110, .2);
border-radius: 1.25rem;
.poster {
width: 100%;
display: block;
border-radius: .875rem;
}
}
.empty {
padding: 5rem 1rem;
color: var(--td-text-color-secondary);
text-align: center;
}
.actions {
gap: .75rem;
left: 1rem;
right: 1rem;
bottom: calc(.75rem + env(safe-area-inset-bottom, 0));
display: grid;
position: fixed;
grid-template-columns: 1fr 1fr;
}
}
:global(html.theme-dark) .poster-page {
.poster-wrap {
background: #0f172a;
}
}
</style>