aslan-h5/src/views/Invitation/inviteBDLeader.vue

452 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="fullPage">
<!-- 顶部导航 -->
<GeneralHeader
:showLanguageList="true"
title="Invite User To Become BD Leader"
color="black"
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 999"
/>
<!-- 内容 -->
<div style="padding: 16px; position: relative">
<!-- 搜索框 -->
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px">
<div
style="
flex: 1;
border-radius: 32px;
border: 1px solid #fff;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
padding: 0 8px;
display: flex;
align-items: center;
height: max-content;
"
>
<img
v-smart-img
src="../../assets/icon/search.png"
alt=""
style="display: block; width: 2em; aspect-ratio: 1/1; opacity: 0.6"
/>
<input
v-model="searchQuery"
type="text"
name=""
id=""
:placeholder="t('enter_user_id')"
@keyup.enter="handleEnterPress"
@input="handleInputChange"
style="
width: 100%;
color: rgba(0, 0, 0, 0.4);
font-weight: 600;
outline: none;
border: none;
background-color: transparent;
padding: 8px;
"
/>
<button
v-if="searchQuery"
@click="clearSearch"
style="background: none; border: none; color: #9ca3af; cursor: pointer; padding: 0 8px"
>
×
</button>
</div>
<button
style="font-weight: 600"
class="invite-btn"
@click="inviteAgency"
:disabled="disInvite"
>
{{ t('invite') }}
</button>
</div>
<!-- 搜索结果 -->
<div
v-if="showUserList.length > 0"
style="margin: 16px 0; display: flex; flex-direction: column; gap: 12px"
>
<div
v-for="user in showUserList"
:key="user.id"
style="
position: relative;
background-color: white;
padding: 4px 10px;
border-radius: 12px;
box-sizing: border-box;
border: 2px solid transparent;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
transition: all 0.2s;
"
@click="toggleUserSelection(user)"
>
<!-- 绝对定位 选中框 -->
<div
style="position: absolute; inset: -2px; border-radius: 12px; z-index: 2"
:class="{ selected: selectedUser?.id === user.userProfile.id }"
></div>
<!-- 绝对定位 状态显示 -->
<div
style="position: absolute; top: -2px; right: -2px"
v-if="user.status == 0 || user.status == 1"
>
<div
v-if="user.status == 0"
style="
background-image: linear-gradient(112deg, #759cff 5.66%, #3d73ff 42.49%);
padding: 4px 8px;
border-radius: 0 12px;
color: white;
font-weight: 600;
"
>
{{ t('pending') }}
</div>
<div
v-if="user.status == 1"
style="
background-image: linear-gradient(112deg, #75ff98 5.66%, #3dff54 42.49%);
padding: 4px 8px;
border-radius: 0 12px;
color: white;
font-weight: 600;
"
>
{{ t('success') }}
</div>
</div>
<!-- 标题 -->
<div style="font-weight: 600">{{ t('information') }}:</div>
<!-- 用户信息 -->
<div style="display: flex; align-items: center; justify-content: space-between">
<!-- 基本信息 -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
align-items: center;
gap: 4px;
"
>
<div
style="
width: 3em;
display: flex;
align-items: center;
justify-content: center;
"
>
<img
:src="user.userProfile.userAvatar || ''"
style="width: 100%; aspect-ratio: 1/1; object-fit: cover; border-radius: 50%"
alt=""
@error="defaultAvatarUrl"
/>
</div>
<!-- 基本信息 -->
<div style="flex: 1; min-width: 0; align-self: stretch">
<div
style="
margin-bottom: 4px;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
>
{{ user.userProfile.userNickname }}
</div>
<div style="font-size: 0.9em; color: rgba(0, 0, 0, 0.4); font-weight: 500">
ID: {{ user.userProfile.account }}
</div>
</div>
</div>
<!-- 取消邀请按钮 -->
<div
style="
width: auto;
min-width: 0;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
"
>
<div
v-if="user.status == 0"
style="
padding: 4px 8px;
border-radius: 32px;
background-color: red;
color: white;
font-size: 16px;
font-weight: 600;
z-index: 5;
"
@click="cancelInviteAgency(user.id)"
>
{{ t('cancel') }}
</div>
</div>
</div>
</div>
</div>
<!-- 无数据提示 -->
<div v-else class="no-data">
{{ t('no_users_found') }}
</div>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { setDocumentDirection } from '@/locales/i18n'
import { showError, showSuccess } from '@/utils/toast.js'
import { useDebounce } from '@/utils/useDebounce'
import { searchUser } from '@/api/userInfo'
import {
myInvitedList_BDLeader, // 获取已邀请列表
inviteToBe_BDLeader, // 邀请成为BDLeader
cancelInvite_BDLeader, // 处理邀请(取消邀请)
} from '@/api/admin.js'
import GeneralHeader from '@/components/GeneralHeader.vue'
const { t, locale } = useI18n()
// 监听语言变化并设置文档方向
locale.value && setDocumentDirection(locale.value)
const searchQuery = ref('') //关键词
const invitedUserList = ref([]) //已邀请列表
const searchResults = ref([]) //搜索结果
const disInvite = ref(true) //不可邀请
const selectedUser = ref({}) //选中的用户
//展示的用户列表
const showUserList = computed(() => {
if (searchQuery.value) {
return searchResults.value
} else {
return invitedUserList.value
}
})
const defaultAvatarUrl = (e) => {
console.log('头像资源出错')
e.target.onerror = null //防止循环
e.target.src = new URL('/src/assets/icon/defaultAvatar.png', import.meta.url).href
}
// 清空关键词
const clearSearch = () => {
searchQuery.value = ''
searchResults.value = []
selectedUser.value = {}
disInvite.value = true
getInvitedList() //重新获取已邀请列表
}
// 输入框变化
const handleInputChange = () => {
// 输入框为空时,清空搜索结果
if (!searchQuery.value.trim()) {
searchResults.value = []
selectedUser.value = {}
disInvite.value = true
getInvitedList() //重新获取已邀请列表
}
}
// 创建防抖版本的搜索函数
const debouncedSearch = useDebounce(
async () => {
console.log('搜索用户id', searchQuery.value)
searchResults.value = []
selectedUser.value = {}
disInvite.value = true
if (searchQuery.value.trim()) {
const resSearchUser = await searchUser(searchQuery.value)
if (resSearchUser.status && resSearchUser.body) {
let targetUserInfo = { userProfile: resSearchUser.body }
searchResults.value.push(targetUserInfo)
}
}
},
500,
true
)
// 回车防抖搜索
const handleEnterPress = async () => {
debouncedSearch()
}
// 选择用户
const toggleUserSelection = (user) => {
console.log('选择用户:', user)
if (selectedUser.value?.id == user.userProfile.id) {
selectedUser.value = {}
disInvite.value = true
} else {
selectedUser.value = user.userProfile
if (user.status == 0 || user.status == 1) {
disInvite.value = true
} else {
disInvite.value = false
}
}
}
// 邀请代理
const inviteAgency = async () => {
console.log('点击邀请代理')
try {
let data = { inviteUserId: selectedUser.value.id }
const resInvite = await inviteToBe_BDLeader(data)
if (resInvite.status) {
showSuccess(t('invitation_submitted'))
}
} catch (error) {
showError(error.response?.errorMsg || t('something_went_wrong'))
}
clearSearch()
}
// 获取已邀请列表
const getInvitedList = async () => {
const resInvitedList = await myInvitedList_BDLeader()
console.log('已邀请列表:', resInvitedList)
if (resInvitedList.status && resInvitedList.body) {
invitedUserList.value = resInvitedList.body
}
}
// 取消邀请
const cancelInviteAgency = async (userId) => {
console.log('取消邀请用户id:', userId)
try {
const resCancel = await cancelInvite_BDLeader(userId)
if (resCancel.status) {
showSuccess(t('application_cancelled'))
getInvitedList()
}
} catch (error) {
let errorMsg =
error.response?.errorMsg?.replace(/^.*\]\s*/, '') || t('cancellation_failed_try_again')
showError(errorMsg)
}
}
onMounted(() => {
getInvitedList()
})
</script>
<style lang="scss" scoped>
* {
color: rgba(0, 0, 0, 0.8);
font-family: 'SF Pro Text';
}
input::placeholder {
font-weight: bold;
color: rgba(0, 0, 0, 0.4);
}
.fullPage {
width: 100vw;
min-height: 100vh;
background-color: #ffffff;
background-image: url(../../assets/images/secondBg.png);
background-size: 100% auto;
background-repeat: no-repeat;
position: relative;
}
.invite-btn {
padding: 10px 12px;
border-radius: 32px;
background: linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%);
color: white;
border: none;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.invite-btn:not(:disabled):active {
background: linear-gradient(135deg, #b080ff 2.82%, #6302ff 99.15%);
}
.invite-btn:disabled {
background: #d1d5db;
cursor: not-allowed;
}
.selected {
border: 2px solid #8b5cf6 !important;
}
.no-data {
text-align: center;
padding: 40px 20px;
color: #666;
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;
}
}
@media screen and (min-width: 360px) {
* {
font-size: 12px;
}
}
@media screen and (min-width: 768px) {
* {
font-size: 24px;
}
}
@media screen and (min-width: 1024px) {
* {
font-size: 32px;
}
}
/* RTL支持 */
[dir='rtl'] .avatar {
margin-right: 0;
margin-left: 12px;
}
[dir='rtl'] .user-info {
text-align: right;
}
</style>