509 lines
14 KiB
Vue
509 lines
14 KiB
Vue
<template>
|
|
<div class="user-invites">
|
|
<!-- 添加返回按钮 -->
|
|
<div class="page-header" style="margin-bottom: 20px;">
|
|
<el-button
|
|
type="primary"
|
|
:icon="ArrowLeft"
|
|
@click="handleBack"
|
|
>
|
|
返回
|
|
</el-button>
|
|
</div>
|
|
|
|
<!-- 标签页 -->
|
|
<el-tabs v-model="activeTab" type="card" @tab-change="handleTabChange">
|
|
<!-- 邀请码列表 -->
|
|
<el-tab-pane label="邀请码列表" name="inviteCode">
|
|
<!-- 邀请码列表内容 -->
|
|
<div class="invite-code-section">
|
|
<!-- 筛选和生成按钮区域 -->
|
|
<div class="filter-section">
|
|
<el-form :inline="true" :model="filterForm" class="filter-form">
|
|
<el-form-item label="用户ID" v-if="false">
|
|
<el-input v-model="filterForm.userId" placeholder="请输入用户ID" style="width: 150px;"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="邀请码">
|
|
<el-input v-model="filterForm.inviteCode" placeholder="请输入邀请码" style="width: 200px;"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="使用状态">
|
|
<el-select v-model="filterForm.isUsed" placeholder="请选择使用状态" style="width: 150px;">
|
|
<el-option label="全部" value=""></el-option>
|
|
<el-option label="已使用" :value="1"></el-option>
|
|
<el-option label="未使用" :value="0"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
|
<el-button @click="handleReset">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-button type="success" @click="showGenerateDialog = true">生成邀请码</el-button>
|
|
</div>
|
|
|
|
<!-- 邀请码列表 -->
|
|
<div class="invite-table">
|
|
<!-- stripe -->
|
|
<el-table
|
|
:data="inviteCodeList"
|
|
style="width: 100%"
|
|
v-loading="loadingCode"
|
|
>
|
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
|
<el-table-column prop="userNickname" label="用户名" min-width="120" align="center" />
|
|
<el-table-column prop="userEmail" label="邮箱" min-width="180" align="center" />
|
|
<el-table-column prop="inviteCode" label="邀请码" min-width="200" align="center" />
|
|
<el-table-column prop="isUsed" label="使用状态" min-width="100" align="center">
|
|
<template #default="{ row }">
|
|
<el-tag :type="row.isUsed ? 'success' : 'warning'">
|
|
{{ row.isUsed ? '已使用' : '未使用' }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="invitedUserNickname" label="邀请用户" min-width="120" align="center" />
|
|
<el-table-column prop="usedAt" label="使用时间" min-width="180" align="center">
|
|
<template #default="{ row }">
|
|
{{ formatDateTime(row.usedAt) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="createdAt" label="创建时间" min-width="180" align="center">
|
|
<template #default="{ row }">
|
|
{{ formatDateTime(row.createdAt) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="120" align="center">
|
|
<template #default="{ row }">
|
|
<el-button
|
|
type="danger"
|
|
size="small"
|
|
@click="handleDeleteCode(row)"
|
|
:disabled="Boolean(row.isUsed)"
|
|
>
|
|
删除
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
|
|
<!-- 分页 -->
|
|
<div class="pagination">
|
|
<el-pagination
|
|
v-model:current-page="currentPageCode"
|
|
v-model:page-size="pageSizeCode"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
:total="totalCodes"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
@size-change="handleSizeChangeCode"
|
|
@current-change="handleCurrentChangeCode"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 生成邀请码对话框 -->
|
|
<el-dialog
|
|
v-model="showGenerateDialog"
|
|
title="生成邀请码"
|
|
width="400px"
|
|
>
|
|
<el-form :model="generateForm" label-position="top">
|
|
<el-form-item label="生成数量" required>
|
|
<el-input-number
|
|
v-model="generateForm.count"
|
|
:min="1"
|
|
:max="100"
|
|
:step="1"
|
|
placeholder="请输入生成数量"
|
|
></el-input-number>
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="showGenerateDialog = false">取消</el-button>
|
|
<el-button type="primary" @click="handleGenerateCode">确认</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<!-- 删除确认对话框 -->
|
|
<el-dialog
|
|
v-model="showDeleteDialog"
|
|
title="确认删除"
|
|
width="400px"
|
|
>
|
|
<div>确定要删除邀请码 {{ deleteCodeInfo.inviteCode }} 吗?</div>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="showDeleteDialog = false">取消</el-button>
|
|
<el-button type="danger" @click="confirmDeleteCode">确认删除</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
</el-tab-pane>
|
|
|
|
<!-- 邀请用户列表 -->
|
|
<el-tab-pane label="邀请用户列表" name="invitedUsers">
|
|
<!-- 列表区域 -->
|
|
<div class="invite-table">
|
|
<!-- stripe -->
|
|
<el-table
|
|
:data="inviteList"
|
|
style="width: 100%"
|
|
v-loading="loading"
|
|
>
|
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
|
<el-table-column prop="nickname" label="用户名" min-width="120" align="center" />
|
|
<el-table-column prop="email" label="邮箱" min-width="180" align="center" />
|
|
<el-table-column prop="userRole" label="用户角色" min-width="120" align="center">
|
|
<template #default="{ row }">
|
|
{{ getUserRoleLabel(row.userRole) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="createdAt" label="注册日期" min-width="160" align="center">
|
|
<template #default="{ row }">
|
|
{{ formatDateTime(row.createdAt) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="status" label="状态" min-width="100" align="center">
|
|
<template #default="{ row }">
|
|
<el-tag :type="getStatusTagType(row.status)">
|
|
{{ getStatusLabel(row.status) }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
|
|
<!-- 分页 -->
|
|
<div class="pagination">
|
|
<el-pagination
|
|
v-model:current-page="currentPage"
|
|
v-model:page-size="pageSize"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
:total="totalInvites"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
@size-change="handleSizeChange"
|
|
@current-change="handleCurrentChange"
|
|
/>
|
|
</div>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { ElMessage } from 'element-plus'
|
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
|
import { AdminOrders } from './index.js'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const { t } = useI18n()
|
|
const adminOrders = new AdminOrders()
|
|
|
|
// 响应式数据 - 标签页
|
|
const activeTab = ref('inviteCode')
|
|
|
|
// 响应式数据 - 邀请用户列表
|
|
const loading = ref(false)
|
|
const currentPage = ref(1)
|
|
const pageSize = ref(20)
|
|
const totalInvites = ref(0)
|
|
const inviteList = ref([])
|
|
const userId = ref(route.params.id)
|
|
|
|
// 响应式数据 - 邀请码列表
|
|
const loadingCode = ref(false)
|
|
const currentPageCode = ref(1)
|
|
const pageSizeCode = ref(20)
|
|
const totalCodes = ref(0)
|
|
const inviteCodeList = ref([])
|
|
|
|
// 筛选表单数据
|
|
const filterForm = ref({
|
|
userId: '',
|
|
inviteCode: '',
|
|
isUsed: ''
|
|
})
|
|
|
|
// 生成邀请码表单数据
|
|
const generateForm = ref({
|
|
count: 1
|
|
})
|
|
|
|
// 对话框显示状态
|
|
const showGenerateDialog = ref(false)
|
|
const showDeleteDialog = ref(false)
|
|
const deleteCodeInfo = ref({})
|
|
|
|
|
|
// 获取状态标签类型
|
|
const getStatusTagType = (status) => {
|
|
const typeMap = {
|
|
active: 'success',
|
|
disable: 'info',
|
|
banned: 'danger'
|
|
}
|
|
return typeMap[status] || 'info'
|
|
}
|
|
|
|
// 获取状态标签文本
|
|
const getStatusLabel = (status) => {
|
|
const labelMap = {
|
|
active: '活跃',
|
|
disable: '禁用',
|
|
banned: '已封禁'
|
|
}
|
|
return labelMap[status] || status
|
|
}
|
|
|
|
// 获取用户角色标签
|
|
const getUserRoleLabel = (userRole) => {
|
|
const roleMap = {
|
|
0: '候补中',
|
|
1: '免费会员',
|
|
2: '达人会员'
|
|
}
|
|
return roleMap[userRole] || '未知角色'
|
|
}
|
|
|
|
// 格式化日期时间
|
|
const formatDateTime = (dateTimeString) => {
|
|
if (!dateTimeString) return '-'
|
|
const date = new Date(dateTimeString)
|
|
return date.toLocaleString(
|
|
t('admin.review.dateFormat') === 'en-US' ? 'en-US' : 'zh-CN',
|
|
{
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
}
|
|
)
|
|
}
|
|
|
|
// 获取邀请列表
|
|
const getInviteList = async () => {
|
|
loading.value = true
|
|
try {
|
|
const result = await adminOrders.getUsersInvites({
|
|
id: userId.value,
|
|
pageSize: pageSize.value,
|
|
pageNum: currentPage.value
|
|
})
|
|
inviteList.value = result.rows || []
|
|
totalInvites.value = result.total || 0
|
|
} catch (error) {
|
|
console.error('获取邀请列表失败:', error)
|
|
ElMessage.error('获取邀请列表失败')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// 获取邀请码列表
|
|
const getInviteCodeList = async () => {
|
|
loadingCode.value = true
|
|
try {
|
|
const result = await adminOrders.getInviteCodeList({
|
|
id: filterForm.value.userId || userId.value,
|
|
inviteCode: filterForm.value.inviteCode,
|
|
isUsed: filterForm.value.isUsed,
|
|
pageSize: pageSizeCode.value,
|
|
pageNum: currentPageCode.value
|
|
})
|
|
const data = result.data;
|
|
inviteCodeList.value = data.rows || []
|
|
totalCodes.value = data.total || 0
|
|
} catch (error) {
|
|
console.error('获取邀请码列表失败:', error)
|
|
ElMessage.error('获取邀请码列表失败')
|
|
} finally {
|
|
loadingCode.value = false
|
|
}
|
|
}
|
|
|
|
// 生成邀请码
|
|
const handleGenerateCode = async () => {
|
|
try {
|
|
const result = await adminOrders.generateInviteCode({
|
|
id: userId.value,
|
|
count: generateForm.value.count
|
|
})
|
|
ElMessage.success('邀请码生成成功')
|
|
showGenerateDialog.value = false
|
|
// 重置表单
|
|
generateForm.value.count = 1
|
|
// 刷新邀请码列表
|
|
getInviteCodeList()
|
|
} catch (error) {
|
|
console.error('生成邀请码失败:', error)
|
|
ElMessage.error('生成邀请码失败')
|
|
}
|
|
}
|
|
|
|
// 删除邀请码 - 显示确认对话框
|
|
const handleDeleteCode = (row) => {
|
|
deleteCodeInfo.value = row
|
|
showDeleteDialog.value = true
|
|
}
|
|
|
|
// 删除邀请码 - 确认删除
|
|
const confirmDeleteCode = async () => {
|
|
try {
|
|
const result = await adminOrders.deleteInviteCode({
|
|
id: deleteCodeInfo.value.id
|
|
})
|
|
ElMessage.success('邀请码删除成功')
|
|
showDeleteDialog.value = false
|
|
// 刷新邀请码列表
|
|
getInviteCodeList()
|
|
} catch (error) {
|
|
console.error('删除邀请码失败:', error)
|
|
ElMessage.error('删除邀请码失败')
|
|
}
|
|
}
|
|
|
|
// 分页处理 - 邀请用户列表
|
|
const handleSizeChange = (size) => {
|
|
pageSize.value = size
|
|
currentPage.value = 1
|
|
getInviteList()
|
|
}
|
|
|
|
const handleCurrentChange = (page) => {
|
|
currentPage.value = page
|
|
getInviteList()
|
|
}
|
|
|
|
// 分页处理 - 邀请码列表
|
|
const handleSizeChangeCode = (size) => {
|
|
pageSizeCode.value = size
|
|
currentPageCode.value = 1
|
|
getInviteCodeList()
|
|
}
|
|
|
|
const handleCurrentChangeCode = (page) => {
|
|
currentPageCode.value = page
|
|
getInviteCodeList()
|
|
}
|
|
|
|
// 筛选处理
|
|
const handleSearch = () => {
|
|
currentPageCode.value = 1
|
|
getInviteCodeList()
|
|
}
|
|
|
|
const handleReset = () => {
|
|
filterForm.value = {
|
|
userId: '',
|
|
inviteCode: '',
|
|
isUsed: ''
|
|
}
|
|
currentPageCode.value = 1
|
|
getInviteCodeList()
|
|
}
|
|
|
|
// 标签页切换处理
|
|
const handleTabChange = (tabName) => {
|
|
if (tabName === 'inviteCode') {
|
|
getInviteCodeList()
|
|
} else if (tabName === 'invitedUsers') {
|
|
getInviteList()
|
|
}
|
|
}
|
|
|
|
// 返回上一页
|
|
const handleBack = () => {
|
|
router.push('/admin/users')
|
|
}
|
|
|
|
// 初始化
|
|
onMounted(() => {
|
|
// 默认加载邀请码列表
|
|
getInviteCodeList()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.user-invites {
|
|
padding: 20px;
|
|
}
|
|
|
|
.invite-table {
|
|
background: white;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid #e5e7eb;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.invite-code-section {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.filter-section {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
padding: 16px;
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid #e5e7eb;
|
|
}
|
|
|
|
.filter-form {
|
|
display: flex;
|
|
gap: 16px;
|
|
}
|
|
|
|
.pagination {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
padding: 16px 0;
|
|
}
|
|
|
|
/* 表格样式优化 */
|
|
.invite-table :deep(.el-table) {
|
|
width: 100% !important;
|
|
table-layout: auto;
|
|
}
|
|
|
|
.invite-table :deep(.el-table__cell) {
|
|
padding: 8px 12px;
|
|
white-space: nowrap;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.invite-table :deep(.el-table__row) {
|
|
height: 66px;
|
|
}
|
|
|
|
.invite-table :deep(.el-table__header-wrapper) {
|
|
background-color: #f8fafc;
|
|
}
|
|
|
|
.invite-table :deep(.el-table__header th) {
|
|
background-color: #f8fafc;
|
|
color: #374151;
|
|
font-weight: 600;
|
|
border-bottom: 2px solid #e5e7eb;
|
|
white-space: nowrap !important;
|
|
text-align: center;
|
|
text-overflow: ellipsis;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 标签页样式 */
|
|
:deep(.el-tabs__content) {
|
|
padding-top: 20px;
|
|
}
|
|
</style>
|