|
|
@ -1,7 +1,6 @@ |
|
|
<template> |
|
|
<template> |
|
|
<div class="p-2"> |
|
|
<div class="p-2"> |
|
|
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" |
|
|
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> |
|
|
:leave-active-class="proxy?.animate.searchAnimate.leave"> |
|
|
|
|
|
<div v-show="showSearch" class="mb-[10px]"> |
|
|
<div v-show="showSearch" class="mb-[10px]"> |
|
|
<el-card shadow="hover"> |
|
|
<el-card shadow="hover"> |
|
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> |
|
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> |
|
|
@ -11,11 +10,20 @@ |
|
|
<el-form-item label="用户昵称" prop="nickName"> |
|
|
<el-form-item label="用户昵称" prop="nickName"> |
|
|
<el-input v-model="queryParams.nickName" placeholder="请输入用户昵称" clearable @keyup.enter="handleQuery" /> |
|
|
<el-input v-model="queryParams.nickName" placeholder="请输入用户昵称" clearable @keyup.enter="handleQuery" /> |
|
|
</el-form-item> |
|
|
</el-form-item> |
|
|
|
|
|
<el-form-item label="标签" prop="labelIds"> |
|
|
|
|
|
<el-select v-model="queryParams.labelIds" multiple collapse-tags collapse-tags-tooltip placeholder="请选择标签" style="width: 200px"> |
|
|
|
|
|
<el-option v-for="item in labelOptions" :key="item.id" :label="item.labelName" :value="item.id" /> |
|
|
|
|
|
</el-select> |
|
|
|
|
|
</el-form-item> |
|
|
|
|
|
<el-form-item label="群组" prop="groupIds"> |
|
|
|
|
|
<el-select v-model="queryParams.groupIds" multiple collapse-tags collapse-tags-tooltip placeholder="请选择群组" style="width: 200px"> |
|
|
|
|
|
<el-option v-for="item in groupOptions" :key="item.id" :label="item.groupName" :value="item.id" /> |
|
|
|
|
|
</el-select> |
|
|
|
|
|
</el-form-item> |
|
|
<el-form-item> |
|
|
<el-form-item> |
|
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> |
|
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> |
|
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button> |
|
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button> |
|
|
<el-button type="warning" plain icon="Download" @click="handleExport" |
|
|
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['im:user:export']">导出</el-button> |
|
|
v-hasPermi="['im:user:export']">导出</el-button> |
|
|
|
|
|
</el-form-item> |
|
|
</el-form-item> |
|
|
</el-form> |
|
|
</el-form> |
|
|
</el-card> |
|
|
</el-card> |
|
|
@ -36,6 +44,31 @@ |
|
|
<dict-tag :options="sys_user_sex" :value="scope.row.sex" /> |
|
|
<dict-tag :options="sys_user_sex" :value="scope.row.sex" /> |
|
|
</template> |
|
|
</template> |
|
|
</el-table-column> |
|
|
</el-table-column> |
|
|
|
|
|
<el-table-column label="标签" align="center" prop="labelIds" width="200"> |
|
|
|
|
|
<template #default="scope"> |
|
|
|
|
|
<el-tag |
|
|
|
|
|
v-for="labelId in (scope.row.labelIds || '').split(',').filter(id => id)" |
|
|
|
|
|
:key="labelId" |
|
|
|
|
|
size="small" |
|
|
|
|
|
style="margin-right: 5px; margin-bottom: 5px;" |
|
|
|
|
|
> |
|
|
|
|
|
{{ getLabelNameById(labelId) }} |
|
|
|
|
|
</el-tag> |
|
|
|
|
|
</template> |
|
|
|
|
|
</el-table-column> |
|
|
|
|
|
<el-table-column label="群组" align="center" prop="groupIds" width="200"> |
|
|
|
|
|
<template #default="scope"> |
|
|
|
|
|
<el-tag |
|
|
|
|
|
v-for="groupId in (scope.row.groupIds || '').split(',').filter(id => id)" |
|
|
|
|
|
:key="groupId" |
|
|
|
|
|
type="info" |
|
|
|
|
|
size="small" |
|
|
|
|
|
style="margin-right: 5px; margin-bottom: 5px;" |
|
|
|
|
|
> |
|
|
|
|
|
{{ getGroupNameById(groupId) }} |
|
|
|
|
|
</el-tag> |
|
|
|
|
|
</template> |
|
|
|
|
|
</el-table-column> |
|
|
<el-table-column label="是否被封禁" align="center" prop="isBanned"> |
|
|
<el-table-column label="是否被封禁" align="center" prop="isBanned"> |
|
|
<template #default="scope"> |
|
|
<template #default="scope"> |
|
|
<dict-tag :options="im_bool" :value="scope.row.isBanned" /> |
|
|
<dict-tag :options="im_bool" :value="scope.row.isBanned" /> |
|
|
@ -53,25 +86,20 @@ |
|
|
</el-table-column> |
|
|
</el-table-column> |
|
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|
|
<template #default="scope"> |
|
|
<template #default="scope"> |
|
|
<el-button link type="primary" v-hasPermi="['im:user:query']" |
|
|
<el-button link type="primary" v-hasPermi="['im:user:query']" @click="handleDetail(scope.row)">详情</el-button> |
|
|
@click="handleDetail(scope.row)">详情</el-button> |
|
|
<el-button v-if="scope.row.isBanned" link type="danger" v-hasPermi="['im:user:ban']" @click="unbanHandle(scope.row)">解封</el-button> |
|
|
<el-button v-if="scope.row.isBanned" link type="danger" v-hasPermi="['im:user:ban']" |
|
|
<el-button v-else link type="danger" v-hasPermi="['im:user:ban']" @click="banHandle(scope.row)">封禁</el-button> |
|
|
@click="unbanHandle(scope.row)">解封</el-button> |
|
|
|
|
|
<el-button v-else link type="danger" v-hasPermi="['im:user:ban']" |
|
|
|
|
|
@click="banHandle(scope.row)">封禁</el-button> |
|
|
|
|
|
</template> |
|
|
</template> |
|
|
</el-table-column> |
|
|
</el-table-column> |
|
|
</el-table> |
|
|
</el-table> |
|
|
|
|
|
|
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" |
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> |
|
|
v-model:limit="queryParams.pageSize" @pagination="getList" /> |
|
|
|
|
|
</el-card> |
|
|
</el-card> |
|
|
<!-- 添加或修改用户对话框 --> |
|
|
<!-- 添加或修改用户对话框 --> |
|
|
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body> |
|
|
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body> |
|
|
<el-form ref="userFormRef" :model="form" :rules="rules" label-width="100px" disabled> |
|
|
<el-form ref="userFormRef" :model="form" :rules="rules" label-width="100px" disabled> |
|
|
<el-form-item label="用户头像" prop="headImage"> |
|
|
<el-form-item label="用户头像" prop="headImage"> |
|
|
<image-preview v-if="form.headImageThumb" :src="form.headImageThumb" :full-src="form.headImage" |
|
|
<image-preview v-if="form.headImageThumb" :src="form.headImageThumb" :full-src="form.headImage" :width="100" :height="100" /> |
|
|
:width="100" :height="100" /> |
|
|
|
|
|
</el-form-item> |
|
|
</el-form-item> |
|
|
<el-form-item label="用户名" prop="userName"> |
|
|
<el-form-item label="用户名" prop="userName"> |
|
|
<el-input v-model="form.userName" /> |
|
|
<el-input v-model="form.userName" /> |
|
|
@ -86,18 +114,37 @@ |
|
|
<el-input v-model="form.signature" /> |
|
|
<el-input v-model="form.signature" /> |
|
|
</el-form-item> |
|
|
</el-form-item> |
|
|
<el-form-item label="最后登录时间" prop="lastLoginTime"> |
|
|
<el-form-item label="最后登录时间" prop="lastLoginTime"> |
|
|
<el-date-picker clearable v-model="form.lastLoginTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"> |
|
|
<el-date-picker clearable v-model="form.lastLoginTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"> </el-date-picker> |
|
|
</el-date-picker> |
|
|
|
|
|
</el-form-item> |
|
|
</el-form-item> |
|
|
<el-form-item label="注册时间" prop="createdTime"> |
|
|
<el-form-item label="注册时间" prop="createdTime"> |
|
|
<el-date-picker clearable v-model="form.createdTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"> |
|
|
<el-date-picker clearable v-model="form.createdTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"> </el-date-picker> |
|
|
</el-date-picker> |
|
|
</el-form-item> |
|
|
|
|
|
<el-form-item label="标签" prop="labelIds"> |
|
|
|
|
|
<el-tag |
|
|
|
|
|
v-for="labelId in (form.labelIds || '').split(',').filter(id => id)" |
|
|
|
|
|
:key="labelId" |
|
|
|
|
|
size="small" |
|
|
|
|
|
style="margin-right: 5px; margin-bottom: 5px;" |
|
|
|
|
|
> |
|
|
|
|
|
{{ getLabelNameById(labelId) }} |
|
|
|
|
|
</el-tag> |
|
|
|
|
|
</el-form-item> |
|
|
|
|
|
<el-form-item label="群组" prop="groupIds"> |
|
|
|
|
|
<el-tag |
|
|
|
|
|
v-for="groupId in (form.groupIds || '').split(',').filter(id => id)" |
|
|
|
|
|
:key="groupId" |
|
|
|
|
|
type="info" |
|
|
|
|
|
size="small" |
|
|
|
|
|
style="margin-right: 5px; margin-bottom: 5px;" |
|
|
|
|
|
> |
|
|
|
|
|
{{ getGroupNameById(groupId) }} |
|
|
|
|
|
</el-tag> |
|
|
</el-form-item> |
|
|
</el-form-item> |
|
|
<el-form-item label="是否被封禁" prop="isBanned"> |
|
|
<el-form-item label="是否被封禁" prop="isBanned"> |
|
|
<dict-tag :options="im_bool" :value="form.isBanned" /> |
|
|
<dict-tag :options="im_bool" :value="form.isBanned" /> |
|
|
</el-form-item> |
|
|
</el-form-item> |
|
|
<el-form-item v-if="form.isBanned" label="被封禁原因" prop="reason"> |
|
|
<el-form-item v-if="form.isBanned" label="被封禁原因" prop="reason"> |
|
|
<el-input v-model="form.reason"/> |
|
|
<el-input v-model="form.reason" /> |
|
|
</el-form-item> |
|
|
</el-form-item> |
|
|
</el-form> |
|
|
</el-form> |
|
|
<template #footer> |
|
|
<template #footer> |
|
|
@ -110,7 +157,7 @@ |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<script setup name="User" lang="ts"> |
|
|
<script setup name="User" lang="ts"> |
|
|
import { listUser, getUser, ban, unban } from '@/api/im/user'; |
|
|
import { listUser, getUser, ban, unban, getLabelList, getGroupList } from '@/api/im/user'; |
|
|
import { UserVO, UserQuery, UserForm } from '@/api/im/user/types'; |
|
|
import { UserVO, UserQuery, UserForm } from '@/api/im/user/types'; |
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance; |
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance; |
|
|
@ -123,6 +170,10 @@ const single = ref(true); |
|
|
const multiple = ref(true); |
|
|
const multiple = ref(true); |
|
|
const total = ref(0); |
|
|
const total = ref(0); |
|
|
|
|
|
|
|
|
|
|
|
// 存储标签和群组选项 |
|
|
|
|
|
const labelOptions = ref<any[]>([]); |
|
|
|
|
|
const groupOptions = ref<any[]>([]); |
|
|
|
|
|
|
|
|
const queryFormRef = ref<ElFormInstance>(); |
|
|
const queryFormRef = ref<ElFormInstance>(); |
|
|
const userFormRef = ref<ElFormInstance>(); |
|
|
const userFormRef = ref<ElFormInstance>(); |
|
|
const dialog = reactive<DialogOption>({ |
|
|
const dialog = reactive<DialogOption>({ |
|
|
@ -143,8 +194,10 @@ const initFormData: UserForm = { |
|
|
createdTime: undefined, |
|
|
createdTime: undefined, |
|
|
type: undefined, |
|
|
type: undefined, |
|
|
isBanned: undefined, |
|
|
isBanned: undefined, |
|
|
reason: undefined |
|
|
reason: undefined, |
|
|
} |
|
|
labelIds: '', |
|
|
|
|
|
groupIds: '' |
|
|
|
|
|
}; |
|
|
const data = reactive<PageData<UserForm, UserQuery>>({ |
|
|
const data = reactive<PageData<UserForm, UserQuery>>({ |
|
|
form: { ...initFormData }, |
|
|
form: { ...initFormData }, |
|
|
queryParams: { |
|
|
queryParams: { |
|
|
@ -152,8 +205,9 @@ const data = reactive<PageData<UserForm, UserQuery>>({ |
|
|
pageSize: 10, |
|
|
pageSize: 10, |
|
|
userName: undefined, |
|
|
userName: undefined, |
|
|
nickName: undefined, |
|
|
nickName: undefined, |
|
|
params: { |
|
|
labelIds: [], |
|
|
} |
|
|
groupIds: [], |
|
|
|
|
|
params: {} |
|
|
}, |
|
|
}, |
|
|
rules: {} |
|
|
rules: {} |
|
|
}); |
|
|
}); |
|
|
@ -165,55 +219,69 @@ const { sys_user_sex } = toRefs<any>(proxy?.useDict('sys_user_sex')); |
|
|
/** 查询用户列表 */ |
|
|
/** 查询用户列表 */ |
|
|
const getList = async () => { |
|
|
const getList = async () => { |
|
|
loading.value = true; |
|
|
loading.value = true; |
|
|
const res = await listUser(queryParams.value); |
|
|
|
|
|
|
|
|
// 转换标签和群组ID数组为逗号分隔的字符串 |
|
|
|
|
|
const params = { ...queryParams.value }; |
|
|
|
|
|
if (params.labelIds && Array.isArray(params.labelIds)) { |
|
|
|
|
|
params.labelIds = params.labelIds.join(','); |
|
|
|
|
|
} |
|
|
|
|
|
if (params.groupIds && Array.isArray(params.groupIds)) { |
|
|
|
|
|
params.groupIds = params.groupIds.join(','); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const res = await listUser(params); |
|
|
userList.value = res.rows; |
|
|
userList.value = res.rows; |
|
|
total.value = res.total; |
|
|
total.value = res.total; |
|
|
loading.value = false; |
|
|
loading.value = false; |
|
|
console.log("getList") |
|
|
console.log('getList'); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
/** 搜索按钮操作 */ |
|
|
/** 搜索按钮操作 */ |
|
|
const handleQuery = () => { |
|
|
const handleQuery = () => { |
|
|
queryParams.value.pageNum = 1; |
|
|
queryParams.value.pageNum = 1; |
|
|
getList(); |
|
|
getList(); |
|
|
console.log("handleQuery") |
|
|
console.log('handleQuery'); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
/** 重置按钮操作 */ |
|
|
/** 重置按钮操作 */ |
|
|
const resetQuery = () => { |
|
|
const resetQuery = () => { |
|
|
queryFormRef.value?.resetFields(); |
|
|
queryFormRef.value?.resetFields(); |
|
|
|
|
|
// 清空标签和群组选择 |
|
|
|
|
|
queryParams.value.labelIds = []; |
|
|
|
|
|
queryParams.value.groupIds = []; |
|
|
handleQuery(); |
|
|
handleQuery(); |
|
|
console.log("handleQuery") |
|
|
console.log('handleQuery'); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
/** 多选框选中数据 */ |
|
|
/** 多选框选中数据 */ |
|
|
const handleSelectionChange = (selection: UserVO[]) => { |
|
|
const handleSelectionChange = (selection: UserVO[]) => { |
|
|
ids.value = selection.map(item => item.id); |
|
|
ids.value = selection.map((item) => item.id); |
|
|
single.value = selection.length != 1; |
|
|
single.value = selection.length != 1; |
|
|
multiple.value = !selection.length; |
|
|
multiple.value = !selection.length; |
|
|
console.log("handleSelectionChange") |
|
|
console.log('handleSelectionChange'); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
/** 表单重置 */ |
|
|
/** 表单重置 */ |
|
|
const reset = () => { |
|
|
const reset = () => { |
|
|
form.value = { ...initFormData }; |
|
|
form.value = { ...initFormData }; |
|
|
userFormRef.value?.resetFields(); |
|
|
userFormRef.value?.resetFields(); |
|
|
console.log("reset") |
|
|
console.log('reset'); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
/** 修改按钮操作 */ |
|
|
/** 修改按钮操作 */ |
|
|
const handleDetail = async (row?: UserVO) => { |
|
|
const handleDetail = async (row?: UserVO) => { |
|
|
reset(); |
|
|
reset(); |
|
|
const _id = row?.id || ids.value[0] |
|
|
const _id = row?.id || ids.value[0]; |
|
|
const res = await getUser(_id); |
|
|
const res = await getUser(_id); |
|
|
Object.assign(form.value, res.data); |
|
|
Object.assign(form.value, res.data); |
|
|
dialog.visible = true; |
|
|
dialog.visible = true; |
|
|
dialog.title = "用户信息"; |
|
|
dialog.title = '用户信息'; |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
/** 提交按钮 */ |
|
|
/** 提交按钮 */ |
|
|
const submitForm = () => { |
|
|
const submitForm = () => { |
|
|
dialog.visible = false; |
|
|
dialog.visible = false; |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
const banHandle = (user: any) => { |
|
|
const banHandle = (user: any) => { |
|
|
ElMessageBox.prompt('封禁原因:', '确定对该用户进行封禁?', { |
|
|
ElMessageBox.prompt('封禁原因:', '确定对该用户进行封禁?', { |
|
|
@ -222,37 +290,66 @@ const banHandle = (user: any) => { |
|
|
confirmButtonText: '确定', |
|
|
confirmButtonText: '确定', |
|
|
cancelButtonText: '取消' |
|
|
cancelButtonText: '取消' |
|
|
}).then(({ value }) => { |
|
|
}).then(({ value }) => { |
|
|
const data = { id: user.id, reason: value } |
|
|
const data = { id: user.id, reason: value }; |
|
|
ban(data).then(() => { |
|
|
ban(data).then(() => { |
|
|
user.isBanned = true |
|
|
user.isBanned = true; |
|
|
ElMessage.success(`用户'${user.userName}'已被封禁`); |
|
|
ElMessage.success(`用户'${user.userName}'已被封禁`); |
|
|
}) |
|
|
}); |
|
|
}) |
|
|
}); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
const unbanHandle = (user: any) => { |
|
|
const unbanHandle = (user: any) => { |
|
|
ElMessageBox.confirm('确定解除该用户的封禁状态??', '提示', { |
|
|
ElMessageBox.confirm('确定解除该用户的封禁状态??', '提示', { |
|
|
confirmButtonText: '确定', |
|
|
confirmButtonText: '确定', |
|
|
cancelButtonText: '取消' |
|
|
cancelButtonText: '取消' |
|
|
}).then(() => { |
|
|
}).then(() => { |
|
|
const data = { id: user.id } |
|
|
const data = { id: user.id }; |
|
|
unban(data).then(() => { |
|
|
unban(data).then(() => { |
|
|
user.isBanned = false |
|
|
user.isBanned = false; |
|
|
ElMessage.success(`用户'${user.userName}'解锁成功`); |
|
|
ElMessage.success(`用户'${user.userName}'解锁成功`); |
|
|
}) |
|
|
}); |
|
|
|
|
|
}); |
|
|
}) |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** 导出按钮操作 */ |
|
|
/** 导出按钮操作 */ |
|
|
const handleExport = () => { |
|
|
const handleExport = () => { |
|
|
proxy?.download('im/user/export', { |
|
|
// 转换标签和群组ID数组为逗号分隔的字符串 |
|
|
...queryParams.value |
|
|
const params = { ...queryParams.value }; |
|
|
}, `user_${new Date().getTime()}.xlsx`) |
|
|
if (params.labelIds && Array.isArray(params.labelIds)) { |
|
|
} |
|
|
params.labelIds = params.labelIds.join(','); |
|
|
|
|
|
} |
|
|
|
|
|
if (params.groupIds && Array.isArray(params.groupIds)) { |
|
|
|
|
|
params.groupIds = params.groupIds.join(','); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
proxy?.download('im/user/export', params, `user_${new Date().getTime()}.xlsx`); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 获取标签名称 |
|
|
|
|
|
const getLabelNameById = (id: string | number) => { |
|
|
|
|
|
const label = labelOptions.value.find((item) => item.id == id); |
|
|
|
|
|
return label ? label.labelName : ''; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 获取群组名称 |
|
|
|
|
|
const getGroupNameById = (id: string | number) => { |
|
|
|
|
|
const group = groupOptions.value.find((item) => item.id == id); |
|
|
|
|
|
return group ? group.groupName : ''; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 获取标签和群组列表 |
|
|
|
|
|
const getLabelAndGroupList = async () => { |
|
|
|
|
|
try { |
|
|
|
|
|
const [labelsRes, groupsRes] = await Promise.all([getLabelList(), getGroupList()]); |
|
|
|
|
|
labelOptions.value = labelsRes.data || []; |
|
|
|
|
|
groupOptions.value = groupsRes.data || []; |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('获取标签和群组列表失败:', error); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
onMounted(() => { |
|
|
onMounted(async () => { |
|
|
|
|
|
await getLabelAndGroupList(); |
|
|
getList(); |
|
|
getList(); |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
</script> |
|
|
|