refactor(project): Reorganize frontend and backend directories
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
<template>
|
||||
<div class="user-list-page">
|
||||
<!-- 搜索表单 -->
|
||||
<el-card shadow="never" class="search-card">
|
||||
<el-form :model="queryParams" inline>
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="queryParams.keyword"
|
||||
placeholder="用户名/昵称/手机号"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="全部" clearable>
|
||||
<el-option label="正常" :value="1" />
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="注册时间">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="handleDateChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleReset">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-card shadow="never" class="table-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>用户列表</span>
|
||||
<el-button type="primary" @click="handleExport">
|
||||
<el-icon><Download /></el-icon>
|
||||
导出
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="userList" style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column label="用户信息" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<div class="user-info">
|
||||
<el-avatar :src="row.avatar" :size="40">
|
||||
{{ row.nickname?.charAt(0) }}
|
||||
</el-avatar>
|
||||
<div class="info">
|
||||
<div class="nickname">{{ row.nickname }}</div>
|
||||
<div class="username">@{{ row.username }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="email" label="邮箱" width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="phone" label="手机号" width="120" />
|
||||
<el-table-column label="等级" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="warning">Lv.{{ row.level }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据" width="200">
|
||||
<template #default="{ row }">
|
||||
<div class="stats">
|
||||
<span>视频 {{ row.videoCount }}</span>
|
||||
<span>粉丝 {{ formatNumber(row.followerCount) }}</span>
|
||||
<span>获赞 {{ formatNumber(row.likeCount) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? '正常' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="注册时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" text @click="handleDetail(row)">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="row.status === 1 ? 'danger' : 'success'"
|
||||
text
|
||||
@click="handleStatus(row)"
|
||||
>
|
||||
{{ row.status === 1 ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button type="warning" text @click="handleResetPassword(row)">
|
||||
重置密码
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.page"
|
||||
v-model:page-size="queryParams.size"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 用户详情对话框 -->
|
||||
<el-dialog v-model="detailVisible" title="用户详情" width="600px">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="用户ID">{{ currentUser?.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用户名">{{ currentUser?.username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="昵称">{{ currentUser?.nickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ currentUser?.email }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号">{{ currentUser?.phone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">
|
||||
{{ currentUser?.gender === 1 ? '男' : currentUser?.gender === 2 ? '女' : '未知' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="等级">Lv.{{ currentUser?.level }}</el-descriptions-item>
|
||||
<el-descriptions-item label="VIP等级">
|
||||
{{ currentUser?.vipLevel > 0 ? `VIP${currentUser?.vipLevel}` : '普通用户' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="粉丝数">{{ currentUser?.followerCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关注数">{{ currentUser?.followingCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="视频数">{{ currentUser?.videoCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="获赞数">{{ currentUser?.likeCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="currentUser?.status === 1 ? 'success' : 'danger'">
|
||||
{{ currentUser?.status === 1 ? '正常' : '禁用' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="注册时间">{{ formatDateTime(currentUser?.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ formatDateTime(currentUser?.updateTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="个人签名" :span="2">
|
||||
{{ currentUser?.signature || '暂无' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { userApi } from '@/api/user.ts'
|
||||
import { formatDateTime, formatNumber } from '@/utils/format.ts'
|
||||
import type { PlatformUser, UserQueryParams } from '@/types'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive<UserQueryParams>({
|
||||
page: 1,
|
||||
size: 10,
|
||||
keyword: '',
|
||||
status: undefined,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
})
|
||||
|
||||
// 日期范围
|
||||
const dateRange = ref<[string, string] | null>(null)
|
||||
|
||||
// 数据
|
||||
const loading = ref(false)
|
||||
const userList = ref<PlatformUser[]>([])
|
||||
const total = ref(0)
|
||||
|
||||
// 详情
|
||||
const detailVisible = ref(false)
|
||||
const currentUser = ref<PlatformUser | null>(null)
|
||||
|
||||
// 加载用户列表
|
||||
const loadUserList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await userApi.getList(queryParams)
|
||||
userList.value = res.records
|
||||
total.value = res.total
|
||||
} catch (error) {
|
||||
console.error('加载用户列表失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
queryParams.page = 1
|
||||
loadUserList()
|
||||
}
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
queryParams.keyword = ''
|
||||
queryParams.status = undefined
|
||||
queryParams.startDate = ''
|
||||
queryParams.endDate = ''
|
||||
dateRange.value = null
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
// 日期变化
|
||||
const handleDateChange = (value: [string, string] | null) => {
|
||||
if (value) {
|
||||
queryParams.startDate = value[0]
|
||||
queryParams.endDate = value[1]
|
||||
} else {
|
||||
queryParams.startDate = ''
|
||||
queryParams.endDate = ''
|
||||
}
|
||||
}
|
||||
|
||||
// 分页
|
||||
const handleSizeChange = (size: number) => {
|
||||
queryParams.size = size
|
||||
loadUserList()
|
||||
}
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
queryParams.page = page
|
||||
loadUserList()
|
||||
}
|
||||
|
||||
// 详情
|
||||
const handleDetail = async (row: PlatformUser) => {
|
||||
try {
|
||||
currentUser.value = await userApi.getDetail(row.id)
|
||||
detailVisible.value = true
|
||||
} catch (error) {
|
||||
ElMessage.error('获取用户详情失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 切换状态
|
||||
const handleStatus = async (row: PlatformUser) => {
|
||||
const newStatus = row.status === 1 ? 0 : 1
|
||||
const action = newStatus === 0 ? '禁用' : '启用'
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要${action}该用户吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
await userApi.updateStatus(row.id, newStatus)
|
||||
ElMessage.success(`${action}成功`)
|
||||
loadUserList()
|
||||
} catch (error) {
|
||||
// 取消或失败
|
||||
}
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
const handleResetPassword = async (row: PlatformUser) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要重置该用户的密码吗?密码将重置为默认密码。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
await userApi.resetPassword(row.id)
|
||||
ElMessage.success('密码重置成功')
|
||||
} catch (error) {
|
||||
// 取消或失败
|
||||
}
|
||||
}
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
ElMessage.info('导出功能开发中')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadUserList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.user-list-page {
|
||||
.search-card {
|
||||
margin-bottom: 20px;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.table-card {
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.info {
|
||||
.nickname {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user