Merge remote-tracking branch 'origin/main' into dev
This commit is contained in:
commit
46186ac453
@ -7,6 +7,7 @@ from .base import base_router
|
||||
from .menus import menus_router
|
||||
from .roles import roles_router
|
||||
from .users import users_router
|
||||
from .depts import depts_router
|
||||
|
||||
v1_router = APIRouter()
|
||||
|
||||
@ -15,3 +16,4 @@ v1_router.include_router(users_router, prefix="/user", dependencies=[DependPermi
|
||||
v1_router.include_router(roles_router, prefix="/role", dependencies=[DependPermisson])
|
||||
v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependPermisson])
|
||||
v1_router.include_router(apis_router, prefix="/api", dependencies=[DependPermisson])
|
||||
v1_router.include_router(depts_router, prefix="/dept", dependencies=[DependPermisson])
|
||||
|
||||
8
app/api/v1/depts/__init__.py
Normal file
8
app/api/v1/depts/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .depts import router
|
||||
|
||||
depts_router = APIRouter()
|
||||
depts_router.include_router(router, tags=["部门模块"])
|
||||
|
||||
__all__ = ["depts_router"]
|
||||
49
app/api/v1/depts/depts.py
Normal file
49
app/api/v1/depts/depts.py
Normal file
@ -0,0 +1,49 @@
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from app.controllers.dept import dept_controller
|
||||
from app.log import logger
|
||||
from app.schemas import Success, SuccessExtra
|
||||
from app.schemas.depts import *
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/list", summary="查看部门列表")
|
||||
async def list_dept(
|
||||
name: str = Query(None, description="部门名称"),
|
||||
):
|
||||
dept_tree = await dept_controller.get_dept_tree(name)
|
||||
return Success(data=dept_tree)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看部门")
|
||||
async def get_dept(
|
||||
id: int = Query(..., description="部门ID"),
|
||||
):
|
||||
dept_obj = await dept_controller.get(id=id)
|
||||
data = await dept_obj.to_dict()
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建部门")
|
||||
async def create_dept(
|
||||
dept_in: DeptCreate,
|
||||
):
|
||||
await dept_controller.create_dept(obj_in=dept_in)
|
||||
return Success(msg="Created Successfully")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新部门")
|
||||
async def update_dept(
|
||||
dept_in: DeptUpdate,
|
||||
):
|
||||
await dept_controller.update_dept(obj_in=dept_in)
|
||||
return Success(msg="Update Successfully")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除部门")
|
||||
async def delete_dept(
|
||||
dept_id: int = Query(..., description="部门ID"),
|
||||
):
|
||||
await dept_controller.delete_dept(dept_id=dept_id)
|
||||
return Success(msg="Deleted Success")
|
||||
@ -7,6 +7,7 @@ from tortoise.expressions import Q
|
||||
from app.controllers.user import UserController
|
||||
from app.schemas.base import Success, SuccessExtra
|
||||
from app.schemas.users import *
|
||||
from app.controllers.dept import dept_controller
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -19,6 +20,7 @@ async def list_user(
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
username: str = Query("", description="用户名称,用于搜索"),
|
||||
email: str = Query("", description="邮箱地址"),
|
||||
dept_id: int = Query(None, description="部门ID")
|
||||
):
|
||||
user_controller = UserController()
|
||||
q = Q()
|
||||
@ -26,8 +28,14 @@ async def list_user(
|
||||
q &= Q(username__contains=username)
|
||||
if email:
|
||||
q &= Q(email__contains=email)
|
||||
if dept_id is not None:
|
||||
q &= Q(dept_id=dept_id)
|
||||
total, user_objs = await user_controller.list(page=page, page_size=page_size, search=q)
|
||||
data = [await obj.to_dict(m2m=True, exclude_fields=["password"]) for obj in user_objs]
|
||||
for item in data:
|
||||
dept_id = item.pop("dept_id", None)
|
||||
item["dept"] = await (await dept_controller.get(id=dept_id)).to_dict() if dept_id else {}
|
||||
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
|
||||
|
||||
|
||||
95
app/controllers/dept.py
Normal file
95
app/controllers/dept.py
Normal file
@ -0,0 +1,95 @@
|
||||
from app.core.crud import CRUDBase
|
||||
from app.models.admin import Dept, DeptClosure
|
||||
from app.schemas.depts import DeptCreate, DeptUpdate
|
||||
from tortoise.transactions import atomic
|
||||
from tortoise.expressions import Q
|
||||
|
||||
|
||||
class DeptController(CRUDBase[Dept, DeptCreate, DeptUpdate]):
|
||||
def __init__(self):
|
||||
super().__init__(model=Dept)
|
||||
|
||||
async def get_dept_tree(self, name):
|
||||
q = Q()
|
||||
# 获取所有未被软删除的部门
|
||||
q &= Q(is_deleted=False)
|
||||
if name:
|
||||
q &= Q(name__contains=name)
|
||||
all_depts = await self.model.filter(q).order_by('order')
|
||||
# 辅助函数,用于递归构建部门树
|
||||
def build_tree(parent_id):
|
||||
return [
|
||||
{
|
||||
'id': dept.id,
|
||||
'name': dept.name,
|
||||
'desc': dept.desc,
|
||||
'order': dept.order,
|
||||
'parent_id': dept.parent_id,
|
||||
'children': build_tree(dept.id) # 递归构建子部门
|
||||
}
|
||||
for dept in all_depts if dept.parent_id == parent_id
|
||||
]
|
||||
|
||||
# 从顶级部门(parent_id=0)开始构建部门树
|
||||
dept_tree = build_tree(0)
|
||||
return dept_tree
|
||||
|
||||
async def get_dept_info(self):
|
||||
pass
|
||||
|
||||
async def update_dept_closure(self, obj: Dept):
|
||||
parent_depts = await DeptClosure.filter(descendant=obj.parent_id)
|
||||
for i in parent_depts:
|
||||
print(i.ancestor, i.descendant)
|
||||
dept_closure_objs: list[DeptClosure] = []
|
||||
# 插入父级关系
|
||||
for item in parent_depts:
|
||||
dept_closure_objs.append(
|
||||
DeptClosure(
|
||||
ancestor=item.ancestor,
|
||||
descendant=obj.id,
|
||||
level=item.level + 1
|
||||
)
|
||||
)
|
||||
# 插入自身x
|
||||
dept_closure_objs.append(
|
||||
DeptClosure(
|
||||
ancestor=obj.id,
|
||||
descendant=obj.id,
|
||||
level=0
|
||||
)
|
||||
)
|
||||
# 创建关系
|
||||
await DeptClosure.bulk_create(dept_closure_objs)
|
||||
|
||||
@atomic()
|
||||
async def create_dept(self, obj_in: DeptCreate):
|
||||
# 创建
|
||||
if obj_in.parent_id != 0:
|
||||
await self.get(id=obj_in.parent_id)
|
||||
new_obj = await self.create(obj_in=obj_in)
|
||||
await self.update_dept_closure(new_obj)
|
||||
|
||||
|
||||
@atomic()
|
||||
async def update_dept(self, obj_in: DeptUpdate):
|
||||
dept_obj = await self.get(id=obj_in.id)
|
||||
# 更新部门关系
|
||||
if dept_obj.parent_id != obj_in.parent_id:
|
||||
await DeptClosure.filter(ancestor=dept_obj.id).delete()
|
||||
await DeptClosure.filter(descendant=dept_obj.id).delete()
|
||||
await self.update_dept_closure(dept_obj)
|
||||
# 更新部门信息
|
||||
dept_obj.update_from_dict(obj_in.model_dump(exclude_unset=True))
|
||||
await dept_obj.save()
|
||||
|
||||
@atomic()
|
||||
async def delete_dept(self, dept_id: int):
|
||||
# 删除部门
|
||||
obj = await self.get(id=dept_id)
|
||||
obj.is_deleted = True
|
||||
await obj.save()
|
||||
# 删除关系
|
||||
await DeptClosure.filter(descendant=dept_id).delete()
|
||||
|
||||
dept_controller = DeptController()
|
||||
@ -16,6 +16,7 @@ class User(BaseModel, TimestampMixin):
|
||||
is_superuser = fields.BooleanField(default=False, description="是否为超级管理员")
|
||||
last_login = fields.DatetimeField(null=True, description="最后登录时间")
|
||||
roles = fields.ManyToManyField("models.Role", related_name="user_roles")
|
||||
dept_id = fields.IntField(null=True, description="部门ID")
|
||||
|
||||
class Meta:
|
||||
table = "user"
|
||||
@ -65,10 +66,16 @@ class Menu(BaseModel, TimestampMixin):
|
||||
|
||||
class Dept(BaseModel, TimestampMixin):
|
||||
name = fields.CharField(max_length=20, unique=True, description="部门名称")
|
||||
desc = fields.CharField(max_length=500, null=True, blank=True, description="菜单描述")
|
||||
desc = fields.CharField(max_length=500, null=True, blank=True, description="备注")
|
||||
is_deleted = fields.BooleanField(default=False, description="软删除标记")
|
||||
order = fields.IntField(default=0, description="排序")
|
||||
parent_id = fields.IntField(default=0, max_length=10, description="父部门ID")
|
||||
|
||||
class Meta:
|
||||
table = "dept"
|
||||
|
||||
|
||||
class DeptClosure(BaseModel, TimestampMixin):
|
||||
ancestor = fields.IntField(description="父代")
|
||||
descendant = fields.IntField(description="子代")
|
||||
level = fields.IntField(default=0, description="深度")
|
||||
|
||||
19
app/schemas/depts.py
Normal file
19
app/schemas/depts.py
Normal file
@ -0,0 +1,19 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class BaseDept(BaseModel):
|
||||
name: str = Field(..., description="部门名称", example="研发中心")
|
||||
desc: str = Field("", description="备注", example="研发中心")
|
||||
order: int = Field(0, description="排序")
|
||||
parent_id: int = Field(0, description="父部门ID")
|
||||
|
||||
|
||||
class DeptCreate(BaseDept):
|
||||
...
|
||||
|
||||
|
||||
class DeptUpdate(BaseDept):
|
||||
id: int
|
||||
|
||||
def update_dict(self):
|
||||
return self.model_dump(exclude_unset=True, exclude={"id"})
|
||||
@ -23,6 +23,7 @@ class UserCreate(BaseModel):
|
||||
is_active: Optional[bool] = True
|
||||
is_superuser: Optional[bool] = False
|
||||
roles: Optional[List[int]] = []
|
||||
dept_id: Optional[int] = Field(0, description="部门ID")
|
||||
|
||||
def create_dict(self):
|
||||
return self.model_dump(exclude_unset=True, exclude={"roles"})
|
||||
@ -35,6 +36,7 @@ class UserUpdate(BaseModel):
|
||||
is_active: Optional[bool] = True
|
||||
is_superuser: Optional[bool] = False
|
||||
roles: Optional[List[int]] = []
|
||||
dept_id: Optional[int] = 0
|
||||
|
||||
|
||||
class UpdatePassword(BaseModel):
|
||||
|
||||
5483
web/pnpm-lock.yaml
generated
5483
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -31,4 +31,9 @@ export default {
|
||||
updateApi: (data = {}) => request.post('/api/update', data),
|
||||
deleteApi: (params = {}) => request.delete('/api/delete', { params }),
|
||||
refreshApi: (data = {}) => request.post('/api/refresh', data),
|
||||
// depts
|
||||
getDepts: (params = {}) => request.get('/dept/list', { params }),
|
||||
createDept: (data = {}) => request.post('/dept/create', data),
|
||||
updateDept: (data = {}) => request.post('/dept/update', data),
|
||||
deleteDept: (params = {}) => request.delete('/dept/delete', { params }),
|
||||
}
|
||||
|
||||
@ -128,5 +128,6 @@ function onChecked(rowKeys) {
|
||||
defineExpose({
|
||||
handleSearch,
|
||||
handleReset,
|
||||
tableData,
|
||||
})
|
||||
</script>
|
||||
|
||||
216
web/src/views/system/dept/index.vue
Normal file
216
web/src/views/system/dept/index.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<script setup>
|
||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
||||
import { NButton, NForm, NFormItem, NInput, NInputNumber, NPopconfirm, NTreeSelect } from 'naive-ui'
|
||||
|
||||
import CommonPage from '@/components/page/CommonPage.vue'
|
||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
||||
import CrudModal from '@/components/table/CrudModal.vue'
|
||||
import CrudTable from '@/components/table/CrudTable.vue'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
import { renderIcon } from '@/utils'
|
||||
import { useCRUD } from '@/composables'
|
||||
// import { loginTypeMap, loginTypeOptions } from '@/constant/data'
|
||||
import api from '@/api'
|
||||
|
||||
defineOptions({ name: '部门管理' })
|
||||
|
||||
const $table = ref(null)
|
||||
const queryItems = ref({})
|
||||
const vPermission = resolveDirective('permission')
|
||||
|
||||
const {
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
modalLoading,
|
||||
handleSave,
|
||||
modalForm,
|
||||
modalFormRef,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleAdd,
|
||||
} = useCRUD({
|
||||
name: 'API',
|
||||
initForm: { order: 0 },
|
||||
doCreate: api.createDept,
|
||||
doUpdate: api.updateDept,
|
||||
doDelete: api.deleteDept,
|
||||
refresh: () => $table.value?.handleSearch(),
|
||||
})
|
||||
|
||||
const deptOption = ref([])
|
||||
const isDisabled = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
$table.value?.handleSearch()
|
||||
api.getDepts().then((res) => (deptOption.value = res.data))
|
||||
})
|
||||
|
||||
const deptRules = {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入部门名称',
|
||||
trigger: ['input', 'blur', 'change'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
async function addDepts() {
|
||||
isDisabled.value = false
|
||||
handleAdd()
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '部门名称',
|
||||
key: 'name',
|
||||
width: 'auto',
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'desc',
|
||||
align: 'center',
|
||||
width: 'auto',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 'auto',
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
render(row) {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
style: 'margin-right: 8px;',
|
||||
onClick: () => {
|
||||
console.log('row', row.parent_id)
|
||||
if (row.parent_id === 0) {
|
||||
isDisabled.value = true
|
||||
} else {
|
||||
isDisabled.value = false
|
||||
}
|
||||
handleEdit(row)
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => '编辑',
|
||||
icon: renderIcon('material-symbols:edit', { size: 16 }),
|
||||
}
|
||||
),
|
||||
[[vPermission, 'post/api/v1/dept/update']]
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDelete({ dept_id: row.id }, false),
|
||||
onNegativeClick: () => {},
|
||||
},
|
||||
{
|
||||
trigger: () =>
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 16 }),
|
||||
}
|
||||
),
|
||||
[[vPermission, 'delete/api/v1/dept/delete']]
|
||||
),
|
||||
default: () => h('div', {}, '确定删除该部门吗?'),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 业务页面 -->
|
||||
<CommonPage show-footer title="部门列表">
|
||||
<template #action>
|
||||
<div>
|
||||
<NButton
|
||||
v-permission="'post/api/v1/dept/create'"
|
||||
class="float-right mr-15"
|
||||
type="primary"
|
||||
@click="addDepts"
|
||||
>
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建部门
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 表格 -->
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:columns="columns"
|
||||
:get-data="api.getDepts"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="部门名称" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.name"
|
||||
clearable
|
||||
type="text"
|
||||
placeholder="请输入部门名称"
|
||||
@keypress.enter="$table?.handleSearch()"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
|
||||
<!-- 新增/编辑 弹窗 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
@save="handleSave"
|
||||
>
|
||||
<NForm
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:rules="deptRules"
|
||||
>
|
||||
<NFormItem label="父级部门" path="parent_id">
|
||||
<NTreeSelect
|
||||
v-model:value="modalForm.parent_id"
|
||||
:options="deptOption"
|
||||
key-field="id"
|
||||
label-field="name"
|
||||
placeholder="请选择父级部门"
|
||||
clearable
|
||||
default-expand-all
|
||||
:disabled="isDisabled"
|
||||
></NTreeSelect>
|
||||
</NFormItem>
|
||||
<NFormItem label="部门名称" path="name">
|
||||
<NInput v-model:value="modalForm.name" clearable placeholder="请输入部门名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="desc">
|
||||
<NInput v-model:value="modalForm.desc" type="textarea" clearable />
|
||||
</NFormItem>
|
||||
<NFormItem label="排序" path="order">
|
||||
<NInputNumber v-model:value="modalForm.order" min="0"></NInputNumber>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
@ -25,7 +25,6 @@ import { formatDate, renderIcon } from '@/utils'
|
||||
import { useCRUD } from '@/composables'
|
||||
import api from '@/api'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
import { uniq } from 'lodash-es'
|
||||
|
||||
defineOptions({ name: '角色管理' })
|
||||
|
||||
|
||||
@ -12,6 +12,10 @@ import {
|
||||
NSwitch,
|
||||
NTag,
|
||||
NPopconfirm,
|
||||
NLayout,
|
||||
NLayoutSider,
|
||||
NLayoutContent,
|
||||
NTreeSelect,
|
||||
} from 'naive-ui'
|
||||
|
||||
import CommonPage from '@/components/page/CommonPage.vue'
|
||||
@ -53,10 +57,12 @@ const {
|
||||
})
|
||||
|
||||
const roleOption = ref([])
|
||||
const deptOption = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
$table.value?.handleSearch()
|
||||
api.getRoleList({ page: 1, page_size: 9999 }).then((res) => (roleOption.value = res.data))
|
||||
api.getDepts().then((res) => (deptOption.value = res.data))
|
||||
})
|
||||
|
||||
const columns = [
|
||||
@ -104,6 +110,13 @@ const columns = [
|
||||
return h('span', group)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '部门',
|
||||
key: 'dept.name',
|
||||
align: 'center',
|
||||
width: 40,
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '超级用户',
|
||||
key: 'is_superuser',
|
||||
@ -169,6 +182,7 @@ const columns = [
|
||||
onClick: () => {
|
||||
// roles => role_ids
|
||||
handleEdit(row)
|
||||
modalForm.value.dept_id = row.dept?.id
|
||||
modalForm.value.roles = row.roles.map((e) => (e = e.id))
|
||||
},
|
||||
},
|
||||
@ -225,6 +239,7 @@ async function handleUpdateDisable(row) {
|
||||
role_ids.push(e.id)
|
||||
})
|
||||
row.roles = role_ids
|
||||
row.dept_id = row.dept?.id
|
||||
try {
|
||||
await api.updateUser(row)
|
||||
$message?.success(row.is_active ? '已取消禁用该用户' : '已禁用该用户')
|
||||
@ -237,6 +252,16 @@ async function handleUpdateDisable(row) {
|
||||
}
|
||||
}
|
||||
|
||||
const nodeProps = ({ option }) => {
|
||||
return {
|
||||
onClick() {
|
||||
api.getUserList({ dept_id: option.id }).then((res) => {
|
||||
$table.value.tableData = res.data
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const validateAddUser = {
|
||||
username: [
|
||||
{
|
||||
@ -299,110 +324,138 @@ const validateAddUser = {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 业务页面 -->
|
||||
<CommonPage show-footer title="用户列表">
|
||||
<template #action>
|
||||
<NButton v-permission="'post/api/v1/user/create'" type="primary" @click="handleAdd">
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建用户
|
||||
</NButton>
|
||||
</template>
|
||||
<!-- 表格 -->
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:columns="columns"
|
||||
:get-data="api.getUserList"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="名称" :label-width="40">
|
||||
<NInput
|
||||
v-model:value="queryItems.username"
|
||||
clearable
|
||||
type="text"
|
||||
placeholder="请输入用户名称"
|
||||
@keypress.enter="$table?.handleSearch()"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="邮箱" :label-width="40">
|
||||
<NInput
|
||||
v-model:value="queryItems.email"
|
||||
clearable
|
||||
type="text"
|
||||
placeholder="请输入邮箱"
|
||||
@keypress.enter="$table?.handleSearch()"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
|
||||
<!-- 新增/编辑 弹窗 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
@save="handleSave"
|
||||
>
|
||||
<NForm
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:rules="validateAddUser"
|
||||
<NLayout has-sider wh-full>
|
||||
<NLayoutSider bordered content-style="padding: 24px;">
|
||||
<h1>部门管理</h1>
|
||||
<br />
|
||||
<NTree
|
||||
block-line
|
||||
:data="deptOption"
|
||||
key-field="id"
|
||||
label-field="name"
|
||||
default-expand-all
|
||||
:node-props="nodeProps"
|
||||
>
|
||||
<NFormItem label="用户名称" path="username">
|
||||
<NInput v-model:value="modalForm.username" clearable placeholder="请输入用户名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="邮箱" path="email">
|
||||
<NInput v-model:value="modalForm.email" clearable placeholder="请输入邮箱" />
|
||||
</NFormItem>
|
||||
<NFormItem v-if="modalAction === 'add'" label="密码" path="password">
|
||||
<NInput
|
||||
v-model:value="modalForm.password"
|
||||
show-password-on="mousedown"
|
||||
type="password"
|
||||
clearable
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem v-if="modalAction === 'add'" label="确认密码" path="confirmPassword">
|
||||
<NInput
|
||||
v-model:value="modalForm.confirmPassword"
|
||||
show-password-on="mousedown"
|
||||
type="password"
|
||||
clearable
|
||||
placeholder="请确认密码"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="角色" path="roles">
|
||||
<NCheckboxGroup v-model:value="modalForm.roles">
|
||||
<NSpace item-style="display: flex;">
|
||||
<NCheckbox
|
||||
v-for="item in roleOption"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
</NTree>
|
||||
</NLayoutSider>
|
||||
<NLayoutContent>
|
||||
<CommonPage show-footer title="用户列表">
|
||||
<template #action>
|
||||
<NButton v-permission="'post/api/v1/user/create'" type="primary" @click="handleAdd">
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建用户
|
||||
</NButton>
|
||||
</template>
|
||||
<!-- 表格 -->
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:columns="columns"
|
||||
:get-data="api.getUserList"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="名称" :label-width="40">
|
||||
<NInput
|
||||
v-model:value="queryItems.username"
|
||||
clearable
|
||||
type="text"
|
||||
placeholder="请输入用户名称"
|
||||
@keypress.enter="$table?.handleSearch()"
|
||||
/>
|
||||
</NSpace>
|
||||
</NCheckboxGroup>
|
||||
</NFormItem>
|
||||
<NFormItem label="超级用户" path="is_superuser">
|
||||
<NSwitch
|
||||
v-model:value="modalForm.is_superuser"
|
||||
size="small"
|
||||
:checked-value="true"
|
||||
:unchecked-value="false"
|
||||
></NSwitch>
|
||||
</NFormItem>
|
||||
<NFormItem label="禁用" path="is_active">
|
||||
<NSwitch
|
||||
v-model:value="modalForm.is_active"
|
||||
:checked-value="false"
|
||||
:unchecked-value="true"
|
||||
:default-value="true"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="邮箱" :label-width="40">
|
||||
<NInput
|
||||
v-model:value="queryItems.email"
|
||||
clearable
|
||||
type="text"
|
||||
placeholder="请输入邮箱"
|
||||
@keypress.enter="$table?.handleSearch()"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
|
||||
<!-- 新增/编辑 弹窗 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
@save="handleSave"
|
||||
>
|
||||
<NForm
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:rules="validateAddUser"
|
||||
>
|
||||
<NFormItem label="用户名称" path="username">
|
||||
<NInput v-model:value="modalForm.username" clearable placeholder="请输入用户名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="邮箱" path="email">
|
||||
<NInput v-model:value="modalForm.email" clearable placeholder="请输入邮箱" />
|
||||
</NFormItem>
|
||||
<NFormItem v-if="modalAction === 'add'" label="密码" path="password">
|
||||
<NInput
|
||||
v-model:value="modalForm.password"
|
||||
show-password-on="mousedown"
|
||||
type="password"
|
||||
clearable
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem v-if="modalAction === 'add'" label="确认密码" path="confirmPassword">
|
||||
<NInput
|
||||
v-model:value="modalForm.confirmPassword"
|
||||
show-password-on="mousedown"
|
||||
type="password"
|
||||
clearable
|
||||
placeholder="请确认密码"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="角色" path="roles">
|
||||
<NCheckboxGroup v-model:value="modalForm.roles">
|
||||
<NSpace item-style="display: flex;">
|
||||
<NCheckbox
|
||||
v-for="item in roleOption"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
/>
|
||||
</NSpace>
|
||||
</NCheckboxGroup>
|
||||
</NFormItem>
|
||||
<NFormItem label="超级用户" path="is_superuser">
|
||||
<NSwitch
|
||||
v-model:value="modalForm.is_superuser"
|
||||
size="small"
|
||||
:checked-value="true"
|
||||
:unchecked-value="false"
|
||||
></NSwitch>
|
||||
</NFormItem>
|
||||
<NFormItem label="禁用" path="is_active">
|
||||
<NSwitch
|
||||
v-model:value="modalForm.is_active"
|
||||
:checked-value="false"
|
||||
:unchecked-value="true"
|
||||
:default-value="true"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="部门" path="dept_id">
|
||||
<NTreeSelect
|
||||
v-model:value="modalForm.dept_id"
|
||||
:options="deptOption"
|
||||
key-field="id"
|
||||
label-field="name"
|
||||
placeholder="请选择部门"
|
||||
clearable
|
||||
default-expand-all
|
||||
></NTreeSelect>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</NLayoutContent>
|
||||
</NLayout>
|
||||
<!-- 业务页面 -->
|
||||
</template>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user