feat: 用户管理

This commit is contained in:
wangdan-fit2cloud 2024-03-20 10:38:59 +08:00
parent 2aeb8acfb3
commit eba8cb9045
12 changed files with 526 additions and 8 deletions

View File

@ -1,5 +1,5 @@
import { Result } from '@/request/Result'
import { get, post, postStream, del, put } from '@/request/index'
import { get, post, del, put } from '@/request/index'
import { type Ref } from 'vue'

View File

@ -1,5 +1,5 @@
import { Result } from '@/request/Result'
import { get, post, postStream, del, put } from '@/request/index'
import { get, post, del, put } from '@/request/index'
import type { pageRequest } from '@/api/type/common'
import { type Ref } from 'vue'

View File

@ -89,11 +89,11 @@ interface ResetPasswordRequest {
/**
*
*/
email: string
email?: string
/**
*
*/
code: string
code?: string
/**
*
*/

77
ui/src/api/user-manage.ts Normal file
View File

@ -0,0 +1,77 @@
import { Result } from '@/request/Result'
import { get, post, del, put } from '@/request/index'
import type { pageRequest } from '@/api/type/common'
import { type Ref } from 'vue'
const prefix = '/user_manage'
/**
*
* @param
* page {
"current_page": "string",
"page_size": "string",
}
* @query
email_or_username: string
*/
const getUserManage: (
page: pageRequest,
email_or_username: string,
loading?: Ref<boolean>
) => Promise<Result<any>> = (page, email_or_username, loading) => {
return get(
`${prefix}/${page.current_page}/${page.page_size}`,
email_or_username ? { email_or_username } : undefined,
loading
)
}
/**
*
* @param user_id,
*/
const delUserManage: (user_id: string, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
user_id,
loading
) => {
return del(`${prefix}/${user_id}`, undefined, {}, loading)
}
/**
*
*/
const postUserManage: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
data,
loading
) => {
return post(`${prefix}`, data, undefined, loading)
}
/**
*
*/
const putUserManage: (
user_id: string,
data: any,
loading?: Ref<boolean>
) => Promise<Result<any>> = (user_id, data, loading) => {
return put(`${prefix}/${user_id}`, data, undefined, loading)
}
/**
*
*/
const putUserManagePassword: (
user_id: string,
data: any,
loading?: Ref<boolean>
) => Promise<Result<any>> = (user_id, data, loading) => {
return put(`${prefix}/${user_id}/re_password`, data, undefined, loading)
}
export default {
getUserManage,
delUserManage,
postUserManage,
putUserManage,
putUserManagePassword
}

View File

@ -3,20 +3,34 @@ const settingRouter = {
path: '/setting',
name: 'setting',
meta: { icon: 'Setting', title: '系统设置', permission: 'SETTING:READ' },
redirect: '/setting',
redirect: '/user',
component: Layout,
children: [
{
path: '/setting',
name: 'setting',
path: '/user',
name: 'user',
meta: {
icon: 'User',
iconActive: 'UserFilled',
title: '用户管理',
activeMenu: '/setting',
parentPath: '/setting',
parentName: 'setting'
},
component: () => import('@/views/user-manage/index.vue')
},
{
path: '/team',
name: 'team',
meta: {
icon: 'app-team',
iconActive: 'app-team-active',
title: '团队管理',
activeMenu: '/setting',
parentPath: '/setting',
parentName: 'setting'
},
component: () => import('@/views/setting/index.vue')
component: () => import('@/views/team/index.vue')
},
{
path: '/template',

View File

@ -144,3 +144,4 @@ onMounted(() => {
})
</script>
<style lang="scss" scope></style>
../utils

View File

@ -0,0 +1,143 @@
<template>
<el-dialog :title="title" v-model="dialogVisible">
<el-form
ref="userFormRef"
:model="userForm"
:rules="rules"
label-position="top"
require-asterisk-position="right"
@submit.prevent
>
<el-form-item prop="username" label="用户名">
<el-input
v-model="userForm.username"
placeholder="请输入用户名"
maxlength="64"
show-word-limit
>
</el-input>
</el-form-item>
<el-form-item label="姓名">
<el-input
v-model="userForm.nick_name"
placeholder="请输入姓名"
maxlength="64"
show-word-limit
>
</el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input type="email" v-model="userForm.email" placeholder="请输入邮箱"> </el-input>
</el-form-item>
<el-form-item label="手机号">
<el-input type="email" v-model="userForm.phone" placeholder="请输入手机号"> </el-input>
</el-form-item>
<el-form-item label="登录密码" prop="password" v-if="!isEdit">
<el-input
type="password"
v-model="userForm.password"
placeholder="请输入密码"
show-password
>
</el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click.prevent="dialogVisible = false"> 取消 </el-button>
<el-button type="primary" @click="submit(userFormRef)" :loading="loading"> 保存 </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { cloneDeep } from 'lodash'
import userApi from '@/api/user-manage'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
const props = defineProps({
title: String
})
const emit = defineEmits(['refresh'])
const userFormRef = ref()
const userForm = ref<any>({
username: '',
email: '',
password: '',
phone: '',
nick_name: ''
})
const rules = reactive({
username: [{ required: true, message: '请输入用户名' }],
email: [{ required: true, message: '请输入邮箱' }],
password: [
{
required: true,
message: '请输入密码',
trigger: 'blur'
},
{
min: 6,
max: 20,
message: '长度在 6 到 20 个字符',
trigger: 'blur'
}
]
})
const dialogVisible = ref<boolean>(false)
const loading = ref(false)
const isEdit = ref(false)
watch(dialogVisible, (bool) => {
if (!bool) {
userForm.value = {
username: '',
email: '',
password: '',
phone: '',
nick_name: ''
}
userFormRef.value?.clearValidate()
isEdit.value = false
}
})
const open = (data: any) => {
if (data) {
userForm.value = cloneDeep(data)
isEdit.value = true
}
dialogVisible.value = true
}
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
if (isEdit.value) {
userApi.putUserManage(userForm.value.id, userForm.value, loading).then((res) => {
emit('refresh')
MsgSuccess('编辑成功')
dialogVisible.value = false
})
} else {
userApi.postUserManage(userForm.value, loading).then((res) => {
emit('refresh')
MsgSuccess('创建成功')
dialogVisible.value = false
})
}
} else {
console.log('error submit!', fields)
}
})
}
defineExpose({ open })
</script>
<style lang="scss" scope></style>

View File

@ -0,0 +1,128 @@
<template>
<el-dialog title="修改用户密码" v-model="dialogVisible">
<el-form
ref="userFormRef"
:model="userForm"
:rules="rules"
label-position="top"
require-asterisk-position="right"
@submit.prevent
>
<el-form-item label="新密码" prop="password">
<el-input
type="password"
v-model="userForm.password"
placeholder="请输入新密码"
show-password
>
</el-input>
</el-form-item>
<el-form-item label="确认密码" prop="re_password">
<el-input
type="password"
v-model="userForm.re_password"
placeholder="请输入确认密码"
show-password
>
</el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click.prevent="dialogVisible = false"> 取消 </el-button>
<el-button type="primary" @click="submit(userFormRef)" :loading="loading"> 保存 </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import type { ResetPasswordRequest } from '@/api/type/user'
import { cloneDeep } from 'lodash'
import userApi from '@/api/user-manage'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
const emit = defineEmits(['refresh'])
const userFormRef = ref()
const userForm = ref<any>({
password: '',
re_password: ''
})
const rules = reactive<FormRules<ResetPasswordRequest>>({
password: [
{
required: true,
message: '请输入新密码',
trigger: 'blur'
},
{
min: 6,
max: 20,
message: '长度在 6 到 20 个字符',
trigger: 'blur'
}
],
re_password: [
{
required: true,
message: '请输入确认密码',
trigger: 'blur'
},
{
min: 6,
max: 20,
message: '长度在 6 到 20 个字符',
trigger: 'blur'
},
{
validator: (rule, value, callback) => {
if (userFormRef.value.password != userFormRef.value.re_password) {
callback(new Error('密码不一致'))
} else {
callback()
}
},
trigger: 'blur'
}
]
})
const dialogVisible = ref<boolean>(false)
const loading = ref(false)
const userId = ref('')
watch(dialogVisible, (bool) => {
if (!bool) {
userForm.value = {
password: '',
re_password: ''
}
userFormRef.value?.clearValidate()
}
})
const open = (data: any) => {
userId.value = data.id
dialogVisible.value = true
}
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
userApi.putUserManagePassword(userId.value, userForm.value, loading).then((res) => {
emit('refresh')
MsgSuccess('修改用户密码成功')
dialogVisible.value = false
})
} else {
console.log('error submit!', fields)
}
})
}
defineExpose({ open })
</script>
<style lang="scss" scope></style>

View File

@ -0,0 +1,155 @@
<template>
<LayoutContainer header="用户管理">
<div class="p-24">
<div class="flex-between">
<el-button type="primary" @click="createUser">创建用户</el-button>
<el-input
v-model="searchValue"
@change="searchHandle"
placeholder="搜索"
prefix-icon="Search"
class="w-240"
/>
</div>
<app-table
class="mt-16"
:data="tableData"
:pagination-config="paginationConfig"
@sizeChange="handleSizeChange"
@changePage="getList"
v-loading="loading"
>
<el-table-column prop="username" label="用户名" />
<el-table-column prop="nick_name" label="姓名" />
<el-table-column prop="email" label="邮箱" show-overflow-tooltip />
<el-table-column prop="phone" label="手机号" />
<el-table-column label="状态" width="60">
<template #default="{ row }">
<div @click.stop>
<el-switch size="small" v-model="row.is_active" @change="changeState($event, row)" />
</div>
</template>
</el-table-column>
<el-table-column label="创建时间" width="180">
<template #default="{ row }">
{{ datetimeFormat(row.create_time) }}
</template>
</el-table-column>
<el-table-column label="操作" width="110" align="left">
<template #default="{ row }">
<span class="mr-4">
<el-tooltip effect="dark" content="编辑" placement="top">
<el-button type="primary" text @click.stop="editUser(row)">
<el-icon><EditPen /></el-icon>
</el-button>
</el-tooltip>
</span>
<span class="mr-4">
<el-tooltip effect="dark" content="修改用户密码" placement="top">
<el-button type="primary" text @click.stop="editPwdUser(row)">
<el-icon><Lock /></el-icon>
</el-button>
</el-tooltip>
</span>
<span class="mr-4">
<el-tooltip effect="dark" content="删除" placement="top">
<el-button type="primary" text @click.stop="deleteUserManage(row)">
<el-icon><Delete /></el-icon>
</el-button>
</el-tooltip>
</span>
</template>
</el-table-column>
</app-table>
</div>
<UserDialog :title="title" ref="UserDialogRef" @refresh="refresh" />
<UserPwdDialog ref="UserPwdDialogRef" @refresh="refresh" />
</LayoutContainer>
</template>
<script setup lang="ts">
import { ref, onMounted, reactive, watch, computed } from 'vue'
import UserDialog from './component/UserDialog.vue'
import UserPwdDialog from './component/UserPwdDialog.vue'
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
import userApi from '@/api/user-manage'
import { datetimeFormat } from '@/utils/time'
const UserDialogRef = ref()
const UserPwdDialogRef = ref()
const title = ref('')
const loading = ref(false)
const paginationConfig = reactive({
current_page: 1,
page_size: 20,
total: 0
})
const tableData = ref<any[]>([])
const searchValue = ref('')
function searchHandle() {
paginationConfig.total = 0
paginationConfig.current_page = 1
tableData.value = []
getList()
}
function editPwdUser(row: any) {
UserPwdDialogRef.value.open(row)
}
function editUser(row: any) {
title.value = '编辑用户'
UserDialogRef.value.open(row)
}
function createUser() {
title.value = '创建用户'
UserDialogRef.value.open()
}
function deleteUserManage(row: any) {
MsgConfirm(
`是否删除用户:${row.username} ?`,
`删除用户,该用户创建的资源(应用、知识库、模型)都会删除,请谨慎操作。`,
{
confirmButtonText: '删除',
confirmButtonClass: 'danger'
}
)
.then(() => {
loading.value = true
userApi.delUserManage(row.id, loading).then(() => {
MsgSuccess('删除成功')
getList()
})
})
.catch(() => {})
}
function handleSizeChange() {
paginationConfig.current_page = 1
getList()
}
function getList() {
return userApi.getUserManage(paginationConfig, searchValue.value, loading).then((res) => {
tableData.value = res.data.records
paginationConfig.total = res.data.total
})
}
function refresh() {
getList()
}
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>
.log-table tr {
cursor: pointer;
}
</style>