feat(shop): 添加邀请注册功能并优化注册流程
- 新增邀请注册弹窗组件,用于生成邀请链接和二维码 - 在注册页面添加邀请信息显示,引导用户通过邀请链接注册 - 实现注册成功后自动建立推荐关系的功能 - 优化注册表单,支持通过邀请链接直接进入注册页面
This commit is contained in:
@@ -35,6 +35,14 @@
|
||||
>
|
||||
</div>
|
||||
<template v-if="loginType === 'account'">
|
||||
<!-- 邀请信息显示 -->
|
||||
<div v-if="inviterId" style="margin-bottom: 16px; padding: 12px; background: #f6ffed; border: 1px solid #b7eb8f; border-radius: 4px;">
|
||||
<div style="color: #52c41a; font-size: 14px;">
|
||||
<check-circle-outlined style="margin-right: 4px;" />
|
||||
您正在通过邀请链接注册,注册成功后将自动建立推荐关系
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-form-item name="phone">
|
||||
<a-input
|
||||
allow-clear
|
||||
@@ -227,12 +235,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, reactive, unref, watch} from 'vue';
|
||||
import {ref, reactive, unref, watch, onMounted} from 'vue';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {useRouter} from 'vue-router';
|
||||
import {getTenantId} from '@/utils/domain';
|
||||
import {Form, message} from 'ant-design-vue';
|
||||
import { AimOutlined } from '@ant-design/icons-vue';
|
||||
import { AimOutlined, CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import {goHomeRoute, cleanPageTabs} from '@/utils/page-tab-util';
|
||||
import {loginBySms, getCaptcha} from '@/api/passport/login';
|
||||
@@ -248,6 +256,7 @@ import {push} from "@/utils/common";
|
||||
import {useThemeStore} from "@/store/modules/theme";
|
||||
import {CmsWebsite} from "@/api/cms/cmsWebsite/model";
|
||||
import {createCmsWebSite} from "@/api/layout";
|
||||
import { bindUserReferee } from '@/api/user/referee';
|
||||
|
||||
const useForm = Form.useForm;
|
||||
const {currentRoute} = useRouter();
|
||||
@@ -382,6 +391,8 @@ const countdownTime = ref(0);
|
||||
let countdownTimer: number | null = null;
|
||||
// 是否显示地图选择弹窗
|
||||
const showMap = ref(false);
|
||||
// 邀请人ID
|
||||
const inviterId = ref<number>();
|
||||
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
@@ -548,8 +559,28 @@ const submit = () => {
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
|
||||
// 普通注册流程
|
||||
createCmsWebSite(form)
|
||||
.then((msg) => {
|
||||
.then(async (msg) => {
|
||||
// 如果有邀请人,注册成功后建立推荐关系
|
||||
if (inviterId.value) {
|
||||
try {
|
||||
const userId = localStorage.getItem('UserId');
|
||||
if (userId) {
|
||||
await bindUserReferee({
|
||||
dealerId: inviterId.value,
|
||||
userId: Number(userId),
|
||||
level: 1
|
||||
});
|
||||
message.success('注册成功,已建立推荐关系');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('建立推荐关系失败:', e);
|
||||
// 不影响注册流程,只是推荐关系建立失败
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
// 登录成功
|
||||
message.success(msg);
|
||||
@@ -593,6 +624,15 @@ const changeCaptcha = () => {
|
||||
// 首次加载
|
||||
changeCaptcha();
|
||||
|
||||
// 检查URL参数中的邀请人ID
|
||||
onMounted(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const inviterParam = urlParams.get('inviter');
|
||||
if (inviterParam) {
|
||||
inviterId.value = Number(inviterParam);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
currentRoute,
|
||||
() => {
|
||||
|
||||
146
src/views/shop/shopAdmin/components/invitation-modal.vue
Normal file
146
src/views/shop/shopAdmin/components/invitation-modal.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:width="500"
|
||||
:visible="visible"
|
||||
:footer="null"
|
||||
title="邀请注册"
|
||||
@update:visible="updateVisible"
|
||||
>
|
||||
<div style="text-align: center">
|
||||
<div style="margin-bottom: 20px">
|
||||
<a-typography-title :level="4">邀请新用户注册</a-typography-title>
|
||||
<a-typography-text type="secondary">
|
||||
分享以下链接或二维码,邀请用户注册并自动建立推荐关系
|
||||
</a-typography-text>
|
||||
</div>
|
||||
|
||||
<!-- 邀请链接 -->
|
||||
<div style="margin-bottom: 20px">
|
||||
<a-input
|
||||
:value="invitationLink"
|
||||
readonly
|
||||
style="margin-bottom: 8px"
|
||||
>
|
||||
<template #addonAfter>
|
||||
<a-button type="link" size="small" @click="copyLink">
|
||||
复制链接
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
|
||||
<!-- 二维码 -->
|
||||
<div style="margin-bottom: 20px">
|
||||
<div style="display: inline-block; padding: 10px; background: white; border-radius: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1)">
|
||||
<QrCode
|
||||
:value="invitationLink"
|
||||
:size="200"
|
||||
:margin="10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 使用说明 -->
|
||||
<div style="text-align: left; background: #f5f5f5; padding: 12px; border-radius: 4px; margin-bottom: 20px">
|
||||
<div style="font-weight: 500; margin-bottom: 8px">使用说明:</div>
|
||||
<div style="font-size: 12px; color: #666; line-height: 1.5">
|
||||
1. 复制邀请链接发送给用户,或让用户扫描二维码<br>
|
||||
2. 用户点击链接进入注册页面<br>
|
||||
3. 用户完成注册后,系统自动建立推荐关系<br>
|
||||
4. 您可以在"推荐关系管理"中查看邀请结果
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div>
|
||||
<a-space>
|
||||
<a-button @click="downloadQRCode">下载二维码</a-button>
|
||||
<a-button type="primary" @click="copyLink">复制链接</a-button>
|
||||
<a-button @click="goToRefereeManage">查看推荐关系</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { useRouter } from 'vue-router';
|
||||
import QrCode from '@/components/QrCode/index.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
inviterId?: number; // 邀请人ID(当前登录用户ID)
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 生成邀请链接
|
||||
const invitationLink = computed(() => {
|
||||
const baseUrl = window.location.origin;
|
||||
const inviterId = props.inviterId || localStorage.getItem('UserId');
|
||||
return `${baseUrl}/register?inviter=${inviterId}`;
|
||||
});
|
||||
|
||||
// 复制链接
|
||||
const copyLink = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(invitationLink.value);
|
||||
message.success('邀请链接已复制到剪贴板');
|
||||
} catch (e) {
|
||||
// 降级方案
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = invitationLink.value;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textArea);
|
||||
message.success('邀请链接已复制到剪贴板');
|
||||
}
|
||||
};
|
||||
|
||||
// 下载二维码
|
||||
const downloadQRCode = () => {
|
||||
try {
|
||||
// 查找二维码canvas元素
|
||||
const canvas = document.querySelector('.ant-modal-body canvas') as HTMLCanvasElement;
|
||||
if (canvas) {
|
||||
const link = document.createElement('a');
|
||||
link.download = `邀请注册二维码.png`;
|
||||
link.href = canvas.toDataURL();
|
||||
link.click();
|
||||
message.success('二维码已下载');
|
||||
} else {
|
||||
message.error('下载失败,请稍后重试');
|
||||
}
|
||||
} catch (e) {
|
||||
message.error('下载失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 跳转到推荐关系管理
|
||||
const goToRefereeManage = () => {
|
||||
router.push('/shop/user-referee');
|
||||
updateVisible(false);
|
||||
};
|
||||
|
||||
// 更新visible
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-typography-title) {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
:deep(.ant-input-group-addon) {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -16,7 +16,7 @@
|
||||
>
|
||||
<template #toolbar>
|
||||
<a-space>
|
||||
<a-button type="primary" class="ele-btn-icon" @click="openEdit()">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="openInvitation">
|
||||
<template #icon>
|
||||
<plus-outlined/>
|
||||
</template>
|
||||
@@ -105,6 +105,8 @@
|
||||
<user-import v-model:visible="showImport" @done="reload"/>
|
||||
<!-- 用户详情 -->
|
||||
<user-info v-model:visible="showInfo" :data="current" @done="reload"/>
|
||||
<!-- 邀请注册弹窗 -->
|
||||
<invitation-modal v-model:visible="showInvitation" :inviter-id="currentUserId"/>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
@@ -128,6 +130,7 @@ import {messageLoading, formatNumber} from 'ele-admin-pro/es';
|
||||
import UserEdit from './components/user-edit.vue';
|
||||
import UserImport from './components/user-import.vue';
|
||||
import UserInfo from './components/user-info.vue';
|
||||
import InvitationModal from './components/invitation-modal.vue';
|
||||
import {toDateString} from 'ele-admin-pro';
|
||||
import {
|
||||
pageUsers,
|
||||
@@ -165,8 +168,12 @@ const showEdit = ref(false);
|
||||
const showInfo = ref(false);
|
||||
// 是否显示用户导入弹窗
|
||||
const showImport = ref(false);
|
||||
// 是否显示邀请注册弹窗
|
||||
const showInvitation = ref(false);
|
||||
const userType = ref<number>();
|
||||
const searchText = ref('');
|
||||
// 当前用户ID
|
||||
const currentUserId = ref<number>();
|
||||
|
||||
// 加载角色
|
||||
const roles = ref<any[]>([]);
|
||||
@@ -410,6 +417,18 @@ const query = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 打开邀请注册弹窗 */
|
||||
const openInvitation = () => {
|
||||
// 获取当前用户ID
|
||||
const userId = localStorage.getItem('UserId');
|
||||
if (userId) {
|
||||
currentUserId.value = Number(userId);
|
||||
showInvitation.value = true;
|
||||
} else {
|
||||
message.error('获取用户信息失败');
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => router.currentRoute.value.query,
|
||||
() => {
|
||||
|
||||
Reference in New Issue
Block a user