Initial commit
This commit is contained in:
172
frontend/src/views/admin/users.vue
Normal file
172
frontend/src/views/admin/users.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<div class="admin-users-page">
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">用户管理</h2>
|
||||
<div class="header-actions">
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索用户..."
|
||||
clearable
|
||||
style="width: 200px"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="filteredUsers" style="width: 100%" v-loading="loading">
|
||||
<el-table-column label="用户" width="250">
|
||||
<template #default="{ row }">
|
||||
<div class="user-cell">
|
||||
<el-avatar :size="40" :src="row.avatar">{{ row.nickname?.charAt(0) }}</el-avatar>
|
||||
<div class="user-info">
|
||||
<span class="name">{{ row.nickname }}</span>
|
||||
<span class="phone">{{ row.phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="levelName" label="等级" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="warning" size="small">{{ row.levelName }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="points" label="积分" width="100" />
|
||||
<el-table-column prop="inviteCount" label="邀请人数" width="100" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 'active' ? 'success' : 'danger'" size="small">
|
||||
{{ row.status === 'active' ? '正常' : '封禁' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createdAt" label="注册时间" width="180" />
|
||||
<el-table-column label="操作" fixed="right" width="200">
|
||||
<template #default="{ row }">
|
||||
<el-button text type="primary" size="small" @click="viewUser(row)">查看</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'active'"
|
||||
text
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="banUser(row)"
|
||||
>
|
||||
封禁
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
text
|
||||
type="success"
|
||||
size="small"
|
||||
@click="unbanUser(row)"
|
||||
>
|
||||
解封
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog v-model="userDialogVisible" title="用户详情" width="500px">
|
||||
<template v-if="currentUser">
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="用户ID">{{ currentUser.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="昵称">{{ currentUser.nickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号">{{ currentUser.phone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ currentUser.email || '未设置' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="会员等级">{{ currentUser.levelName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="积分余额">{{ currentUser.points }}</el-descriptions-item>
|
||||
<el-descriptions-item label="累计积分">{{ currentUser.totalPoints }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邀请人数">{{ currentUser.inviteCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邀请码">{{ currentUser.inviteCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="注册时间">{{ currentUser.createdAt }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最后登录">{{ currentUser.lastLoginAt }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useAdminStore } from '@/stores'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
const adminStore = useAdminStore()
|
||||
|
||||
const loading = ref(false)
|
||||
const searchKeyword = ref('')
|
||||
const userDialogVisible = ref(false)
|
||||
const currentUser = ref(null)
|
||||
|
||||
const users = computed(() => adminStore.users)
|
||||
|
||||
const filteredUsers = computed(() => {
|
||||
if (!searchKeyword.value) return users.value
|
||||
const keyword = searchKeyword.value.toLowerCase()
|
||||
return users.value.filter(u =>
|
||||
u.nickname?.toLowerCase().includes(keyword) ||
|
||||
u.phone?.includes(keyword)
|
||||
)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
adminStore.loadUsers()
|
||||
})
|
||||
|
||||
const viewUser = (user) => {
|
||||
currentUser.value = user
|
||||
userDialogVisible.value = true
|
||||
}
|
||||
|
||||
const banUser = (user) => {
|
||||
ElMessageBox.confirm(`确定要封禁用户 ${user.nickname} 吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
adminStore.banUser(user.id)
|
||||
ElMessage.success('已封禁')
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
const unbanUser = (user) => {
|
||||
adminStore.unbanUser(user.id)
|
||||
ElMessage.success('已解封')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.admin-users-page {
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.user-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.user-info {
|
||||
.name {
|
||||
display: block;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.phone {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user