feat: 用户管理
This commit is contained in:
parent
fc8632b16e
commit
62c647c1f2
@ -48,10 +48,10 @@ async def user_arr(
|
||||
|
||||
async def user_list(query: UserQuery) -> Response[ListAll[list[UserRead]]]:
|
||||
"""post查询用户列表"""
|
||||
limit = query.size
|
||||
skip = (query.offset - 1) * limit
|
||||
del query.offset, query.size
|
||||
users, count = await get_users(skip, limit, query.dict())
|
||||
size = query.limit
|
||||
skip = (query.offset - 1) * size
|
||||
del query.offset, query.limit
|
||||
users, count = await get_users(skip, size, query.dict())
|
||||
return Response(data=ListAll(total=count, items=users))
|
||||
|
||||
|
||||
|
@ -63,9 +63,8 @@ async def get_users(skip: int, limit: int, kwargs: dict = None):
|
||||
kwargs = {f"{k}__contains": v for k, v in kwargs.items()}
|
||||
else:
|
||||
kwargs = {}
|
||||
result = (
|
||||
UserModel.filter(status__not_in=[9, 5], **kwargs).all().order_by("-created")
|
||||
)
|
||||
result = UserModel.filter(**kwargs).all().order_by("-created")
|
||||
print(await result.offset(skip).limit(limit))
|
||||
return await result.offset(skip).limit(limit), await result.count()
|
||||
|
||||
|
||||
@ -98,9 +97,11 @@ async def put_user(uid: int, data: UserPut):
|
||||
for role in roles:
|
||||
if await get_role({"id": role.rid, "status__not": 9}) is None:
|
||||
return role.rid
|
||||
|
||||
# 更新用户
|
||||
if data.password != "加密之后的密码":
|
||||
data.password = get_password_hash(data.password)
|
||||
else:
|
||||
del data.password
|
||||
await UserModel.filter(id=uid).update(**data.dict())
|
||||
|
||||
# todo 1. 先前有的角色,这次更新成没有 2. 先前没有的角色 这次更新成有, 3. 只更新了状态
|
||||
|
BIN
backend/mini.db
BIN
backend/mini.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -42,7 +42,7 @@ class QueryData(BaseModel):
|
||||
"""分页查询基础数据"""
|
||||
|
||||
offset: int = 1
|
||||
size: int = 10
|
||||
limit: int = 10
|
||||
|
||||
|
||||
class ListAll(GenericModel, Generic[T]):
|
||||
|
@ -6,7 +6,6 @@ const route = useRoute()
|
||||
const cruPath = computed(() => {
|
||||
return route.path.substring(1, route.path.length).split('/')
|
||||
})
|
||||
console.log(route.path, route.fullPath)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -27,6 +27,8 @@ const handleOk = () => {
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
visible.value = false
|
||||
// 页面刷新-》 白屏
|
||||
location.reload()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
9
frontend/src/service/role.js
Normal file
9
frontend/src/service/role.js
Normal file
@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取角色列表, 需要考虑创建用户选择角色应该是所有未被删除的情况
|
||||
export function getRoles(parms) {
|
||||
return request({
|
||||
url: '/role',
|
||||
parms
|
||||
})
|
||||
}
|
@ -31,8 +31,44 @@ export function selectRole(rid) {
|
||||
}
|
||||
|
||||
// 获取用户列表
|
||||
export function getUsers() {
|
||||
export function getUsers(params) {
|
||||
return request({
|
||||
url: '/user'
|
||||
url: '/user',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 条件查询用户列表
|
||||
export function queryUser(data) {
|
||||
return request({
|
||||
url: '/user/query',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
export function delUser(id) {
|
||||
return request({
|
||||
url: `/user/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
export function addUser(data) {
|
||||
return request({
|
||||
url: '/user',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新用户
|
||||
export function putUser(id, data) {
|
||||
return request({
|
||||
url: `/user/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ export default (app) => {
|
||||
// 按钮权限
|
||||
app.directive('per', {
|
||||
mounted(el, binding) {
|
||||
console.log(el, binding.value)
|
||||
if (
|
||||
// 是否存在
|
||||
userStore().userInfo.permissions.indexOf(binding.value) === -1
|
||||
|
11
frontend/src/views/login/conf.js
Normal file
11
frontend/src/views/login/conf.js
Normal file
@ -0,0 +1,11 @@
|
||||
// 登录表单验证配置
|
||||
export const loginRules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 5, max: 20, message: '5~20', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, max: 12, message: '6~12', trigger: 'blur' }
|
||||
]
|
||||
}
|
@ -3,19 +3,9 @@ import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
|
||||
import { userStore } from '@/stores/user'
|
||||
import { loginRules } from './conf'
|
||||
|
||||
const store = userStore()
|
||||
// 表单配置
|
||||
const rules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 5, max: 20, message: '5~20', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, max: 12, message: '6~12', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
// 响应式数据
|
||||
const formRef = ref()
|
||||
@ -40,7 +30,7 @@ const submitForm = (formEl) => {
|
||||
<div class="continer">
|
||||
<h1>Mini RBAC</h1>
|
||||
|
||||
<a-form ref="formRef" :model="formData" :rules="rules">
|
||||
<a-form ref="formRef" :model="formData" :rules="loginRules">
|
||||
<a-form-item has-feedback name="username">
|
||||
<a-input v-model:value.trim="formData.username" placeholder="Username">
|
||||
<template #prefix>
|
||||
|
@ -1,11 +1,9 @@
|
||||
// 表格数据列 表头配置
|
||||
export const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
align: 'center',
|
||||
customRender: ({ index }) => `${index + 1}`
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id'
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
@ -37,3 +35,33 @@ export const columns = [
|
||||
key: 'action'
|
||||
}
|
||||
]
|
||||
|
||||
// 新增用户的校验配置
|
||||
export const addUserRules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 5, max: 20, message: '5~20', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, max: 12, message: '6~12', trigger: 'blur' }
|
||||
],
|
||||
nickname: [
|
||||
{ required: true, message: '请输入昵称', trigger: 'blur' },
|
||||
{ min: 5, max: 20, message: '5~20', trigger: 'blur' }
|
||||
],
|
||||
roles: [{ required: true, message: '请配置角色', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// 编辑用户的校验配置
|
||||
export const putUserRules = {
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, max: 12, message: '6~12', trigger: 'blur' }
|
||||
],
|
||||
nickname: [
|
||||
{ required: true, message: '请输入昵称', trigger: 'blur' },
|
||||
{ min: 5, max: 20, message: '5~20', trigger: 'blur' }
|
||||
],
|
||||
roles: [{ required: true, message: '请配置角色', trigger: 'blur' }]
|
||||
}
|
||||
|
@ -1,46 +1,193 @@
|
||||
<script setup>
|
||||
import { getUsers } from '@/service/user'
|
||||
import { columns } from './conf'
|
||||
import { formatTime } from '@/utils/format'
|
||||
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
|
||||
import { PlusOutlined, SearchOutlined } from '@ant-design/icons-vue'
|
||||
import { PlusOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
import { getUsers, queryUser, delUser, addUser, getUserInfo, putUser } from '@/service/user'
|
||||
import { getRoles } from '@/service/role'
|
||||
import { columns, addUserRules, putUserRules } from './conf'
|
||||
import { formatTime } from '@/utils/format'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { userStore } from '@/stores/user'
|
||||
import router from '@/router'
|
||||
|
||||
/**查询表单响应式数据 */
|
||||
const queryFormRef = ref()
|
||||
|
||||
const queryForm = reactive({
|
||||
username: '',
|
||||
nickname: ''
|
||||
})
|
||||
|
||||
// 是否查询
|
||||
const isQuery = ref(false)
|
||||
|
||||
// 列表数据
|
||||
const dataSource = ref([])
|
||||
|
||||
// 页面展示数据条数变化回调
|
||||
const pageSizeChange = (current, size) => {
|
||||
console.log(current, size, '展示数量变化')
|
||||
}
|
||||
|
||||
// 页码变化回调
|
||||
const pageChange = (page, pageSize) => {
|
||||
console.log(page, pageSize, '页码变化')
|
||||
}
|
||||
|
||||
//分页
|
||||
//分页响应式配置
|
||||
const pagination = reactive({
|
||||
current: 1, //当前页
|
||||
pageSize: 10, // 每页数量
|
||||
showSizeChanger: true,
|
||||
total: 200,
|
||||
pageSizeOptions: ['10', '50', '100'],
|
||||
pageSize: 5, // 每页数量
|
||||
// showSizeChanger: true,
|
||||
total: 0,
|
||||
pageSizeOptions: ['5', '10', '50'],
|
||||
showTotal: (total) => `共 ${total} 条数据`,
|
||||
onShowSizeChange: pageSizeChange,
|
||||
onChange: pageChange
|
||||
onChange: (page, pageSize) => {
|
||||
pagination.current = page
|
||||
pagination.pageSize = pageSize
|
||||
getPageData()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getPageData()
|
||||
})
|
||||
|
||||
// 获取页面数据
|
||||
const getPageData = () => {
|
||||
getUsers().then((res) => {
|
||||
let offset = pagination.current
|
||||
let limit = pagination.pageSize
|
||||
if (!isQuery.value) {
|
||||
getUsers({ offset, limit }).then((res) => {
|
||||
dataSource.value = res.data.items
|
||||
pagination.total = res.data.total
|
||||
})
|
||||
} else {
|
||||
queryUser({ offset, limit, username: queryForm.username, nickname: queryForm.nickname }).then(
|
||||
(res) => {
|
||||
dataSource.value = res.data.items
|
||||
pagination.total = res.data.total
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 点击查询事件
|
||||
const clickQuery = () => {
|
||||
isQuery.value = true
|
||||
getPageData()
|
||||
}
|
||||
|
||||
// 重置搜索框
|
||||
const resetQueryForm = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
isQuery.value = false
|
||||
getPageData()
|
||||
}
|
||||
|
||||
// 删除
|
||||
const delClick = (record) => {
|
||||
delUser(record.id)
|
||||
getPageData()
|
||||
}
|
||||
|
||||
/**新增用户 */
|
||||
const addVisible = ref(false)
|
||||
const formRef = ref(null)
|
||||
const newUserForm = reactive({
|
||||
username: '',
|
||||
nickname: '',
|
||||
password: '',
|
||||
roles: []
|
||||
})
|
||||
// 选择角色
|
||||
const roleOptions = ref([])
|
||||
|
||||
const addClick = () => {
|
||||
addVisible.value = !addVisible.value
|
||||
// 请求角色信息
|
||||
getRoles({ limit: 100 }).then((res) => {
|
||||
roleOptions.value = res.data.items.map((e) => ({ label: e.name, value: e.id }))
|
||||
})
|
||||
}
|
||||
|
||||
// 新增modal 确定的回调
|
||||
const onOk = () => {
|
||||
formRef.value.validateFields().then(() => {
|
||||
// 表单验证通过
|
||||
const { username, nickname, password, roles } = newUserForm
|
||||
let rids = roles.map((e, i) => ({ rid: e, status: i === 0 ? 5 : 1 }))
|
||||
addUser({ username, nickname, password, rids }).then((res) => {
|
||||
if (res.msg === '请求成功') {
|
||||
message.success('新增成功')
|
||||
// 1. 关闭 modal
|
||||
addVisible.value = !addVisible.value
|
||||
// 2. 重置响应式数据
|
||||
formRef.value.resetFields()
|
||||
// 3. 刷新页面数据
|
||||
getPageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 新增modal 取消回调
|
||||
const onCancel = () => {
|
||||
// 2. 重置响应式数据
|
||||
formRef.value.resetFields()
|
||||
}
|
||||
|
||||
/**更新用户 */
|
||||
|
||||
const putClick = (record) => {
|
||||
// 打开编辑的modal
|
||||
putVisible.value = !putVisible.value
|
||||
// 请求所有角色信息
|
||||
getRoles({ limit: 100 }).then((res) => {
|
||||
roleOptions.value = res.data.items.map((e) => ({ label: e.name, value: e.id }))
|
||||
})
|
||||
putId.value = record.id
|
||||
// 加载当前用户id 具备的用户角色
|
||||
getUserInfo(record.id).then((res) => {
|
||||
// 角色信息
|
||||
putUserForm.roles = res.data.roles.map((e) => e.id)
|
||||
// 昵称信息
|
||||
putUserForm.nickname = res.data.nickname
|
||||
putUserForm.password = '加密之后的密码'
|
||||
})
|
||||
}
|
||||
|
||||
const putVisible = ref(false)
|
||||
|
||||
const putUserFormRef = ref()
|
||||
const putUserForm = reactive({
|
||||
nickname: '',
|
||||
password: '',
|
||||
roles: []
|
||||
})
|
||||
const putId = ref()
|
||||
|
||||
//modal 事件
|
||||
const onOkPut = () => {
|
||||
//校验数据
|
||||
putUserFormRef.value.validateFields().then(() => {
|
||||
//验证通过
|
||||
const { nickname, password, roles } = putUserForm
|
||||
let rids = roles.map((e, i) => ({ rid: e, status: i === 0 ? 5 : 1 }))
|
||||
putUser(putId.value, { nickname, password, rids }).then((res) => {
|
||||
if (res.msg === '请求成功') {
|
||||
message.success('修改成功')
|
||||
// 1. 关闭 modal
|
||||
putVisible.value = !putVisible.value
|
||||
// 2. 重置响应式数据
|
||||
putUserFormRef.value.resetFields()
|
||||
if (putId.value === userStore().userInfo.id) {
|
||||
// 改了自己
|
||||
message.warning('修改了自己,请重新登录')
|
||||
userStore().$reset()
|
||||
router.push('/login')
|
||||
} else {
|
||||
// 3. 刷新页面数据
|
||||
getPageData()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const onCancelPut = () => {
|
||||
putUserFormRef.value.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -48,17 +195,16 @@ const getPageData = () => {
|
||||
<div class="user">
|
||||
<!-- 查询 -->
|
||||
<div class="search">
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="用户名">
|
||||
<a-input placeholder="用户名"> </a-input>
|
||||
<a-form ref="queryFormRef" layout="inline" :model="queryForm">
|
||||
<a-form-item label="用户名" name="username">
|
||||
<a-input placeholder="用户名" v-model:value="queryForm.username"> </a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="昵称">
|
||||
<a-input placeholder="昵称"> </a-input>
|
||||
<a-form-item label="昵称" name="nickname">
|
||||
<a-input placeholder="昵称" v-model:value="queryForm.nickname"> </a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" v-per="'user:query'">
|
||||
<template #icon> <SearchOutlined /> </template>查询</a-button
|
||||
>
|
||||
<a-form-item v-per="'user:query'">
|
||||
<a-button type="primary" @click="clickQuery">查询</a-button>
|
||||
<a-button style="margin-left: 10px" @click="resetQueryForm">重置</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
@ -67,18 +213,23 @@ const getPageData = () => {
|
||||
<div class="data">
|
||||
<a-card title="用户列表"
|
||||
><template #extra>
|
||||
<a-button type="primary" v-per="'user:create'">
|
||||
<a-button type="primary" v-per="'user:create'" @click="addClick">
|
||||
<template #icon><plus-outlined /></template>
|
||||
新增</a-button
|
||||
>
|
||||
</template>
|
||||
|
||||
<!-- 数据 -->
|
||||
<a-table :columns="columns" :data-source="dataSource" :pagination="pagination">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:scroll="{ y: 'calc(100vh - 460px)' }"
|
||||
:data-source="dataSource"
|
||||
:pagination="pagination"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag :color="record.status !== 9 ? 'green' : 'red'">
|
||||
{{ record.status !== 9 ? '正常' : '已删除' }}
|
||||
{{ record.status !== 9 ? '正常' : '删除' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'created'">
|
||||
@ -89,10 +240,10 @@ const getPageData = () => {
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<span>
|
||||
<a v-pre="'user:update'">修改</a>
|
||||
<a v-per="'user:update'" @click="putClick(record)">编辑</a>
|
||||
<a-divider type="vertical" />
|
||||
<template v-if="record.status !== 9">
|
||||
<a v-per="'user:delete'">删除</a>
|
||||
<a v-per="'user:delete'" @click="delClick(record)">删除</a>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
@ -100,6 +251,65 @@ const getPageData = () => {
|
||||
</a-table>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<!-- 新增 用户-->
|
||||
<a-modal
|
||||
v-model:visible="addVisible"
|
||||
title="新建用户"
|
||||
ok-text="创建"
|
||||
cancel-text="取消"
|
||||
@ok="onOk"
|
||||
@cancel="onCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="newUserForm" :rules="addUserRules">
|
||||
<a-form-item name="username" label="账号">
|
||||
<a-input v-model:value="newUserForm.username" placeholder="用于登录" />
|
||||
</a-form-item>
|
||||
<a-form-item name="nickname" label="昵称">
|
||||
<a-input v-model:value="newUserForm.nickname" />
|
||||
</a-form-item>
|
||||
<a-form-item name="password" label="密码">
|
||||
<a-input-password v-model:value="newUserForm.password" autocomplete="on" />
|
||||
</a-form-item>
|
||||
<a-form-item name="roles" label="角色">
|
||||
<a-select
|
||||
v-model:value="newUserForm.roles"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
placeholder="默认激活第一个角色"
|
||||
:options="roleOptions"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 修改用户 -->
|
||||
<a-modal
|
||||
v-model:visible="putVisible"
|
||||
title="编辑用户"
|
||||
ok-text="确认"
|
||||
cancel-text="取消"
|
||||
@ok="onOkPut"
|
||||
@cancel="onCancelPut"
|
||||
>
|
||||
<a-form ref="putUserFormRef" :model="putUserForm" :rules="putUserRules">
|
||||
<a-form-item name="nickname" label="昵称">
|
||||
<a-input v-model:value="putUserForm.nickname" />
|
||||
</a-form-item>
|
||||
<a-form-item name="password" label="密码">
|
||||
<a-input-password v-model:value="putUserForm.password" autocomplete="on" />
|
||||
</a-form-item>
|
||||
<a-form-item name="roles" label="角色">
|
||||
<a-select
|
||||
v-model:value="putUserForm.roles"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
placeholder="默认激活第一个角色"
|
||||
:options="roleOptions"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user