v0.2.19
parent
336f062650
commit
b5e4d4b477
|
|
@ -21,5 +21,6 @@
|
|||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@10.6.5+sha512.cdf928fca20832cd59ec53826492b7dc25dc524d4370b6b4adbf65803d32efaa6c1c88147c0ae4e8d579a6c9eec715757b50d4fa35eea179d868eada4ed043af"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export function getActivity(id) {
|
|||
// 创建活动
|
||||
export function createActivity(data) {
|
||||
return request({
|
||||
url: '/activity',
|
||||
url: '/activity/create',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ export const studentRoutes = [
|
|||
path: 'notice',
|
||||
component: () => import('@/views/notice/index.vue'),
|
||||
meta: { title: '公告', roles: ['PARTICIPANT'] }
|
||||
},
|
||||
{
|
||||
path: 'profile',
|
||||
component: () => import('@/views/profile/index.vue'),
|
||||
meta: { title: '个人中心', roles: ['PARTICIPANT'] }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -87,6 +92,11 @@ export const adminRoutes = [
|
|||
path: 'edit/:id',
|
||||
component: () => import('@/views/admin/activity/form.vue'),
|
||||
meta: { title: '编辑活动' }
|
||||
},
|
||||
{
|
||||
path: 'approval',
|
||||
component: () => import('@/views/admin/activity/approval.vue'),
|
||||
meta: { title: '活动审批' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -109,6 +119,11 @@ export const adminRoutes = [
|
|||
path: 'edit/:id',
|
||||
component: () => import('@/views/admin/club/form.vue'),
|
||||
meta: { title: '编辑社团' }
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
component: () => import('@/views/admin/club/members.vue'),
|
||||
meta: { title: '成员管理' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -117,6 +132,11 @@ export const adminRoutes = [
|
|||
component: () => import('@/views/admin/user/index.vue'),
|
||||
meta: { title: '人员管理', roles: ['ADMIN', 'COLLEGE_ADMIN'] },
|
||||
children: [
|
||||
{
|
||||
path: 'list',
|
||||
component: () => import('@/views/admin/user/list.vue'),
|
||||
meta: { title: '用户列表' }
|
||||
},
|
||||
{
|
||||
path: 'password',
|
||||
component: () => import('@/views/profile/password.vue'),
|
||||
|
|
|
|||
|
|
@ -1,114 +1,64 @@
|
|||
<!-- 社团成员管理页面 -->
|
||||
<template>
|
||||
<div class="club-members">
|
||||
<!-- 社团信息卡片 -->
|
||||
<el-card class="club-card">
|
||||
<div class="club-info">
|
||||
<div class="club-name">{{ clubInfo.name }}</div>
|
||||
<div class="club-type">
|
||||
<el-tag :type="getClubTypeTag(clubInfo.type)">
|
||||
{{ getClubTypeLabel(clubInfo.type) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="club-leader">负责人:{{ clubInfo.leader }}</div>
|
||||
<div class="club-count">成员数量:{{ clubInfo.memberCount }}</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 成员列表卡片 -->
|
||||
<el-card class="member-card">
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>成员列表</span>
|
||||
<div class="header-operations">
|
||||
<el-button type="primary" :icon="Plus" @click="handleAddMember">添加成员</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
:disabled="!selectedMembers.length"
|
||||
@click="handleBatchRemove"
|
||||
>
|
||||
批量移除
|
||||
</el-button>
|
||||
</div>
|
||||
<span>成员管理</span>
|
||||
<el-button type="primary" @click="handleAdd">添加成员</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 搜索工具栏 -->
|
||||
<el-form :inline="true" :model="queryParams" class="search-form">
|
||||
<el-form-item label="姓名">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
|
||||
<el-form-item label="学号">
|
||||
<el-input
|
||||
v-model="queryParams.studentId"
|
||||
placeholder="请输入学号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.schoolId" placeholder="请输入学号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="queryParams.role" placeholder="请选择角色" clearable>
|
||||
<el-option label="普通成员" value="member" />
|
||||
<el-option label="管理员" value="admin" />
|
||||
<el-option label="负责人" value="leader" />
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入姓名" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
||||
<el-option label="活跃" value="active" />
|
||||
<el-option label="非活跃" value="inactive" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
<el-button type="primary" @click="handleQuery">查询</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 成员列表 -->
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="memberList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="姓名" prop="name" width="120" />
|
||||
<el-table-column label="学号" prop="studentId" width="120" />
|
||||
<el-table-column label="性别" prop="gender" width="80">
|
||||
<template #default="{ row }">
|
||||
{{ row.gender === 'male' ? '男' : '女' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学院" prop="college" width="150" />
|
||||
<el-table-column label="专业" prop="major" width="150" />
|
||||
<el-table-column label="角色" prop="role" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getRoleType(row.role)">
|
||||
{{ getRoleLabel(row.role) }}
|
||||
<!-- 表格 -->
|
||||
<el-table :data="memberList" style="width: 100%" v-loading="loading">
|
||||
<el-table-column prop="schoolId" label="学号" />
|
||||
<el-table-column prop="name" label="姓名" />
|
||||
<el-table-column prop="joinDate" label="加入时间" />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
|
||||
{{ scope.row.status === 'active' ? '活跃' : '非活跃' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="加入时间" prop="joinTime" width="180" />
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-table-column label="操作" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:icon="Edit"
|
||||
@click="handleUpdateRole(row)"
|
||||
>修改角色</el-button>
|
||||
size="small"
|
||||
@click="handleEdit(scope.row)"
|
||||
>编辑</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
:icon="Delete"
|
||||
@click="handleRemove(row)"
|
||||
size="small"
|
||||
@click="handleRemove(scope.row)"
|
||||
>移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
|
|
@ -121,94 +71,31 @@
|
|||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 添加成员对话框 -->
|
||||
<!-- 添加/编辑成员对话框 -->
|
||||
<el-dialog
|
||||
v-model="addDialogVisible"
|
||||
title="添加成员"
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '添加成员' : '编辑成员'"
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
ref="addFormRef"
|
||||
:model="addForm"
|
||||
:rules="addFormRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="学号" prop="studentId">
|
||||
<el-input v-model="addForm.studentId" placeholder="请输入学号" />
|
||||
<el-form :model="memberForm" label-width="100px">
|
||||
<el-form-item label="学号" v-if="dialogType === 'add'">
|
||||
<el-input v-model="memberForm.schoolId" placeholder="请输入学号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色" prop="role">
|
||||
<el-select v-model="addForm.role" placeholder="请选择角色" style="width: 100%">
|
||||
<el-option label="普通成员" value="member" />
|
||||
<el-option label="管理员" value="admin" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="addLoading" @click="confirmAdd">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 修改角色对话框 -->
|
||||
<el-dialog
|
||||
v-model="roleDialogVisible"
|
||||
title="修改角色"
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
ref="roleFormRef"
|
||||
:model="roleForm"
|
||||
:rules="roleFormRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="姓名">
|
||||
<span>{{ roleForm.name }}</span>
|
||||
<el-input v-model="memberForm.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学号">
|
||||
<span>{{ roleForm.studentId }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色" prop="role">
|
||||
<el-select v-model="roleForm.role" placeholder="请选择角色" style="width: 100%">
|
||||
<el-option label="普通成员" value="member" />
|
||||
<el-option label="管理员" value="admin" />
|
||||
<el-option label="负责人" value="leader" />
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="memberForm.status" placeholder="请选择状态">
|
||||
<el-option label="活跃" value="active" />
|
||||
<el-option label="非活跃" value="inactive" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="roleDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="roleLoading" @click="confirmUpdateRole">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 批量移除确认框 -->
|
||||
<el-dialog
|
||||
v-model="removeDialogVisible"
|
||||
title="确认移除"
|
||||
width="400px"
|
||||
append-to-body
|
||||
>
|
||||
<div class="dialog-content">
|
||||
<el-icon class="warning-icon"><Warning /></el-icon>
|
||||
<span>确定要移除选中的 {{ selectedMembers.length }} 个成员吗?</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="removeDialogVisible = false">取消</el-button>
|
||||
<el-button type="danger" :loading="removeLoading" @click="confirmBatchRemove">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
|
@ -216,96 +103,38 @@
|
|||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
Search,
|
||||
Refresh,
|
||||
Plus,
|
||||
Delete,
|
||||
Edit,
|
||||
Warning
|
||||
} from '@element-plus/icons-vue'
|
||||
import {
|
||||
getClubDetail,
|
||||
getClubMembers,
|
||||
addClubMember,
|
||||
removeClubMember,
|
||||
updateClubMemberRole
|
||||
} from '@/api/club'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const loading = ref(false)
|
||||
const addLoading = ref(false)
|
||||
const roleLoading = ref(false)
|
||||
const removeLoading = ref(false)
|
||||
const clubInfo = ref({})
|
||||
const memberList = ref([])
|
||||
const total = ref(0)
|
||||
const selectedMembers = ref([])
|
||||
const addDialogVisible = ref(false)
|
||||
const roleDialogVisible = ref(false)
|
||||
const removeDialogVisible = ref(false)
|
||||
const addFormRef = ref(null)
|
||||
const roleFormRef = ref(null)
|
||||
import { getClubMembers, addClubMember, updateClubMember, removeClubMember } from '@/api/club'
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
schoolId: '',
|
||||
name: '',
|
||||
studentId: '',
|
||||
role: ''
|
||||
status: ''
|
||||
})
|
||||
|
||||
// 添加成员表单
|
||||
const addForm = reactive({
|
||||
studentId: '',
|
||||
role: 'member'
|
||||
})
|
||||
// 数据列表
|
||||
const memberList = ref([])
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
|
||||
// 修改角色表单
|
||||
const roleForm = reactive({
|
||||
memberId: '',
|
||||
// 对话框
|
||||
const dialogVisible = ref(false)
|
||||
const dialogType = ref('')
|
||||
const memberForm = reactive({
|
||||
schoolId: '',
|
||||
name: '',
|
||||
studentId: '',
|
||||
role: ''
|
||||
status: 'active'
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const addFormRules = {
|
||||
studentId: [
|
||||
{ required: true, message: '请输入学号', trigger: 'blur' },
|
||||
{ pattern: /^\d{8}$/, message: '请输入8位数字学号', trigger: 'blur' }
|
||||
],
|
||||
role: [
|
||||
{ required: true, message: '请选择角色', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
const roleFormRules = {
|
||||
role: [
|
||||
{ required: true, message: '请选择角色', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 获取社团详情
|
||||
const getDetail = async () => {
|
||||
try {
|
||||
const res = await getClubDetail(route.params.id)
|
||||
clubInfo.value = res.data
|
||||
} catch (error) {
|
||||
console.error('获取社团详情失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取成员列表
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getClubMembers(route.params.id, queryParams)
|
||||
memberList.value = res.data.list
|
||||
const res = await getClubMembers(queryParams)
|
||||
memberList.value = res.data.records
|
||||
total.value = res.data.total
|
||||
} catch (error) {
|
||||
console.error('获取成员列表失败:', error)
|
||||
|
|
@ -314,264 +143,101 @@ const getList = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// 搜索
|
||||
// 查询按钮操作
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置
|
||||
// 重置按钮操作
|
||||
const resetQuery = () => {
|
||||
queryParams.schoolId = ''
|
||||
queryParams.name = ''
|
||||
queryParams.studentId = ''
|
||||
queryParams.role = ''
|
||||
queryParams.status = ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 选择变化
|
||||
const handleSelectionChange = (selection) => {
|
||||
selectedMembers.value = selection
|
||||
}
|
||||
|
||||
// 添加成员
|
||||
const handleAddMember = () => {
|
||||
addForm.studentId = ''
|
||||
addForm.role = 'member'
|
||||
addDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认添加
|
||||
const confirmAdd = async () => {
|
||||
if (!addFormRef.value) return
|
||||
|
||||
await addFormRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
|
||||
addLoading.value = true
|
||||
try {
|
||||
await addClubMember(route.params.id, addForm)
|
||||
ElMessage.success('添加成功')
|
||||
addDialogVisible.value = false
|
||||
getList()
|
||||
} catch (error) {
|
||||
console.error('添加成员失败:', error)
|
||||
} finally {
|
||||
addLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 修改角色
|
||||
const handleUpdateRole = (row) => {
|
||||
roleForm.memberId = row.id
|
||||
roleForm.name = row.name
|
||||
roleForm.studentId = row.studentId
|
||||
roleForm.role = row.role
|
||||
roleDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认修改角色
|
||||
const confirmUpdateRole = async () => {
|
||||
if (!roleFormRef.value) return
|
||||
|
||||
await roleFormRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
|
||||
roleLoading.value = true
|
||||
try {
|
||||
await updateClubMemberRole(route.params.id, roleForm.memberId, {
|
||||
role: roleForm.role
|
||||
})
|
||||
ElMessage.success('修改成功')
|
||||
roleDialogVisible.value = false
|
||||
getList()
|
||||
} catch (error) {
|
||||
console.error('修改角色失败:', error)
|
||||
} finally {
|
||||
roleLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 移除成员
|
||||
const handleRemove = (row) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要移除成员"${row.name}"吗?`,
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(async () => {
|
||||
try {
|
||||
await removeClubMember(route.params.id, row.id)
|
||||
ElMessage.success('移除成功')
|
||||
getList()
|
||||
} catch (error) {
|
||||
console.error('移除成员失败:', error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 批量移除
|
||||
const handleBatchRemove = () => {
|
||||
removeDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认批量移除
|
||||
const confirmBatchRemove = async () => {
|
||||
if (!selectedMembers.value.length) return
|
||||
|
||||
removeLoading.value = true
|
||||
try {
|
||||
const memberIds = selectedMembers.value.map(item => item.id)
|
||||
await Promise.all(
|
||||
memberIds.map(id => removeClubMember(route.params.id, id))
|
||||
)
|
||||
ElMessage.success('移除成功')
|
||||
removeDialogVisible.value = false
|
||||
getList()
|
||||
} catch (error) {
|
||||
console.error('批量移除成员失败:', error)
|
||||
} finally {
|
||||
removeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 分页大小变化
|
||||
// 分页大小改变
|
||||
const handleSizeChange = (val) => {
|
||||
queryParams.pageSize = val
|
||||
getList()
|
||||
}
|
||||
|
||||
// 页码变化
|
||||
// 页码改变
|
||||
const handleCurrentChange = (val) => {
|
||||
queryParams.pageNum = val
|
||||
getList()
|
||||
}
|
||||
|
||||
// 获取社团类型标签
|
||||
const getClubTypeTag = (type) => {
|
||||
const map = {
|
||||
academic: 'success',
|
||||
sports: 'warning',
|
||||
art: 'info',
|
||||
charity: 'danger',
|
||||
other: ''
|
||||
}
|
||||
return map[type] || ''
|
||||
// 添加成员
|
||||
const handleAdd = () => {
|
||||
dialogType.value = 'add'
|
||||
memberForm.schoolId = ''
|
||||
memberForm.name = ''
|
||||
memberForm.status = 'active'
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 获取社团类型标签文本
|
||||
const getClubTypeLabel = (type) => {
|
||||
const map = {
|
||||
academic: '学术类',
|
||||
sports: '体育类',
|
||||
art: '艺术类',
|
||||
charity: '公益类',
|
||||
other: '其他'
|
||||
}
|
||||
return map[type] || '其他'
|
||||
// 编辑成员
|
||||
const handleEdit = (row) => {
|
||||
dialogType.value = 'edit'
|
||||
Object.assign(memberForm, row)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 获取角色标签类型
|
||||
const getRoleType = (role) => {
|
||||
const map = {
|
||||
leader: 'danger',
|
||||
admin: 'warning',
|
||||
member: 'info'
|
||||
}
|
||||
return map[role] || ''
|
||||
// 移除成员
|
||||
const handleRemove = (row) => {
|
||||
ElMessageBox.confirm('确认移除该成员吗?', '提示', {
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await removeClubMember(row.id)
|
||||
ElMessage.success('移除成功')
|
||||
getList()
|
||||
} catch (error) {
|
||||
console.error('移除成员失败:', error)
|
||||
ElMessage.error('移除失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取角色标签文本
|
||||
const getRoleLabel = (role) => {
|
||||
const map = {
|
||||
leader: '负责人',
|
||||
admin: '管理员',
|
||||
member: '普通成员'
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
if (dialogType.value === 'add') {
|
||||
await addClubMember(memberForm)
|
||||
ElMessage.success('添加成功')
|
||||
} else {
|
||||
await updateClubMember(memberForm)
|
||||
ElMessage.success('更新成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
getList()
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error)
|
||||
ElMessage.error('操作失败')
|
||||
}
|
||||
return map[role] || '未知'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDetail()
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.club-members {
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.club-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.club-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.club-name {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.club-type {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.club-leader,
|
||||
.club-count {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.member-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-operations {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
font-size: 24px;
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,366 +1,306 @@
|
|||
<!-- 个人中心页面 -->
|
||||
<template>
|
||||
<div class="profile">
|
||||
<!-- 个人信息卡片 -->
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<!-- 左侧个人信息卡片 -->
|
||||
<el-col :span="8">
|
||||
<el-card class="profile-card">
|
||||
<div class="profile-header">
|
||||
<el-avatar :size="100" :src="userInfo.avatar" />
|
||||
<h3>{{ userInfo.name }}</h3>
|
||||
<p>{{ userInfo.studentId }}</p>
|
||||
<h2 class="username">{{ userInfo.nickName }}</h2>
|
||||
<p class="school-id">学号:{{ userInfo.schoolId }}</p>
|
||||
</div>
|
||||
<div class="profile-info">
|
||||
<div class="info-item">
|
||||
<label>学院:</label>
|
||||
<span>{{ userInfo.college }}</span>
|
||||
<i class="el-icon-user"></i>
|
||||
<span>姓名:{{ userInfo.userName }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>专业:</label>
|
||||
<span>{{ userInfo.major }}</span>
|
||||
<i class="el-icon-college"></i>
|
||||
<span>学院:{{ userInfo.collegeName }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>年级:</label>
|
||||
<span>{{ userInfo.grade }}</span>
|
||||
<i class="el-icon-major"></i>
|
||||
<span>专业:{{ userInfo.major }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>手机:</label>
|
||||
<span>{{ userInfo.phone }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>邮箱:</label>
|
||||
<span>{{ userInfo.email }}</span>
|
||||
<i class="el-icon-email"></i>
|
||||
<span>邮箱:{{ userInfo.email }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-actions">
|
||||
<el-button type="primary" @click="handleEdit">编辑资料</el-button>
|
||||
<el-button type="primary" @click="handleEditProfile">编辑资料</el-button>
|
||||
<el-button @click="handleChangePassword">修改密码</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="18">
|
||||
<el-card>
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- 我的社团 -->
|
||||
<el-tab-pane label="我的社团" name="clubs">
|
||||
<div class="my-clubs">
|
||||
<el-empty v-if="myClubs.length === 0" description="暂无加入的社团" />
|
||||
<el-row v-else :gutter="20">
|
||||
<el-col :span="8" v-for="club in myClubs" :key="club.id">
|
||||
<el-card class="club-card" shadow="hover">
|
||||
<div class="club-cover">
|
||||
<el-image :src="club.cover" fit="cover">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon><Picture /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="club-info">
|
||||
<h4>{{ club.name }}</h4>
|
||||
<p class="club-role">{{ club.role }}</p>
|
||||
<p class="club-join-time">加入时间:{{ club.joinTime }}</p>
|
||||
<div class="club-actions">
|
||||
<el-button type="primary" link @click="viewClubDetail(club.id)">
|
||||
查看详情
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- 右侧活动记录 -->
|
||||
<el-col :span="16">
|
||||
<el-card class="activity-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>我的活动</span>
|
||||
<el-radio-group v-model="activityType" size="small">
|
||||
<el-radio-button label="all">全部</el-radio-button>
|
||||
<el-radio-button label="registered">已报名</el-radio-button>
|
||||
<el-radio-button label="attended">已参加</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 我的活动 -->
|
||||
<el-tab-pane label="我的活动" name="activities">
|
||||
<div class="my-activities">
|
||||
<el-empty v-if="myActivities.length === 0" description="暂无参与的活动" />
|
||||
<el-timeline v-else>
|
||||
<el-timeline-item
|
||||
v-for="activity in myActivities"
|
||||
:key="activity.id"
|
||||
:timestamp="activity.startTime"
|
||||
:type="getActivityType(activity.status)"
|
||||
>
|
||||
<el-card class="activity-card">
|
||||
<div class="activity-info">
|
||||
<h4>{{ activity.name }}</h4>
|
||||
<p>{{ activity.description }}</p>
|
||||
<div class="activity-meta">
|
||||
<span>
|
||||
<el-icon><Location /></el-icon>
|
||||
{{ activity.location }}
|
||||
</span>
|
||||
<el-tag size="small" :type="getActivityStatusType(activity.status)">
|
||||
{{ activity.status }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="activity-actions">
|
||||
<el-button type="primary" link @click="viewActivityDetail(activity.id)">
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
@click="cancelActivity(activity.id)"
|
||||
v-if="activity.status === '报名中'"
|
||||
>
|
||||
取消报名
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-table :data="activityList" style="width: 100%" v-loading="loading">
|
||||
<el-table-column prop="title" label="活动名称" />
|
||||
<el-table-column prop="clubName" label="主办社团" />
|
||||
<el-table-column prop="startTime" label="开始时间" />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getActivityStatusType(scope.row.status)">
|
||||
{{ getActivityStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleViewActivity(scope.row)"
|
||||
>查看</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === 'REGISTERED'"
|
||||
type="danger"
|
||||
link
|
||||
@click="handleCancelRegistration(scope.row)"
|
||||
>取消报名</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:page-sizes="[5, 10, 20, 50]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 编辑资料对话框 -->
|
||||
<el-dialog
|
||||
v-model="editDialog.visible"
|
||||
title="编辑个人资料"
|
||||
v-model="editDialogVisible"
|
||||
title="编辑资料"
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="80px">
|
||||
<el-form :model="editForm" label-width="100px">
|
||||
<el-form-item label="头像">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="/api/file/upload"
|
||||
action="/api/upload"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
>
|
||||
<el-avatar v-if="editForm.avatar" :src="editForm.avatar" :size="100" />
|
||||
<img v-if="editForm.avatar" :src="editForm.avatar" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="editForm.name" />
|
||||
<el-form-item label="昵称">
|
||||
<el-input v-model="editForm.nickName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phone">
|
||||
<el-input v-model="editForm.phone" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="editForm.email" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelEdit">取 消</el-button>
|
||||
<el-button type="primary" @click="submitEdit">确 定</el-button>
|
||||
</div>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitEditProfile">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 修改密码对话框 -->
|
||||
<change-password v-model:visible="passwordDialogVisible" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Picture, Location, Plus } from '@element-plus/icons-vue'
|
||||
import { getUserInfo, updateUserInfo } from '@/api/user'
|
||||
import { getMyClubs } from '@/api/club/club'
|
||||
import { getMyActivities, cancelActivitySignUp } from '@/api/activity/activity'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 当前激活的标签页
|
||||
const activeTab = ref('clubs')
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { getUserProfile, updateUserProfile, getUserActivities, cancelActivityRegistration } from '@/api/user'
|
||||
import ChangePassword from './components/ChangePassword.vue'
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref({
|
||||
id: null,
|
||||
name: '',
|
||||
studentId: '',
|
||||
college: '',
|
||||
major: '',
|
||||
grade: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
avatar: ''
|
||||
const userInfo = ref({})
|
||||
|
||||
// 活动列表
|
||||
const activityList = ref([])
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
const activityType = ref('all')
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
type: 'all'
|
||||
})
|
||||
|
||||
// 我的社团列表
|
||||
const myClubs = ref([])
|
||||
|
||||
// 我的活动列表
|
||||
const myActivities = ref([])
|
||||
|
||||
// 编辑对话框
|
||||
const editDialog = reactive({
|
||||
visible: false
|
||||
})
|
||||
|
||||
// 编辑表单
|
||||
const editDialogVisible = ref(false)
|
||||
const editForm = reactive({
|
||||
name: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
avatar: ''
|
||||
avatar: '',
|
||||
nickName: '',
|
||||
email: ''
|
||||
})
|
||||
|
||||
// 编辑表单校验规则
|
||||
const editRules = {
|
||||
name: [
|
||||
{ required: true, message: '请输入姓名', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号码', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
// 获取活动状态样式
|
||||
const getActivityStatusType = (status) => {
|
||||
const statusMap = {
|
||||
'未开始': 'info',
|
||||
'报名中': 'success',
|
||||
'进行中': 'warning',
|
||||
'已结束': 'info',
|
||||
'已取消': 'danger'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
}
|
||||
|
||||
// 获取活动时间线类型
|
||||
const getActivityType = (status) => {
|
||||
const typeMap = {
|
||||
'未开始': 'primary',
|
||||
'报名中': 'success',
|
||||
'进行中': 'warning',
|
||||
'已结束': 'info'
|
||||
}
|
||||
return typeMap[status] || 'info'
|
||||
}
|
||||
// 修改密码对话框
|
||||
const passwordDialogVisible = ref(false)
|
||||
|
||||
// 获取用户信息
|
||||
const fetchUserInfo = async () => {
|
||||
const getUserInfo = async () => {
|
||||
try {
|
||||
const response = await getUserInfo()
|
||||
userInfo.value = response.data
|
||||
const res = await getUserProfile()
|
||||
userInfo.value = res.data
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取我的社团列表
|
||||
const fetchMyClubs = async () => {
|
||||
// 获取活动列表
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await getMyClubs()
|
||||
myClubs.value = response.data
|
||||
const res = await getUserActivities(queryParams)
|
||||
activityList.value = res.data.records
|
||||
total.value = res.data.total
|
||||
} catch (error) {
|
||||
console.error('获取我的社团列表失败:', error)
|
||||
console.error('获取活动列表失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取我的活动列表
|
||||
const fetchMyActivities = async () => {
|
||||
// 分页大小改变
|
||||
const handleSizeChange = (val) => {
|
||||
queryParams.pageSize = val
|
||||
getList()
|
||||
}
|
||||
|
||||
// 页码改变
|
||||
const handleCurrentChange = (val) => {
|
||||
queryParams.pageNum = val
|
||||
getList()
|
||||
}
|
||||
|
||||
// 获取活动状态类型
|
||||
const getActivityStatusType = (status) => {
|
||||
const statusMap = {
|
||||
REGISTERED: 'warning',
|
||||
ATTENDED: 'success',
|
||||
CANCELLED: 'info'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
}
|
||||
|
||||
// 获取活动状态文本
|
||||
const getActivityStatusText = (status) => {
|
||||
const statusMap = {
|
||||
REGISTERED: '已报名',
|
||||
ATTENDED: '已参加',
|
||||
CANCELLED: '已取消'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
// 编辑资料
|
||||
const handleEditProfile = () => {
|
||||
Object.assign(editForm, userInfo.value)
|
||||
editDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 提交编辑资料
|
||||
const submitEditProfile = async () => {
|
||||
try {
|
||||
const response = await getMyActivities()
|
||||
myActivities.value = response.data
|
||||
await updateUserProfile(editForm)
|
||||
ElMessage.success('更新成功')
|
||||
editDialogVisible.value = false
|
||||
getUserInfo()
|
||||
} catch (error) {
|
||||
console.error('获取我的活动列表失败:', error)
|
||||
console.error('更新用户信息失败:', error)
|
||||
ElMessage.error('更新失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 查看社团详情
|
||||
const viewClubDetail = (clubId) => {
|
||||
router.push(`/club/detail/${clubId}`)
|
||||
// 修改密码
|
||||
const handleChangePassword = () => {
|
||||
passwordDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 查看活动详情
|
||||
const viewActivityDetail = (activityId) => {
|
||||
router.push(`/activity/detail/${activityId}`)
|
||||
// 查看活动
|
||||
const handleViewActivity = (row) => {
|
||||
// TODO: 实现查看活动详情功能
|
||||
}
|
||||
|
||||
// 取消活动报名
|
||||
const cancelActivity = async (activityId) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要取消报名该活动吗?', '提示', {
|
||||
type: 'warning'
|
||||
})
|
||||
await cancelActivitySignUp(activityId)
|
||||
ElMessage.success('取消报名成功')
|
||||
fetchMyActivities()
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
// 取消报名
|
||||
const handleCancelRegistration = (row) => {
|
||||
ElMessageBox.confirm('确认取消报名该活动吗?', '提示', {
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await cancelActivityRegistration(row.id)
|
||||
ElMessage.success('取消报名成功')
|
||||
getList()
|
||||
} catch (error) {
|
||||
console.error('取消报名失败:', error)
|
||||
ElMessage.error('取消报名失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理编辑
|
||||
const handleEdit = () => {
|
||||
editDialog.visible = true
|
||||
Object.assign(editForm, {
|
||||
name: userInfo.value.name,
|
||||
phone: userInfo.value.phone,
|
||||
email: userInfo.value.email,
|
||||
avatar: userInfo.value.avatar
|
||||
})
|
||||
}
|
||||
|
||||
// 头像上传成功
|
||||
const handleAvatarSuccess = (response) => {
|
||||
editForm.avatar = response.data
|
||||
const handleAvatarSuccess = (res) => {
|
||||
editForm.avatar = res.data
|
||||
}
|
||||
|
||||
// 头像上传前校验
|
||||
// 头像上传前
|
||||
const beforeAvatarUpload = (file) => {
|
||||
const isJPG = file.type === 'image/jpeg'
|
||||
const isPNG = file.type === 'image/png'
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
|
||||
if (!isJPG && !isPNG) {
|
||||
ElMessage.error('头像只能是 JPG 或 PNG 格式!')
|
||||
return false
|
||||
if (!isJPG) {
|
||||
ElMessage.error('上传头像图片只能是 JPG 格式!')
|
||||
}
|
||||
if (!isLt2M) {
|
||||
ElMessage.error('头像大小不能超过 2MB!')
|
||||
return false
|
||||
ElMessage.error('上传头像图片大小不能超过 2MB!')
|
||||
}
|
||||
return true
|
||||
return isJPG && isLt2M
|
||||
}
|
||||
|
||||
// 提交编辑
|
||||
const submitEdit = async () => {
|
||||
try {
|
||||
await updateUserInfo(editForm)
|
||||
ElMessage.success('更新成功')
|
||||
editDialog.visible = false
|
||||
fetchUserInfo()
|
||||
} catch (error) {
|
||||
console.error('更新失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const cancelEdit = () => {
|
||||
editDialog.visible = false
|
||||
}
|
||||
// 监听活动类型变化
|
||||
watch(activityType, (val) => {
|
||||
queryParams.type = val
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchUserInfo()
|
||||
fetchMyClubs()
|
||||
fetchMyActivities()
|
||||
getUserInfo()
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.profile {
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
|
|
@ -369,126 +309,82 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.profile-header {
|
||||
padding: 20px 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.profile-header h3 {
|
||||
margin: 10px 0 5px;
|
||||
font-size: 18px;
|
||||
color: #303133;
|
||||
.username {
|
||||
margin: 10px 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.profile-header p {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
.school-id {
|
||||
color: #666;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.profile-info {
|
||||
text-align: left;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.info-item label {
|
||||
color: #909399;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.info-item span {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.profile-actions {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
|
||||
.club-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.club-cover {
|
||||
height: 150px;
|
||||
overflow: hidden;
|
||||
.info-item {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.club-cover .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.info-item i {
|
||||
margin-right: 10px;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.club-info {
|
||||
padding: 15px;
|
||||
.profile-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.club-info h4 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 16px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.club-role {
|
||||
margin: 0 0 5px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.club-join-time {
|
||||
margin: 0 0 10px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.activity-card {
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.activity-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.activity-info h4 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 16px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.activity-info p {
|
||||
margin: 0 0 10px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.activity-meta {
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar-uploader .avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
text-align: center;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon:hover {
|
||||
border-color: #409eff;
|
||||
line-height: 100px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.bruce.sams.controller.ams;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bruce.sams.domain.ams.Activity;
|
||||
import com.bruce.sams.domain.entity.AjaxResult;
|
||||
import com.bruce.sams.service.ActivityService;
|
||||
|
|
@ -18,6 +19,54 @@ public class ActivityController {
|
|||
@Autowired
|
||||
private ActivityService activityService;
|
||||
|
||||
/**
|
||||
* 获取活动列表(分页)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public AjaxResult listActivities(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String title,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) Long collegeId,
|
||||
@RequestParam(required = false) Long clubId) {
|
||||
|
||||
Page<Activity> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<Activity> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 添加查询条件
|
||||
if (title != null && !title.isEmpty()) {
|
||||
queryWrapper.like(Activity::getTitle, title);
|
||||
}
|
||||
if (status != null && !status.isEmpty()) {
|
||||
queryWrapper.eq(Activity::getStatus, status);
|
||||
}
|
||||
if (collegeId != null) {
|
||||
queryWrapper.eq(Activity::getCollegeId, collegeId);
|
||||
}
|
||||
if (clubId != null) {
|
||||
queryWrapper.eq(Activity::getClubId, clubId);
|
||||
}
|
||||
|
||||
// 按创建时间降序排序
|
||||
queryWrapper.orderByDesc(Activity::getCreatedAt);
|
||||
|
||||
Page<Activity> result = activityService.page(page, queryWrapper);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动详情
|
||||
*/
|
||||
@GetMapping("/{actId}")
|
||||
public AjaxResult getActivityDetail(@PathVariable Long actId) {
|
||||
Activity activity = activityService.getActivityById(actId);
|
||||
if (activity == null) {
|
||||
return AjaxResult.error("活动不存在");
|
||||
}
|
||||
return AjaxResult.success(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建活动
|
||||
*/
|
||||
|
|
@ -28,6 +77,16 @@ public class ActivityController {
|
|||
return AjaxResult.success("活动创建成功,等待审批");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新活动信息
|
||||
*/
|
||||
@PreAuthorize("hasRole('CLUB_ADMIN')")
|
||||
@PutMapping("/update")
|
||||
public AjaxResult updateActivity(@RequestBody Activity activity) {
|
||||
activityService.updateActivity(activity);
|
||||
return AjaxResult.success("活动信息已更新");
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消活动
|
||||
*/
|
||||
|
|
@ -37,4 +96,12 @@ public class ActivityController {
|
|||
activityService.cancelActivity(actId);
|
||||
return AjaxResult.success("活动已取消");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动统计数据
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public AjaxResult getActivityStats() {
|
||||
return AjaxResult.success(activityService.getActivityStats());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.bruce.sams.controller.sms;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bruce.sams.domain.entity.AjaxResult;
|
||||
import com.bruce.sams.domain.sms.Club;
|
||||
import com.bruce.sams.service.ClubService;
|
||||
|
|
@ -8,7 +10,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 社团管理控制器(按管理员级别分类)
|
||||
* 社团管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/club")
|
||||
|
|
@ -18,50 +20,85 @@ public class ClubController {
|
|||
private ClubService clubService;
|
||||
|
||||
/**
|
||||
* 校级 & 院级管理员:添加社团
|
||||
* 获取社团列表(分页)
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('COLLEGE_ADMIN') or hasRole('DEPARTMENT_ADMIN')")
|
||||
@PostMapping("/add")
|
||||
public AjaxResult addClub(@RequestBody Club club) {
|
||||
clubService.addClub(club);
|
||||
return AjaxResult.success("社团添加成功");
|
||||
@GetMapping("/list")
|
||||
public AjaxResult listClubs(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String clubName,
|
||||
@RequestParam(required = false) String category,
|
||||
@RequestParam(required = false) Long collegeId) {
|
||||
|
||||
Page<Club> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<Club> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 添加查询条件
|
||||
if (clubName != null && !clubName.isEmpty()) {
|
||||
queryWrapper.like(Club::getClubName, clubName);
|
||||
}
|
||||
if (category != null && !category.isEmpty()) {
|
||||
queryWrapper.eq(Club::getCategory, category);
|
||||
}
|
||||
if (collegeId != null) {
|
||||
queryWrapper.eq(Club::getCollegeId, collegeId);
|
||||
}
|
||||
|
||||
// 按创建时间降序排序
|
||||
queryWrapper.orderByDesc(Club::getCreatedAt);
|
||||
|
||||
Page<Club> result = clubService.page(page, queryWrapper);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 社团管理员:修改社团信息
|
||||
* 获取社团详情
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('CLUB_ADMIN')")
|
||||
@GetMapping("/{clubId}")
|
||||
public AjaxResult getClubDetail(@PathVariable Long clubId) {
|
||||
Club club = clubService.getClubById(clubId);
|
||||
if (club == null) {
|
||||
return AjaxResult.error("社团不存在");
|
||||
}
|
||||
return AjaxResult.success(club);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建社团
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('COLLEGE_ADMIN')")
|
||||
@PostMapping("/create")
|
||||
public AjaxResult createClub(@RequestBody Club club) {
|
||||
clubService.createClub(club);
|
||||
return AjaxResult.success("社团创建成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新社团信息
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('COLLEGE_ADMIN')")
|
||||
@PutMapping("/update")
|
||||
public AjaxResult updateClub(@RequestBody Club club) {
|
||||
clubService.updateClub(club);
|
||||
return AjaxResult.success("社团信息更新成功");
|
||||
return AjaxResult.success("社团信息已更新");
|
||||
}
|
||||
|
||||
/**
|
||||
* 校级管理员:删除社团
|
||||
* 获取社团成员列表
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('COLLEGE_ADMIN') or hasRole('DEPARTMENT_ADMIN')")
|
||||
@DeleteMapping("/delete/{clubId}")
|
||||
public AjaxResult deleteClub(@PathVariable Long clubId) {
|
||||
clubService.deleteClub(clubId);
|
||||
return AjaxResult.success("社团删除成功");
|
||||
@GetMapping("/{clubId}/members")
|
||||
public AjaxResult getClubMembers(
|
||||
@PathVariable Long clubId,
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
return AjaxResult.success(clubService.getClubMembers(clubId, pageNum, pageSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团详情(所有管理员)
|
||||
* 获取社团统计数据
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('COLLEGE_ADMIN') or hasRole('DEPARTMENT_ADMIN') or hasRole('CLUB_ADMIN')")
|
||||
@GetMapping("/{clubId}")
|
||||
public AjaxResult getClubById(@PathVariable Long clubId) {
|
||||
return AjaxResult.success(clubService.getClubById(clubId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询社团列表(权限控制)
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('COLLEGE_ADMIN') or hasRole('DEPARTMENT_ADMIN') or hasRole('CLUB_ADMIN')")
|
||||
@GetMapping("/list")
|
||||
public AjaxResult listClubs(@RequestParam(required = false) String keyword, @RequestParam Long userId) {
|
||||
return AjaxResult.success(clubService.listClubs(keyword, userId));
|
||||
@GetMapping("/stats")
|
||||
public AjaxResult getClubStats() {
|
||||
return AjaxResult.success(clubService.getClubStats());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.bruce.sams.controller.sys;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bruce.sams.common.utils.FileUploadUtil;
|
||||
import com.bruce.sams.domain.entity.AjaxResult;
|
||||
import com.bruce.sams.domain.sys.User;
|
||||
|
|
@ -7,20 +9,130 @@ import com.bruce.sams.service.UserService;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
||||
/**
|
||||
* 用户端 - 个人信息管理控制器
|
||||
* 用户管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
@RequestMapping("/api/admin/user")
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* 获取用户列表(分页)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public AjaxResult listUsers(
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String userName,
|
||||
@RequestParam(required = false) String schoolId,
|
||||
@RequestParam(required = false) Long collegeId) {
|
||||
|
||||
Page<User> page = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 添加查询条件
|
||||
if (userName != null && !userName.isEmpty()) {
|
||||
queryWrapper.like(User::getUserName, userName);
|
||||
}
|
||||
if (schoolId != null && !schoolId.isEmpty()) {
|
||||
queryWrapper.eq(User::getSchoolId, schoolId);
|
||||
}
|
||||
if (collegeId != null) {
|
||||
queryWrapper.eq(User::getCollegeId, collegeId);
|
||||
}
|
||||
|
||||
// 按创建时间降序排序
|
||||
queryWrapper.orderByDesc(User::getCreatedAt);
|
||||
|
||||
Page<User> result = userService.page(page, queryWrapper);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
*/
|
||||
@GetMapping("/{userId}")
|
||||
public AjaxResult getUserDetail(@PathVariable Long userId) {
|
||||
User user = userService.getUserById(userId);
|
||||
if (user == null) {
|
||||
return AjaxResult.error("用户不存在");
|
||||
}
|
||||
return AjaxResult.success(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PutMapping("/update")
|
||||
public AjaxResult updateUser(@RequestBody User user) {
|
||||
userService.updateUser(user);
|
||||
return AjaxResult.success("用户信息已更新");
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
@PutMapping("/password")
|
||||
public AjaxResult updatePassword(
|
||||
@RequestParam Long userId,
|
||||
@RequestParam String oldPassword,
|
||||
@RequestParam String newPassword) {
|
||||
|
||||
User user = userService.getUserById(userId);
|
||||
if (user == null) {
|
||||
return AjaxResult.error("用户不存在");
|
||||
}
|
||||
|
||||
// 验证旧密码
|
||||
if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
|
||||
return AjaxResult.error("原密码错误");
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
user.setPassword(passwordEncoder.encode(newPassword));
|
||||
userService.updateUser(user);
|
||||
|
||||
return AjaxResult.success("密码修改成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PutMapping("/reset-password/{userId}")
|
||||
public AjaxResult resetPassword(@PathVariable Long userId) {
|
||||
User user = userService.getUserById(userId);
|
||||
if (user == null) {
|
||||
return AjaxResult.error("用户不存在");
|
||||
}
|
||||
|
||||
// 重置为默认密码
|
||||
user.setPassword(passwordEncoder.encode("123456"));
|
||||
userService.updateUser(user);
|
||||
|
||||
return AjaxResult.success("密码已重置为默认密码");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户统计数据
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public AjaxResult getUserStats() {
|
||||
return AjaxResult.success(userService.getUserStats());
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户修改密码
|
||||
*
|
||||
|
|
@ -86,5 +198,4 @@ public class UserController {
|
|||
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
package com.bruce.sams.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bruce.sams.domain.sms.ClubUser;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author bruce
|
||||
|
|
@ -13,6 +18,20 @@ import org.apache.ibatis.annotations.Mapper;
|
|||
@Mapper
|
||||
public interface ClubUserMapper extends BaseMapper<ClubUser> {
|
||||
|
||||
/**
|
||||
* 查询社团成员列表
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param clubId 社团ID
|
||||
* @return 成员列表
|
||||
*/
|
||||
@Select("SELECT u.user_id, u.user_name, u.nick_name, u.avatar, u.email, " +
|
||||
"cu.join_date, cu.is_active " +
|
||||
"FROM sys_user u " +
|
||||
"JOIN sms_club_user cu ON u.user_id = cu.user_id " +
|
||||
"WHERE cu.club_id = #{clubId} " +
|
||||
"ORDER BY cu.join_date DESC")
|
||||
Page<Map<String, Object>> selectClubMembers(Page<Map<String, Object>> page, @Param("clubId") Long clubId);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package com.bruce.sams.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bruce.sams.domain.sys.User;
|
||||
import com.bruce.sams.entity.User;
|
||||
import com.bruce.sams.vo.ActivityVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
|
@ -43,6 +46,21 @@ public interface UserMapper extends BaseMapper<User> {
|
|||
|
||||
@Update("UPDATE sys_user SET avatar = #{avatarUrl} WHERE user_id = #{userId}")
|
||||
void updateAvatar(Long userId, String avatarUrl);
|
||||
|
||||
/**
|
||||
* 查询用户参与的活动列表
|
||||
*/
|
||||
Page<ActivityVO> selectUserActivities(Page<ActivityVO> page, @Param("userId") Long userId, @Param("type") String type);
|
||||
|
||||
/**
|
||||
* 检查用户是否已报名活动
|
||||
*/
|
||||
boolean checkActivityRegistration(@Param("userId") Long userId, @Param("activityId") Long activityId);
|
||||
|
||||
/**
|
||||
* 取消活动报名
|
||||
*/
|
||||
void cancelActivityRegistration(@Param("userId") Long userId, @Param("activityId") Long activityId);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.bruce.sams.service;
|
|||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bruce.sams.domain.ams.Activity;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author bruce
|
||||
* @description 针对表【ams_activity(活动表)】的数据库操作Service
|
||||
|
|
@ -36,4 +38,11 @@ public interface ActivityService extends IService<Activity> {
|
|||
* @return 活动对象
|
||||
*/
|
||||
Activity getActivityById(Long actId);
|
||||
|
||||
/**
|
||||
* 获取活动统计数据
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Long> getActivityStats();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package com.bruce.sams.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bruce.sams.domain.sms.Club;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* * 社团管理服务接口
|
||||
|
|
@ -23,4 +25,28 @@ public interface ClubService extends IService<Club> {
|
|||
Club getClubById(Long clubId);
|
||||
|
||||
List<Club> listClubs(String keyword, Long userId);
|
||||
|
||||
/**
|
||||
* 创建社团
|
||||
*
|
||||
* @param club 社团信息
|
||||
*/
|
||||
void createClub(Club club);
|
||||
|
||||
/**
|
||||
* 获取社团成员列表
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页大小
|
||||
* @return 成员列表
|
||||
*/
|
||||
Page<Map<String, Object>> getClubMembers(Long clubId, Integer pageNum, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 获取社团统计数据
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Long> getClubStats();
|
||||
}
|
||||
|
|
@ -1,11 +1,18 @@
|
|||
package com.bruce.sams.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bruce.sams.domain.sys.Role;
|
||||
import com.bruce.sams.domain.sys.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface UserService {
|
||||
/**
|
||||
* 用户管理服务接口
|
||||
*/
|
||||
public interface UserService extends IService<User> {
|
||||
|
||||
/**
|
||||
* 批量导入用户
|
||||
|
|
@ -91,4 +98,18 @@ public interface UserService {
|
|||
*/
|
||||
void updateUserAvatar(Long userId, String avatarUrl);
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户信息
|
||||
*/
|
||||
User getUserById(Long userId);
|
||||
|
||||
/**
|
||||
* 获取用户统计数据
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Long> getUserStats();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.bruce.sams.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.sams.common.enums.ActivityStatus;
|
||||
import com.bruce.sams.domain.ams.Activity;
|
||||
|
|
@ -7,6 +8,9 @@ import com.bruce.sams.mapper.ActivityMapper;
|
|||
import com.bruce.sams.service.ActivityService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 活动管理服务实现类
|
||||
* 负责活动的创建、修改、取消,以及初始化审批流程
|
||||
|
|
@ -71,4 +75,31 @@ public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> i
|
|||
public Activity getActivityById(Long actId) {
|
||||
return this.getById(actId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动统计数据
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Long> getActivityStats() {
|
||||
Map<String, Long> stats = new HashMap<>();
|
||||
|
||||
// 获取各种状态的活动数量
|
||||
stats.put("total", this.count());
|
||||
stats.put("pending", this.count(new LambdaQueryWrapper<Activity>()
|
||||
.eq(Activity::getStatus, ActivityStatus.PENDING_CLUB)
|
||||
.or()
|
||||
.eq(Activity::getStatus, ActivityStatus.PENDING_DEPARTMENT)
|
||||
.or()
|
||||
.eq(Activity::getStatus, ActivityStatus.PENDING_COLLEGE)));
|
||||
stats.put("ongoing", this.count(new LambdaQueryWrapper<Activity>()
|
||||
.eq(Activity::getStatus, ActivityStatus.ONGOING)));
|
||||
stats.put("completed", this.count(new LambdaQueryWrapper<Activity>()
|
||||
.eq(Activity::getStatus, ActivityStatus.COMPLETED)));
|
||||
stats.put("cancelled", this.count(new LambdaQueryWrapper<Activity>()
|
||||
.eq(Activity::getStatus, ActivityStatus.CANCELLED)));
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
package com.bruce.sams.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.sams.domain.sms.Club;
|
||||
import com.bruce.sams.domain.sms.ClubUser;
|
||||
import com.bruce.sams.domain.sys.Role;
|
||||
import com.bruce.sams.domain.sys.User;
|
||||
import com.bruce.sams.mapper.ClubMapper;
|
||||
import com.bruce.sams.mapper.ClubUserMapper;
|
||||
import com.bruce.sams.mapper.UserMapper;
|
||||
import com.bruce.sams.service.ClubService;
|
||||
import com.bruce.sams.service.UserService;
|
||||
|
|
@ -13,7 +16,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 社团管理服务实现
|
||||
|
|
@ -32,6 +37,9 @@ public class ClubServiceImpl extends ServiceImpl<ClubMapper, Club>
|
|||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private ClubUserMapper clubUserMapper;
|
||||
|
||||
/**
|
||||
* 添加社团(校级或院级管理员)
|
||||
*/
|
||||
|
|
@ -122,6 +130,55 @@ public class ClubServiceImpl extends ServiceImpl<ClubMapper, Club>
|
|||
query.eq(Club::getLeaderId, userId); // 仅查询自己负责的社团
|
||||
return this.list(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建社团
|
||||
*
|
||||
* @param club 社团信息
|
||||
*/
|
||||
@Override
|
||||
public void createClub(Club club) {
|
||||
this.save(club);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团成员列表
|
||||
*
|
||||
* @param clubId 社团ID
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页大小
|
||||
* @return 成员列表
|
||||
*/
|
||||
@Override
|
||||
public Page<Map<String, Object>> getClubMembers(Long clubId, Integer pageNum, Integer pageSize) {
|
||||
Page<Map<String, Object>> page = new Page<>(pageNum, pageSize);
|
||||
return clubUserMapper.selectClubMembers(page, clubId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取社团统计数据
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Long> getClubStats() {
|
||||
Map<String, Long> stats = new HashMap<>();
|
||||
|
||||
// 获取社团总数
|
||||
stats.put("total", this.count());
|
||||
|
||||
// 获取各类别社团数量
|
||||
stats.put("academic", this.count(new LambdaQueryWrapper<Club>()
|
||||
.eq(Club::getCategory, "学术科技")));
|
||||
stats.put("art", this.count(new LambdaQueryWrapper<Club>()
|
||||
.eq(Club::getCategory, "文化艺术")));
|
||||
stats.put("public", this.count(new LambdaQueryWrapper<Club>()
|
||||
.eq(Club::getCategory, "社会公益")));
|
||||
stats.put("other", this.count(new LambdaQueryWrapper<Club>()
|
||||
.eq(Club::getCategory, "其他")));
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.bruce.sams.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.sams.common.enums.UserStatus;
|
||||
import com.bruce.sams.common.exception.PasswordIncorrectException;
|
||||
|
|
@ -12,10 +13,15 @@ import com.bruce.sams.mapper.RoleMapper;
|
|||
import com.bruce.sams.mapper.UserMapper;
|
||||
import com.bruce.sams.mapper.UserRoleMapper;
|
||||
import com.bruce.sams.service.UserService;
|
||||
import com.bruce.sams.vo.ActivityVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
|
||||
|
|
@ -30,6 +36,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
|
|||
@Autowired
|
||||
private RoleMapper roleMapper;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* 批量导入用户(管理端)
|
||||
*
|
||||
|
|
@ -207,4 +216,72 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
|
|||
userMapper.updateAvatar(userId, avatarUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户信息
|
||||
*/
|
||||
@Override
|
||||
public User getUserById(Long userId) {
|
||||
return this.getById(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户统计数据
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Long> getUserStats() {
|
||||
Map<String, Long> stats = new HashMap<>();
|
||||
|
||||
// 获取用户总数
|
||||
stats.put("total", this.count());
|
||||
|
||||
// 获取各状态用户数量
|
||||
stats.put("active", this.count(new LambdaQueryWrapper<User>()
|
||||
.eq(User::getStatus, "active")));
|
||||
stats.put("inactive", this.count(new LambdaQueryWrapper<User>()
|
||||
.eq(User::getStatus, "inactive")));
|
||||
stats.put("banned", this.count(new LambdaQueryWrapper<User>()
|
||||
.eq(User::getStatus, "banned")));
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ActivityVO> getUserActivities(Long userId, Integer pageNum, Integer pageSize, String type) {
|
||||
Page<ActivityVO> page = new Page<>(pageNum, pageSize);
|
||||
return userMapper.selectUserActivities(page, userId, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelActivityRegistration(Long userId, Long activityId) {
|
||||
// 检查用户是否已报名该活动
|
||||
if (!userMapper.checkActivityRegistration(userId, activityId)) {
|
||||
throw new RuntimeException("您未报名该活动");
|
||||
}
|
||||
// 取消报名
|
||||
userMapper.cancelActivityRegistration(userId, activityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updatePassword(Long userId, String oldPassword, String newPassword) {
|
||||
// 获取用户信息
|
||||
User user = getUserById(userId);
|
||||
if (user == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
// 验证原密码
|
||||
if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
|
||||
throw new RuntimeException("原密码错误");
|
||||
}
|
||||
// 更新密码
|
||||
user.setPassword(passwordEncoder.encode(newPassword));
|
||||
userMapper.updateById(user);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ server:
|
|||
spring:
|
||||
application:
|
||||
name: SAMS
|
||||
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/SAMS
|
||||
|
|
|
|||
Loading…
Reference in New Issue