main
parent
5fc904198b
commit
0efc812afb
@ -0,0 +1,81 @@
|
||||
package com.luojia_channel.modules.admin.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 管理员评论DTO
|
||||
*/
|
||||
@Data
|
||||
public class AdminCommentDTO {
|
||||
/**
|
||||
* 评论ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
private String userAvatar;
|
||||
|
||||
/**
|
||||
* 帖子ID
|
||||
*/
|
||||
private Long postId;
|
||||
|
||||
/**
|
||||
* 帖子标题
|
||||
*/
|
||||
private String postTitle;
|
||||
|
||||
/**
|
||||
* 父评论ID
|
||||
*/
|
||||
private Long parentCommentId;
|
||||
|
||||
/**
|
||||
* 顶级评论ID
|
||||
*/
|
||||
private Long topId;
|
||||
|
||||
/**
|
||||
* 回复用户名
|
||||
*/
|
||||
private String replyUsername;
|
||||
|
||||
/**
|
||||
* 点赞数
|
||||
*/
|
||||
private Integer likeCount;
|
||||
|
||||
/**
|
||||
* 回复数
|
||||
*/
|
||||
private Integer replyCount;
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package com.luojia_channel.modules.admin.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 管理员用户DTO - 仅包含管理员可见的非隐私信息
|
||||
*/
|
||||
@Data
|
||||
public class AdminUserDTO {
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 用户状态(1正常 2冻结)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 用户角色(1普通用户 2管理员 3超级管理员)
|
||||
*/
|
||||
private Integer role;
|
||||
|
||||
/**
|
||||
* 用户积分
|
||||
*/
|
||||
private Integer integral;
|
||||
|
||||
/**
|
||||
* 发帖数量
|
||||
*/
|
||||
private Integer postCount;
|
||||
|
||||
/**
|
||||
* 评论数量
|
||||
*/
|
||||
private Integer commentCount;
|
||||
|
||||
/**
|
||||
* 粉丝数量
|
||||
*/
|
||||
private Integer followerCount;
|
||||
|
||||
/**
|
||||
* 关注数量
|
||||
*/
|
||||
private Integer followingCount;
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package com.luojia_channel.modules.admin.service;
|
||||
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.modules.admin.dto.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 管理员服务接口
|
||||
*/
|
||||
public interface AdminService {
|
||||
/**
|
||||
* 获取系统总览数据
|
||||
* @return 总览数据
|
||||
*/
|
||||
AdminOverviewDTO getOverview();
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @param page 页码
|
||||
* @param size 每页数量
|
||||
* @param keyword 搜索关键词
|
||||
* @param role 角色
|
||||
* @param status 状态
|
||||
* @return 用户列表分页结果
|
||||
*/
|
||||
PageResponse<AdminUserDTO> getUserList(Integer page, Integer size, String keyword, Integer role, Integer status);
|
||||
|
||||
/**
|
||||
* 修改用户状态
|
||||
* @param id 用户ID
|
||||
* @param status 状态
|
||||
*/
|
||||
void changeUserStatus(Long id, Integer status);
|
||||
|
||||
/**
|
||||
* 修改用户角色
|
||||
* @param id 用户ID
|
||||
* @param role 角色
|
||||
*/
|
||||
void changeUserRole(Long id, Integer role);
|
||||
|
||||
/**
|
||||
* 获取帖子列表
|
||||
* @param page 页码
|
||||
* @param size 每页数量
|
||||
* @param keyword 搜索关键词
|
||||
* @param categoryId 分类ID
|
||||
* @param status 状态
|
||||
* @return 帖子列表分页结果
|
||||
*/
|
||||
PageResponse<AdminPostDTO> getPostList(Integer page, Integer size, String keyword, Long categoryId, Integer status);
|
||||
|
||||
/**
|
||||
* 删除帖子
|
||||
* @param id 帖子ID
|
||||
*/
|
||||
void deletePost(Long id);
|
||||
|
||||
/**
|
||||
* 修改帖子状态
|
||||
* @param id 帖子ID
|
||||
* @param action 操作类型
|
||||
*/
|
||||
void changePostStatus(Long id, Integer action);
|
||||
|
||||
/**
|
||||
* 获取评论列表
|
||||
* @param page 页码
|
||||
* @param size 每页数量
|
||||
* @param keyword 搜索关键词
|
||||
* @param postId 帖子ID
|
||||
* @return 评论列表分页结果
|
||||
*/
|
||||
PageResponse<AdminCommentDTO> getCommentList(Integer page, Integer size, String keyword, Long postId);
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
* @param id 评论ID
|
||||
*/
|
||||
void deleteComment(Long id);
|
||||
|
||||
/**
|
||||
* 获取用户统计数据
|
||||
* @param type 统计类型
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getUserStatistics(String type, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 获取帖子统计数据
|
||||
* @param type 统计类型
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getPostStatistics(String type, String startDate, String endDate);
|
||||
}
|
@ -1,36 +1,36 @@
|
||||
##本地开发环境
|
||||
# lj:
|
||||
# db:
|
||||
# host: localhost
|
||||
# password: lzt&264610
|
||||
# redis:
|
||||
# host: localhost
|
||||
# port: 6379
|
||||
# password: 123456
|
||||
# rabbitmq:
|
||||
# host: localhost
|
||||
# port: 5672
|
||||
# username: guest
|
||||
# password: guest
|
||||
# minio:
|
||||
# endpoint: http://localhost:9000
|
||||
# accessKey: minioadmin
|
||||
# secretKey: minioadmin
|
||||
|
||||
lj:
|
||||
lj:
|
||||
db:
|
||||
host: 192.168.125.128
|
||||
password: MySQL@5678
|
||||
host: localhost
|
||||
password: 123456
|
||||
redis:
|
||||
host: 192.168.125.128
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: Redis@9012
|
||||
password: 123456
|
||||
rabbitmq:
|
||||
host: 192.168.125.128
|
||||
host: localhost
|
||||
port: 5672
|
||||
username: rabbit_admin
|
||||
password: Rabbit@3456
|
||||
username: root
|
||||
password: 123456
|
||||
minio:
|
||||
endpoint: http://192.168.125.128:9000
|
||||
accessKey: minio_admin
|
||||
secretKey: Minio@1234
|
||||
endpoint: http://localhost:9000
|
||||
accessKey: minioadmin
|
||||
secretKey: minioadmin
|
||||
|
||||
#lj:
|
||||
# db:
|
||||
# host: 192.168.125.128
|
||||
# password: MySQL@5678
|
||||
# redis:
|
||||
# host: 192.168.125.128
|
||||
# port: 6379
|
||||
# password: Redis@9012
|
||||
# rabbitmq:
|
||||
# host: 192.168.125.128
|
||||
# port: 5672
|
||||
# username: rabbit_admin
|
||||
# password: Rabbit@3456
|
||||
# minio:
|
||||
# endpoint: http://192.168.125.128:9000
|
||||
# accessKey: minio_admin
|
||||
# secretKey: Minio@1234
|
||||
|
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<div class="admin-comments">
|
||||
<!-- 搜索和筛选区域 -->
|
||||
<div class="filter-area">
|
||||
<el-form :inline="true" :model="searchForm" class="filter-form">
|
||||
<el-form-item label="内容关键词">
|
||||
<el-input v-model="searchForm.keyword" placeholder="评论内容" clearable @keyup.enter="handleSearch" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="帖子ID">
|
||||
<el-input v-model="searchForm.postId" placeholder="帖子ID" clearable @keyup.enter="handleSearch" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetForm">
|
||||
<el-icon><RefreshLeft /></el-icon> 重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-area">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="commentList"
|
||||
style="width: 100%"
|
||||
border
|
||||
stripe
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="index" width="60" align="center" label="序号" />
|
||||
|
||||
<el-table-column label="评论内容" min-width="300">
|
||||
<template #default="{ row }">
|
||||
<div class="comment-content">
|
||||
<el-tooltip :content="row.content" placement="top" :show-after="1000">
|
||||
<p>{{ row.content }}</p>
|
||||
</el-tooltip>
|
||||
|
||||
<div v-if="row.replyUsername" class="reply-info">
|
||||
<span>回复 @{{ row.replyUsername }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="评论者" width="150">
|
||||
<template #default="{ row }">
|
||||
<div class="user-info">
|
||||
<el-avatar :size="30" :src="row.userAvatar" />
|
||||
<span class="username">{{ row.username }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="所属帖子" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<router-link :to="`/post/${row.postId}`" class="post-link" target="_blank">
|
||||
{{ row.postTitle }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="createTime" label="评论时间" width="180" sortable />
|
||||
|
||||
<el-table-column label="点赞数" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.likeCount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-popconfirm
|
||||
title="确定要删除该评论吗?此操作不可恢复!"
|
||||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button size="small" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.size"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { Search, RefreshLeft } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import axios from 'axios';
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
keyword: '',
|
||||
postId: ''
|
||||
});
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
});
|
||||
|
||||
// 数据加载状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 评论列表
|
||||
const commentList = ref([]);
|
||||
|
||||
// 加载评论列表
|
||||
const loadComments = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await axios.get('/api/admin/comments', {
|
||||
params: {
|
||||
page: pagination.page,
|
||||
size: pagination.size,
|
||||
keyword: searchForm.keyword || null,
|
||||
postId: searchForm.postId ? Number(searchForm.postId) : null
|
||||
}
|
||||
});
|
||||
|
||||
if (response.data.code === 0) {
|
||||
const data = response.data.data;
|
||||
commentList.value = data.records;
|
||||
pagination.total = data.total;
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '加载评论列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载评论列表失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.page = 1;
|
||||
loadComments();
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
searchForm.keyword = '';
|
||||
searchForm.postId = '';
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
// 分页大小变化
|
||||
const handleSizeChange = (size) => {
|
||||
pagination.size = size;
|
||||
loadComments();
|
||||
};
|
||||
|
||||
// 当前页变化
|
||||
const handleCurrentChange = (page) => {
|
||||
pagination.page = page;
|
||||
loadComments();
|
||||
};
|
||||
|
||||
// 删除评论
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
const response = await axios.delete(`/api/admin/comments/${row.id}`);
|
||||
|
||||
if (response.data.code === 0) {
|
||||
ElMessage.success('评论已删除');
|
||||
loadComments();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '删除失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除评论失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadComments();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-comments {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-area {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.comment-content {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.comment-content p {
|
||||
margin: 0;
|
||||
max-height: 60px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-word;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.reply-info {
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.post-link {
|
||||
color: #409EFF;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.post-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 表格响应式调整 */
|
||||
@media screen and (max-width: 1200px) {
|
||||
.el-table {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="admin-dashboard">
|
||||
<!-- 使用全局Header组件保持一致的顶部导航 -->
|
||||
<Header />
|
||||
|
||||
<div class="admin-container">
|
||||
<!-- 左侧菜单 -->
|
||||
<div class="admin-sidebar">
|
||||
<h2 class="sidebar-title">管理中心</h2>
|
||||
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
class="admin-menu"
|
||||
router
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item index="/admin">
|
||||
<el-icon><DataLine /></el-icon>
|
||||
<span>系统概览</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/users">
|
||||
<el-icon><User /></el-icon>
|
||||
<span>用户管理</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/posts">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>帖子管理</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/comments">
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
<span>评论管理</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="/admin/categories">
|
||||
<el-icon><Collection /></el-icon>
|
||||
<span>分类管理</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<div class="admin-content">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import {
|
||||
DataLine,
|
||||
User,
|
||||
Document,
|
||||
ChatDotRound,
|
||||
Collection
|
||||
} from '@element-plus/icons-vue';
|
||||
import Header from '@/components/Header.vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 当前激活的菜单项
|
||||
const activeMenu = ref('');
|
||||
|
||||
// 根据当前路由设置激活菜单
|
||||
const setActiveMenu = () => {
|
||||
activeMenu.value = route.path;
|
||||
};
|
||||
|
||||
// 处理菜单选择
|
||||
const handleSelect = (key) => {
|
||||
activeMenu.value = key;
|
||||
};
|
||||
|
||||
// 检查用户权限
|
||||
const checkPermission = () => {
|
||||
console.log('正在检查管理员权限:', userStore.isLoggedIn, userStore.userInfo.role);
|
||||
|
||||
// 检查用户是否登录以及是否有管理员权限
|
||||
if (!userStore.isLoggedIn) {
|
||||
ElMessage.error('请先登录');
|
||||
router.push('/');
|
||||
return;
|
||||
}
|
||||
|
||||
if (userStore.userInfo.role < 2) {
|
||||
ElMessage.error('您没有管理员权限');
|
||||
router.push('/');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('权限检查通过,用户角色:', userStore.userInfo.role);
|
||||
};
|
||||
|
||||
// 监听路由变化
|
||||
watch(() => route.path, () => {
|
||||
setActiveMenu();
|
||||
});
|
||||
|
||||
// 组件挂载时
|
||||
onMounted(() => {
|
||||
setActiveMenu();
|
||||
checkPermission();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.admin-container {
|
||||
display: flex;
|
||||
margin-top: 50px; /* 为Header预留空间 */
|
||||
min-height: calc(100vh - 50px);
|
||||
}
|
||||
|
||||
.admin-sidebar {
|
||||
width: 240px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
top: 50px; /* Header高度 */
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.admin-menu {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.admin-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
margin-left: 240px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 图标样式 */
|
||||
.el-menu-item .el-icon {
|
||||
margin-right: 10px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.admin-sidebar {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admin-content {
|
||||
margin-left: 64px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,408 @@
|
||||
<template>
|
||||
<div class="admin-posts">
|
||||
<!-- 搜索和筛选区域 -->
|
||||
<div class="filter-area">
|
||||
<el-form :inline="true" :model="searchForm" class="filter-form">
|
||||
<el-form-item label="关键词">
|
||||
<el-input v-model="searchForm.keyword" placeholder="标题/内容" clearable @keyup.enter="handleSearch" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="分类">
|
||||
<el-select v-model="searchForm.categoryId" placeholder="全部分类" clearable>
|
||||
<el-option
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:label="category.name"
|
||||
:value="category.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="全部状态" clearable>
|
||||
<el-option label="正常" :value="0" />
|
||||
<el-option label="置顶" :value="1" />
|
||||
<el-option label="隐藏" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetForm">
|
||||
<el-icon><RefreshLeft /></el-icon> 重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-area">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="postList"
|
||||
style="width: 100%"
|
||||
border
|
||||
stripe
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="index" width="60" align="center" label="序号" />
|
||||
|
||||
<el-table-column prop="title" label="帖子标题" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="row.title" placement="top" :show-after="1000">
|
||||
<router-link :to="`/post/${row.id}`" class="post-title" target="_blank">
|
||||
{{ row.title }}
|
||||
</router-link>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="作者" width="150">
|
||||
<template #default="{ row }">
|
||||
<div class="user-info">
|
||||
<el-avatar :size="30" :src="row.userAvatar" />
|
||||
<span class="username">{{ row.username }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="categoryName" label="分类" width="120" />
|
||||
|
||||
<el-table-column label="统计" width="180">
|
||||
<template #default="{ row }">
|
||||
<div class="post-stats">
|
||||
<span title="浏览"><el-icon><View /></el-icon> {{ row.viewCount }}</span>
|
||||
<span title="点赞"><el-icon><Star /></el-icon> {{ row.likeCount }}</span>
|
||||
<span title="评论"><el-icon><ChatDotRound /></el-icon> {{ row.commentCount }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="createTime" label="发布时间" width="180" sortable />
|
||||
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.status === 0" type="success">正常</el-tag>
|
||||
<el-tag v-else-if="row.status === 1" type="warning">置顶</el-tag>
|
||||
<el-tag v-else-if="row.status === 2" type="info">隐藏</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="250" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.status !== 1" size="small" type="primary" @click="handleSetTop(row)">
|
||||
置顶
|
||||
</el-button>
|
||||
<el-button v-if="row.status === 1" size="small" type="warning" @click="handleCancelTop(row)">
|
||||
取消置顶
|
||||
</el-button>
|
||||
|
||||
<el-button v-if="row.status !== 2" size="small" type="info" @click="handleHide(row)">
|
||||
隐藏
|
||||
</el-button>
|
||||
<el-button v-if="row.status === 2" size="small" type="success" @click="handleShow(row)">
|
||||
显示
|
||||
</el-button>
|
||||
|
||||
<el-popconfirm
|
||||
title="确定要删除该帖子吗?此操作不可恢复!"
|
||||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button size="small" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.size"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { Search, RefreshLeft, View, Star, ChatDotRound } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import axios from 'axios';
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
keyword: '',
|
||||
categoryId: null,
|
||||
status: null
|
||||
});
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
});
|
||||
|
||||
// 数据加载状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 帖子列表
|
||||
const postList = ref([]);
|
||||
|
||||
// 分类列表
|
||||
const categories = ref([]);
|
||||
|
||||
// 加载分类数据
|
||||
const loadCategories = async () => {
|
||||
try {
|
||||
const response = await axios.get('/api/categories');
|
||||
if (response.data.code === 0) {
|
||||
categories.value = response.data.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载分类失败:', error);
|
||||
ElMessage.error('加载分类失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 加载帖子列表
|
||||
const loadPosts = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await axios.get('/api/admin/posts', {
|
||||
params: {
|
||||
page: pagination.page,
|
||||
size: pagination.size,
|
||||
keyword: searchForm.keyword || null,
|
||||
categoryId: searchForm.categoryId || null,
|
||||
status: searchForm.status !== null ? searchForm.status : null
|
||||
}
|
||||
});
|
||||
|
||||
if (response.data.code === 0) {
|
||||
const data = response.data.data;
|
||||
postList.value = data.records;
|
||||
pagination.total = data.total;
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '加载帖子列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载帖子列表失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.page = 1;
|
||||
loadPosts();
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
searchForm.keyword = '';
|
||||
searchForm.categoryId = null;
|
||||
searchForm.status = null;
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
// 分页大小变化
|
||||
const handleSizeChange = (size) => {
|
||||
pagination.size = size;
|
||||
loadPosts();
|
||||
};
|
||||
|
||||
// 当前页变化
|
||||
const handleCurrentChange = (page) => {
|
||||
pagination.page = page;
|
||||
loadPosts();
|
||||
};
|
||||
|
||||
// 置顶帖子
|
||||
const handleSetTop = async (row) => {
|
||||
try {
|
||||
const response = await axios.put(`/api/admin/posts/${row.id}/status`, null, {
|
||||
params: { action: 1 }
|
||||
});
|
||||
|
||||
if (response.data.code === 0) {
|
||||
ElMessage.success('帖子已置顶');
|
||||
loadPosts();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('置顶帖子失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 取消置顶
|
||||
const handleCancelTop = async (row) => {
|
||||
try {
|
||||
const response = await axios.put(`/api/admin/posts/${row.id}/status`, null, {
|
||||
params: { action: 2 }
|
||||
});
|
||||
|
||||
if (response.data.code === 0) {
|
||||
ElMessage.success('已取消置顶');
|
||||
loadPosts();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('取消置顶失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 隐藏帖子
|
||||
const handleHide = async (row) => {
|
||||
try {
|
||||
const response = await axios.put(`/api/admin/posts/${row.id}/status`, null, {
|
||||
params: { action: 3 }
|
||||
});
|
||||
|
||||
if (response.data.code === 0) {
|
||||
ElMessage.success('帖子已隐藏');
|
||||
loadPosts();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('隐藏帖子失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 显示帖子
|
||||
const handleShow = async (row) => {
|
||||
try {
|
||||
const response = await axios.put(`/api/admin/posts/${row.id}/status`, null, {
|
||||
params: { action: 4 }
|
||||
});
|
||||
|
||||
if (response.data.code === 0) {
|
||||
ElMessage.success('帖子已显示');
|
||||
loadPosts();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('显示帖子失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 删除帖子
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
const response = await axios.delete(`/api/admin/posts/${row.id}`);
|
||||
|
||||
if (response.data.code === 0) {
|
||||
ElMessage.success('帖子已删除');
|
||||
loadPosts();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '删除失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除帖子失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadCategories();
|
||||
loadPosts();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-posts {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-area {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.post-title:hover {
|
||||
color: #54ac52;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.post-stats {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.post-stats span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.post-stats .el-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 表格响应式调整 */
|
||||
@media screen and (max-width: 1200px) {
|
||||
.el-table {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,373 @@
|
||||
<template>
|
||||
<div class="admin-users">
|
||||
<!-- 搜索和筛选区域 -->
|
||||
<div class="filter-area">
|
||||
<el-form :inline="true" :model="searchForm" class="filter-form">
|
||||
<el-form-item label="关键词">
|
||||
<el-input v-model="searchForm.keyword" placeholder="用户名" clearable @keyup.enter="handleSearch" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="searchForm.role" placeholder="全部角色" clearable>
|
||||
<el-option label="普通用户" :value="1" />
|
||||
<el-option label="管理员" :value="2" />
|
||||
<el-option label="超级管理员" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="全部状态" clearable>
|
||||
<el-option label="正常" :value="1" />
|
||||
<el-option label="冻结" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetForm">
|
||||
<el-icon><RefreshLeft /></el-icon> 重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-area">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="userList"
|
||||
style="width: 100%"
|
||||
border
|
||||
stripe
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="index" width="60" align="center" label="序号" />
|
||||
|
||||
<el-table-column label="用户信息" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<div class="user-info">
|
||||
<el-avatar :size="40" :src="row.avatar" />
|
||||
<div class="user-meta">
|
||||
<div class="username">{{ row.username }}</div>
|
||||
<div class="user-id">ID: {{ row.id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="统计数据" width="240">
|
||||
<template #default="{ row }">
|
||||
<div class="user-stats">
|
||||
<span title="发帖"><el-icon><Document /></el-icon> {{ row.postCount || 0 }}</span>
|
||||
<span title="评论"><el-icon><ChatDotRound /></el-icon> {{ row.commentCount || 0 }}</span>
|
||||
<span title="粉丝"><el-icon><User /></el-icon> {{ row.followerCount || 0 }}</span>
|
||||
<span title="积分"><el-icon><Medal /></el-icon> {{ row.integral || 0 }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="createTime" label="注册时间" width="180" sortable />
|
||||
|
||||
<el-table-column label="角色" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.role === 1" type="info">普通用户</el-tag>
|
||||
<el-tag v-else-if="row.role === 2" type="warning">管理员</el-tag>
|
||||
<el-tag v-else-if="row.role === 3" type="danger">超级管理员</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.status === 1" type="success">正常</el-tag>
|
||||
<el-tag v-else-if="row.status === 2" type="danger">冻结</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="250" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.status === 1" size="small" type="danger" @click="handleFreezeUser(row)">
|
||||
冻结账号
|
||||
</el-button>
|
||||
<el-button v-if="row.status === 2" size="small" type="success" @click="handleUnfreezeUser(row)">
|
||||
解冻账号
|
||||
</el-button>
|
||||
|
||||
<el-dropdown trigger="click" @command="(cmd) => handleRoleChange(cmd, row)">
|
||||
<el-button size="small" type="primary">
|
||||
设置角色 <el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="1" :disabled="row.role === 1">普通用户</el-dropdown-item>
|
||||
<el-dropdown-item :command="2" :disabled="row.role === 2">管理员</el-dropdown-item>
|
||||
<el-dropdown-item :command="3" :disabled="row.role === 3 || currentUserRole !== 3">超级管理员</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.size"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { Search, RefreshLeft, Document, ChatDotRound, User, Medal, ArrowDown } from '@element-plus/icons-vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { useUserStore } from '@/stores/user.js';
|
||||
import axios from 'axios';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const currentUserRole = computed(() => userStore.userInfo?.role || 1);
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
keyword: '',
|
||||
role: null,
|
||||
status: null
|
||||
});
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
});
|
||||
|
||||
// 数据加载状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 用户列表
|
||||
const userList = ref([]);
|
||||
|
||||
// 加载用户列表
|
||||
const loadUsers = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await axios.get('/api/admin/users', {
|
||||
params: {
|
||||
page: pagination.page,
|
||||
size: pagination.size,
|
||||
keyword: searchForm.keyword || null,
|
||||
role: searchForm.role || null,
|
||||
status: searchForm.status || null
|
||||
}
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
const data = response.data.data;
|
||||
userList.value = data.records;
|
||||
pagination.total = data.total;
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '加载用户列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载用户列表失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.page = 1;
|
||||
loadUsers();
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
searchForm.keyword = '';
|
||||
searchForm.role = null;
|
||||
searchForm.status = null;
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
// 分页大小变化
|
||||
const handleSizeChange = (size) => {
|
||||
pagination.size = size;
|
||||
loadUsers();
|
||||
};
|
||||
|
||||
// 当前页变化
|
||||
const handleCurrentChange = (page) => {
|
||||
pagination.page = page;
|
||||
loadUsers();
|
||||
};
|
||||
|
||||
// 冻结用户
|
||||
const handleFreezeUser = async (row) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'确定要冻结该用户吗?冻结后用户将无法登录系统',
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
);
|
||||
|
||||
const response = await axios.put(`/api/admin/users/${row.id}/status`, null, {
|
||||
params: { status: 2 }
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
ElMessage.success('用户已冻结');
|
||||
loadUsers();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('冻结用户失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 解冻用户
|
||||
const handleUnfreezeUser = async (row) => {
|
||||
try {
|
||||
const response = await axios.put(`/api/admin/users/${row.id}/status`, null, {
|
||||
params: { status: 1 }
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
ElMessage.success('用户已解冻');
|
||||
loadUsers();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解冻用户失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 修改用户角色
|
||||
const handleRoleChange = async (role, row) => {
|
||||
if (role === row.role) return;
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要将该用户角色修改为${role === 1 ? '普通用户' : role === 2 ? '管理员' : '超级管理员'}吗?`,
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
);
|
||||
|
||||
const response = await axios.put(`/api/admin/users/${row.id}/role`, null, {
|
||||
params: { role }
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
ElMessage.success('用户角色已修改');
|
||||
loadUsers();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('修改用户角色失败:', error);
|
||||
ElMessage.error('网络错误,请稍后重试');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadUsers();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-users {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-area {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.user-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.user-stats {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.user-stats span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.user-stats .el-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 表格响应式调整 */
|
||||
@media screen and (max-width: 1200px) {
|
||||
.el-table {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in new issue