This commit is contained in:
13121765685 2025-12-21 20:33:54 +08:00
parent 0fd7cf4c72
commit 63fb522484
9 changed files with 217 additions and 9 deletions

View File

@ -0,0 +1,27 @@
# 实现邀请码升级功能
## 1. 修改 Waitlist.vue 组件
- 在现有界面中添加邀请码输入框和提交按钮
- 设计输入框样式与现有界面风格保持一致
- 添加表单验证逻辑
## 2. 引入必要的模块和方法
- 引入 user 模块的 upgrade 方法
- 引入 auth store 的 updateUserInfo 方法
- 引入 ElMessage 用于显示操作结果
## 3. 实现提交逻辑
1. 用户输入邀请码并点击提交按钮
2. 调用 `/views/user/index.js` 中的 `upgrade` 方法
3. 成功后调用 `/stores/auth.js` 中的 `updateUserInfo` 方法刷新用户信息
4. 刷新成功后返回首页
## 4. 添加错误处理
- 邀请码验证失败时显示错误信息
- 网络请求失败时显示错误提示
- 确保用户体验流畅
## 5. 界面优化
- 输入框添加合适的占位符
- 按钮状态管理(加载中、禁用等)
- 保持响应式设计,适配不同屏幕尺寸

View File

@ -4,6 +4,9 @@ import { useRouter, useRoute } from 'vue-router'
import MainLayout from '@/components/layout/MainLayout.vue'
import AppHeader from '@/components/layout/AppHeader.vue'
import AppSidebar from '@/components/layout/AppSidebar.vue'
import { useAuthStore } from '@/stores/auth';
const authStore = useAuthStore();
authStore.updateUserInfo()
const route = useRoute()
//
const isLoginPage = computed(() => route.path === '/login')

View File

@ -173,6 +173,14 @@ router.beforeEach(async (to, from, next) => {
// window.localStorage.setItem('token','123')
// return next()
// }
if(to.path=='/login'||to.path=='/login/phone'||to.path=='/register'||to.path=='/forgot-password'){
const token = localStorage.getItem('token')
// 如果有 token跳转到首页
if (token) {
next('/czhome')
return
}
}
const newto = freeRoutes.find(route => route.path == to.path)
if (newto?.meta?.requiresAuth) {
const token = localStorage.getItem('token')
@ -184,8 +192,10 @@ router.beforeEach(async (to, from, next) => {
}
// 检查是否需要添加动态路由
const authStore = useAuthStore();
const user_role = authStore.user?.user_role;
if(user_role != '0' && router.getRoutes().length == routes.length) {
// const info = await authStore.updateUserInfo();
// console.log(info,'infoinfo');
const user_role = authStore.user?.userRole;
if((user_role == 1||user_role == 2) && router.getRoutes().length == routes.length) {
// 添加动态路由
addDynamicRoutes();
if(isDynamicRoute(to.path)) {
@ -195,7 +205,7 @@ router.beforeEach(async (to, from, next) => {
}, 20);
return
}
}else if(user_role == '0'){
}else if(user_role == 0){
// 恢复默认路由
removeDynamicRoutes()
router.addRoute(

View File

@ -57,9 +57,11 @@ export const useAuthStore = defineStore('auth', () => {
//登录成功方法
const loginSuccess = (data,callback=null) => {
token.value = data.accessToken
user.value = data
// user.value = data
localStorage.setItem('token', token.value);
updateUserInfo().then(res => {
callback&&callback(data);
})
}
// 登出方法
const logout = async (callback) => {
@ -79,8 +81,28 @@ export const useAuthStore = defineStore('auth', () => {
window?.Redirectlogin()
}
}
//更新用户信息方法
const updateUserInfo = async (data) => {
if(!token.value){
return
}
return new Promise(async (resolve, reject) => {
try {
const res = await requestUtils.common(clientApi.default.USER_INFO, data)
if(res.code === 0){
let data = res.data;
// 更新成功,保存用户信息
user.value = data
resolve(res)
}
} catch (error) {
console.error('更新用户信息失败:', error)
reject(error)
}
})
}
return {
updateUserInfo,
user,
token,
login,

View File

@ -138,7 +138,6 @@ const goToRegister = () => {
const goToPhoneLogin = () => {
router.push('/login/phone')
}
//
onMounted(() => {
})

View File

@ -480,7 +480,7 @@ const handleCreatePromptCard = (index,params) => {
status:'loading',
imgyt:imgyt||'',
type:'image',
inspirationImage:cardData.inspirationImage||'',
// inspirationImage:cardData.inspirationImage||'',
},index);
console.log(newCard,'newCardnewCard');
//

View File

@ -9,6 +9,24 @@
</div>
<div class="waitlist-title">{{ t('waitlist.title') }}</div>
<div class="waitlist-description">{{ t('waitlist.description') }}</div>
<!-- 邀请码输入区域 -->
<div class="invite-code-section">
<el-input
v-model="inviteCode"
placeholder="请输入邀请码"
class="invite-code-input"
clearable
/>
<button
class="primary-button"
@click="submitInviteCode"
:loading="submitting"
>
提交邀请码
</button>
</div>
<div class="action-buttons">
<button class="primary-button" @click="goHome">
{{ t('waitlist.goHome') }}
@ -26,20 +44,60 @@
<script>
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { UserController } from './user/index.js'
import { useAuthStore } from '@/stores/auth.js'
export default {
name: 'Waitlist',
setup() {
const { t } = useI18n()
const router = useRouter()
const authStore = useAuthStore()
const userController = new UserController()
const inviteCode = ref('')
const submitting = ref(false)
const goHome = () => {
router.push('/')
}
//
const submitInviteCode = async () => {
if (!inviteCode.value.trim()) {
ElMessage.warning('请输入邀请码')
return
}
submitting.value = true
try {
// upgrade
const upgradeRes = await userController.upgrade({ inviteCode: inviteCode.value })
if (upgradeRes.code === 0) {
//
await authStore.updateUserInfo()
ElMessage.success('升级成功')
//
goHome()
} else {
ElMessage.error(upgradeRes.message || '升级失败')
}
} catch (error) {
console.error('升级失败:', error)
ElMessage.error(error.message || '升级失败,请稍后重试')
} finally {
submitting.value = false
}
}
return {
t,
goHome
goHome,
inviteCode,
submitting,
submitInviteCode
}
}
}
@ -150,6 +208,48 @@ export default {
left: 10%;
}
/* 邀请码输入区域样式 */
.invite-code-section {
margin-bottom: 30px;
display: flex;
flex-direction: column;
gap: 16px;
align-items: center;
}
.invite-code-input {
width: 100%;
max-width: 400px;
margin: 0 auto;
}
.invite-code-input :deep(.el-input__wrapper) {
border-radius: 8px;
border: 1px solid rgba(107, 70, 193, 0.3);
transition: all 0.2s ease;
}
.invite-code-input :deep(.el-input__wrapper:hover) {
border-color: rgba(107, 70, 193, 0.6);
box-shadow: 0 0 0 2px rgba(107, 70, 193, 0.1);
}
.invite-code-input :deep(.el-input__wrapper.is-focus) {
border-color: #6B46C1;
box-shadow: 0 0 0 2px rgba(107, 70, 193, 0.2);
}
/* 按钮加载状态样式 */
.primary-button:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none !important;
}
.primary-button:disabled:hover {
box-shadow: none;
}
@media (max-width: 768px) {
.waitlist-title {
font-size: 2rem;
@ -167,6 +267,10 @@ export default {
.primary-button {
width: 200px;
}
.invite-code-input {
max-width: 300px;
}
}
@media (max-width: 480px) {
@ -193,5 +297,9 @@ export default {
width: 100px;
height: 100px;
}
.invite-code-input {
max-width: 250px;
}
}
</style>

View File

@ -53,4 +53,41 @@ export class UserController {
}
*/
}
// 返回用户信息
async getUserInfo() {
return await requestUtils.common(clientApi.default.USER_INFO);
/*
返回示例
{
"code": 0,
"success": true,
"data": {
"id": 9007199254740991,
"nickname": "string",
"email": "string",
"avatarUrl": "string",
"phone": "string",
"lastActive": "2025-12-19T09:45:20.801Z",
"status": "string",
"userRole": 1073741824,
"inviteCode": "string",
"otherInfo": {
"additionalProp1": {},
"additionalProp2": {},
"additionalProp3": {}
},
"createdAt": "2025-12-19T09:45:20.801Z",
"updatedAt": "2025-12-19T09:45:20.801Z"
},
"message": "操作成功"
}
*/
}
// 候补会员使用邀请码升级为正式会员
async upgrade(data) {
let parmas = {
inviteCode:data.inviteCode,
}
return await requestUtils.common(clientApi.default.UPGRADE,parmas);
}
}

View File

@ -4,5 +4,7 @@ const login = {
INVITE_CODES:{url:'/api-base/user/invite/codes',method:'GET'},// 返回当前用户的邀请码列表及使用状态
INVITE_RECORDS:{url:'/api-base/user/invite/records',method:'GET'},// 返回邀请的用户列表,包含奖励明细
combined:{url:'/api-base/prompt/active',method:'GET'},// 返回动态提示词
UPGRADE:{url:'/api-base/user/upgrade',method:'POST'},// 候补会员使用邀请码升级为正式会员
USER_INFO:{url:'/api-base/user/info',method:'GET',isLoading:true},// 返回用户信息
}
export default login;