新增部门管理功能,角色权限支持部门数据权限划分,接口权限认证加入部门数据权限字段
This commit is contained in:
parent
d16382c90c
commit
bade36dd1b
25
kinit-admin/src/api/vadmin/auth/dept.ts
Normal file
25
kinit-admin/src/api/vadmin/auth/dept.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export const getDeptListApi = (params: any): Promise<IResponse> => {
|
||||
return request.get({ url: '/vadmin/auth/depts', params })
|
||||
}
|
||||
|
||||
export const delDeptListApi = (data: any): Promise<IResponse> => {
|
||||
return request.delete({ url: '/vadmin/auth/depts', data })
|
||||
}
|
||||
|
||||
export const addDeptListApi = (data: any): Promise<IResponse> => {
|
||||
return request.post({ url: '/vadmin/auth/depts', data })
|
||||
}
|
||||
|
||||
export const putDeptListApi = (data: any): Promise<IResponse> => {
|
||||
return request.put({ url: `/vadmin/auth/depts/${data.id}`, data })
|
||||
}
|
||||
|
||||
export const getDeptTreeOptionsApi = (): Promise<IResponse> => {
|
||||
return request.get({ url: '/vadmin/auth/dept/tree/options' })
|
||||
}
|
||||
|
||||
export const getDeptUserTreeOptionsApi = (): Promise<IResponse> => {
|
||||
return request.get({ url: '/vadmin/auth/dept/user/tree/options' })
|
||||
}
|
232
kinit-admin/src/views/Vadmin/Auth/Dept/Dept.vue
Normal file
232
kinit-admin/src/views/Vadmin/Auth/Dept/Dept.vue
Normal file
@ -0,0 +1,232 @@
|
||||
<script setup lang="tsx">
|
||||
import { reactive, ref, unref } from 'vue'
|
||||
import {
|
||||
getDeptListApi,
|
||||
delDeptListApi,
|
||||
addDeptListApi,
|
||||
putDeptListApi
|
||||
} from '@/api/vadmin/auth/dept'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { ElButton, ElSwitch, ElRow, ElCol } from 'element-plus'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import Write from './components/Write.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
|
||||
defineOptions({
|
||||
name: 'AuthDept'
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { tableRegister, tableState, tableMethods } = useTable({
|
||||
fetchDataApi: async () => {
|
||||
const { pageSize, currentPage } = tableState
|
||||
const res = await getDeptListApi({
|
||||
page: unref(currentPage),
|
||||
limit: unref(pageSize)
|
||||
})
|
||||
return {
|
||||
list: res.data || [],
|
||||
total: res.count || 0
|
||||
}
|
||||
},
|
||||
fetchDelApi: async (value) => {
|
||||
const res = await delDeptListApi(value)
|
||||
return res.code === 200
|
||||
}
|
||||
})
|
||||
|
||||
const { dataList, loading } = tableState
|
||||
const { getList, delList } = tableMethods
|
||||
|
||||
const tableColumns = reactive<TableColumn[]>([
|
||||
{
|
||||
field: 'name',
|
||||
label: '部门名称',
|
||||
disabled: true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
field: 'dept_key',
|
||||
label: '部门标识',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
field: 'owner',
|
||||
label: '负责人',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
label: '联系电话',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: '邮箱',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
field: 'desc',
|
||||
label: '描述',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
field: 'order',
|
||||
label: '排序',
|
||||
width: '120px',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
field: 'disabled',
|
||||
label: '是否禁用',
|
||||
width: '120px',
|
||||
show: true,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.disabled} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '200px',
|
||||
label: '操作',
|
||||
show: true,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" link size="small" onClick={() => editAction(row)}>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton type="primary" link size="small" onClick={() => addSonAction(row)}>
|
||||
添加子部门
|
||||
</ElButton>
|
||||
<ElButton
|
||||
type="danger"
|
||||
loading={delLoading.value}
|
||||
link
|
||||
size="small"
|
||||
onClick={() => delData(row)}
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const delLoading = ref(false)
|
||||
|
||||
const delData = async (row: any) => {
|
||||
delLoading.value = true
|
||||
await delList(true, [row.id]).finally(() => {
|
||||
delLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
|
||||
const currentRow = ref()
|
||||
const parentId = ref(undefined)
|
||||
const actionType = ref('')
|
||||
|
||||
const writeRef = ref<ComponentRef<typeof Write>>()
|
||||
|
||||
const saveLoading = ref(false)
|
||||
|
||||
const editAction = (row: any) => {
|
||||
dialogTitle.value = '编辑'
|
||||
actionType.value = 'edit'
|
||||
currentRow.value = row
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const addAction = () => {
|
||||
dialogTitle.value = '新增'
|
||||
actionType.value = 'add'
|
||||
currentRow.value = undefined
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const addSonAction = (row: any) => {
|
||||
dialogTitle.value = '添加子部门'
|
||||
actionType.value = 'addSon'
|
||||
parentId.value = row.id
|
||||
currentRow.value = undefined
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
const write = unref(writeRef)
|
||||
const formData = await write?.submit()
|
||||
if (formData) {
|
||||
saveLoading.value = true
|
||||
try {
|
||||
const res = ref({})
|
||||
if (actionType.value === 'add' || actionType.value === 'addSon') {
|
||||
res.value = await addDeptListApi(formData)
|
||||
if (res.value) {
|
||||
parentId.value = undefined
|
||||
dialogVisible.value = false
|
||||
getList()
|
||||
}
|
||||
} else if (actionType.value === 'edit') {
|
||||
res.value = await putDeptListApi(formData)
|
||||
if (res.value) {
|
||||
dialogVisible.value = false
|
||||
getList()
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
saveLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:columns="tableColumns"
|
||||
showAction
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:data="dataList"
|
||||
:loading="loading"
|
||||
@register="tableRegister"
|
||||
@refresh="getList"
|
||||
>
|
||||
<template #toolbar>
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5">
|
||||
<ElButton type="primary" @click="addAction">新增部门</ElButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<Write ref="writeRef" :current-row="currentRow" :parent-id="parentId" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton v-if="actionType !== 'detail'" type="primary" :loading="saveLoading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
167
kinit-admin/src/views/Vadmin/Auth/Dept/components/Write.vue
Normal file
167
kinit-admin/src/views/Vadmin/Auth/Dept/components/Write.vue
Normal file
@ -0,0 +1,167 @@
|
||||
<script setup lang="tsx">
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { PropType, reactive, watch } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { getDeptTreeOptionsApi } from '@/api/vadmin/auth/dept'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const props = defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => null
|
||||
},
|
||||
parentId: propTypes.number.def(undefined)
|
||||
})
|
||||
|
||||
const formSchema = reactive<FormSchema[]>([
|
||||
{
|
||||
field: 'parent_id',
|
||||
label: '上级部门',
|
||||
colProps: {
|
||||
span: 24
|
||||
},
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
checkStrictly: true,
|
||||
placeholder: '请选择上级部门',
|
||||
nodeKey: 'value',
|
||||
defaultExpandAll: true
|
||||
},
|
||||
optionApi: async () => {
|
||||
const res = await getDeptTreeOptionsApi()
|
||||
return res.data
|
||||
},
|
||||
value: props.parentId
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '部门名称',
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'dept_key',
|
||||
label: '部门标识',
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'owner',
|
||||
label: '负责人',
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
label: '联系电话',
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: '邮箱',
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'desc',
|
||||
label: '描述',
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'order',
|
||||
label: '显示排序',
|
||||
component: 'InputNumber',
|
||||
colProps: {
|
||||
span: 12
|
||||
},
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'disabled',
|
||||
label: '是否禁用',
|
||||
colProps: {
|
||||
span: 12
|
||||
},
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: '正常',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
label: '停用',
|
||||
value: true
|
||||
}
|
||||
]
|
||||
},
|
||||
value: false
|
||||
}
|
||||
])
|
||||
|
||||
const rules = reactive({
|
||||
name: [required()],
|
||||
dept_key: [required()],
|
||||
disabled: [required()],
|
||||
order: [required()]
|
||||
})
|
||||
|
||||
const { formRegister, formMethods } = useForm()
|
||||
const { setValues, getFormData, getElFormExpose } = formMethods
|
||||
|
||||
const submit = async () => {
|
||||
const elForm = await getElFormExpose()
|
||||
const valid = await elForm?.validate()
|
||||
if (valid) {
|
||||
const formData = await getFormData()
|
||||
return formData
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.currentRow,
|
||||
(currentRow) => {
|
||||
if (!currentRow) return
|
||||
setValues(currentRow)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
defineExpose({
|
||||
submit
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form :rules="rules" @register="formRegister" :schema="formSchema" :labelWidth="100" />
|
||||
</template>
|
@ -15,7 +15,10 @@ import { Search } from '@/components/Search'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import Write from './components/Write.vue'
|
||||
import AuthManage from './components/AuthManage.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { DictDetail, selectDictLabel } from '@/utils/dict'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
|
||||
defineOptions({
|
||||
name: 'AuthRole'
|
||||
@ -45,12 +48,22 @@ const { tableRegister, tableState, tableMethods } = useTable({
|
||||
const { dataList, loading, total, pageSize, currentPage } = tableState
|
||||
const { getList, delList } = tableMethods
|
||||
|
||||
let dataRangeOptions = ref<DictDetail[]>([])
|
||||
|
||||
const getOptions = async () => {
|
||||
const dictStore = useDictStore()
|
||||
const dictOptions = await dictStore.getDictObj(['sys_vadmin_data_range'])
|
||||
dataRangeOptions.value = dictOptions.sys_vadmin_data_range
|
||||
}
|
||||
|
||||
getOptions()
|
||||
|
||||
const tableColumns = reactive<TableColumn[]>([
|
||||
{
|
||||
field: 'id',
|
||||
label: '角色编号',
|
||||
show: true,
|
||||
disabled: true
|
||||
show: false,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
@ -63,6 +76,21 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
label: '权限字符',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
field: 'data_range',
|
||||
label: '数据范围',
|
||||
show: true,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<div>{selectDictLabel(unref(dataRangeOptions), row.data_range.toString())}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'order',
|
||||
label: '显示顺序',
|
||||
@ -92,7 +120,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<ElSwitch value={!row.is_admin} disabled />
|
||||
<ElSwitch value={row.is_admin} disabled />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -105,7 +133,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '150px',
|
||||
width: '170px',
|
||||
label: '操作',
|
||||
show: true,
|
||||
slots: {
|
||||
@ -125,6 +153,15 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-show={row.id !== 1}
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
onClick={() => authManageActive(row)}
|
||||
>
|
||||
权限管理
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-show={row.id !== 1}
|
||||
type="danger"
|
||||
@ -204,6 +241,18 @@ const delData = async (row: any) => {
|
||||
})
|
||||
}
|
||||
|
||||
const authManageRef = ref<ComponentRef<typeof AuthManage>>()
|
||||
|
||||
// 权限管理
|
||||
const authManageActive = async (row: any) => {
|
||||
const res = await getRoleApi(row.id)
|
||||
if (res) {
|
||||
res.data.data_range = res.data.data_range.toString()
|
||||
currentRow.value = res.data
|
||||
authManageRef.value?.openDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
|
||||
@ -217,15 +266,16 @@ const saveLoading = ref(false)
|
||||
const editAction = async (row: any) => {
|
||||
const res = await getRoleApi(row.id)
|
||||
if (res) {
|
||||
dialogTitle.value = '编辑'
|
||||
dialogTitle.value = '编辑角色'
|
||||
actionType.value = 'edit'
|
||||
res.data.data_range = res.data.data_range.toString()
|
||||
currentRow.value = res.data
|
||||
dialogVisible.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const addAction = () => {
|
||||
dialogTitle.value = '新增'
|
||||
dialogTitle.value = '新增角色'
|
||||
actionType.value = 'add'
|
||||
currentRow.value = undefined
|
||||
dialogVisible.value = true
|
||||
@ -298,4 +348,6 @@ const save = async () => {
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<AuthManage ref="authManageRef" :current-row="currentRow" @get-list="getList" />
|
||||
</template>
|
||||
|
255
kinit-admin/src/views/Vadmin/Auth/Role/components/AuthManage.vue
Normal file
255
kinit-admin/src/views/Vadmin/Auth/Role/components/AuthManage.vue
Normal file
@ -0,0 +1,255 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ElDrawer,
|
||||
ElButton,
|
||||
ElDivider,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElTree,
|
||||
ElContainer,
|
||||
ElHeader,
|
||||
ElAside,
|
||||
ElMain,
|
||||
ElMessage
|
||||
} from 'element-plus'
|
||||
import { ref, nextTick, unref, PropType, watch } from 'vue'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import { getMenuRoleTreeOptionsApi } from '@/api/vadmin/auth/menu'
|
||||
import { getDeptUserTreeOptionsApi } from '@/api/vadmin/auth/dept'
|
||||
import { eachTree } from '@/utils/tree'
|
||||
import { isEmptyVal } from '@/utils/is'
|
||||
import { putRoleListApi } from '@/api/vadmin/auth/role'
|
||||
|
||||
const props = defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['getList'])
|
||||
|
||||
const data = ref({} as Recordable)
|
||||
|
||||
watch(
|
||||
() => props.currentRow,
|
||||
(currentRow) => {
|
||||
if (!currentRow) return
|
||||
data.value = JSON.parse(JSON.stringify(currentRow))
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
const drawerVisible = ref(false)
|
||||
|
||||
const dataRangeOptions = ref()
|
||||
const getOptions = async () => {
|
||||
const dictStore = useDictStore()
|
||||
const dictOptions = await dictStore.getDictObj(['sys_vadmin_data_range'])
|
||||
dataRangeOptions.value = dictOptions.sys_vadmin_data_range
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
}
|
||||
|
||||
// 获取部门树
|
||||
let deptTreeData = ref([] as any[])
|
||||
const deptTreeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const getDeptTreeOptions = async () => {
|
||||
const res = await getDeptUserTreeOptionsApi()
|
||||
if (res) {
|
||||
deptTreeData.value = res.data
|
||||
await nextTick()
|
||||
if (props.currentRow) {
|
||||
const dept_ids: number[] = props.currentRow.depts.map((item) => item.id)
|
||||
const checked: number[] = []
|
||||
// 递归按顺序添加选中的菜单项,用于处理半选状态的菜单项
|
||||
eachTree(res.data, (v) => {
|
||||
if (dept_ids.includes(v.value)) {
|
||||
checked.push(v.value)
|
||||
}
|
||||
})
|
||||
for (const item of checked) {
|
||||
unref(deptTreeRef)?.setChecked(item, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取菜单树
|
||||
let menuTreeData = ref([] as any[])
|
||||
const menuTreeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const getMenuRoleTreeOptions = async () => {
|
||||
const res = await getMenuRoleTreeOptionsApi()
|
||||
if (res) {
|
||||
menuTreeData.value = res.data
|
||||
await nextTick()
|
||||
if (props.currentRow) {
|
||||
const menu_ids: number[] = props.currentRow.menus.map((item) => item.id)
|
||||
const checked: number[] = []
|
||||
// 递归按顺序添加选中的菜单项,用于处理半选状态的菜单项
|
||||
eachTree(res.data, (v) => {
|
||||
if (menu_ids.includes(v.value)) {
|
||||
checked.push(v.value)
|
||||
}
|
||||
})
|
||||
for (const item of checked) {
|
||||
unref(menuTreeRef)?.setChecked(item, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const submit = async () => {
|
||||
if (loading.value) return
|
||||
if (isEmptyVal(data.value.data_range)) {
|
||||
ElMessage.error('数据范围选择项不能为空!')
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
const menu_ids = [
|
||||
...(unref(menuTreeRef)?.getCheckedKeys() || []),
|
||||
...(unref(menuTreeRef)?.getHalfCheckedKeys() || [])
|
||||
]
|
||||
data.value.menu_ids = menu_ids
|
||||
data.value.dept_ids = unref(deptTreeRef)?.getCheckedKeys()
|
||||
try {
|
||||
const res = await putRoleListApi(data.value)
|
||||
if (res) {
|
||||
loading.value = false
|
||||
ElMessage.success('保存成功')
|
||||
closeDrawer()
|
||||
emit('getList')
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const openDrawer = () => {
|
||||
drawerVisible.value = true
|
||||
getMenuRoleTreeOptions()
|
||||
getDeptTreeOptions()
|
||||
}
|
||||
|
||||
const closeDrawer = () => {
|
||||
drawerVisible.value = false
|
||||
data.value = {}
|
||||
unref(menuTreeRef)?.setCheckedKeys([])
|
||||
unref(deptTreeRef)?.setCheckedKeys([])
|
||||
}
|
||||
|
||||
getOptions()
|
||||
|
||||
defineExpose({
|
||||
openDrawer
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="auth-manage-main-view">
|
||||
<ElDrawer v-model="drawerVisible" :with-header="false" :size="1000" :before-close="closeDrawer">
|
||||
<ElContainer>
|
||||
<ElHeader>
|
||||
<div class="flex justify-between pt-[20px] pb-[20px]">
|
||||
<span>权限管理</span>
|
||||
<span @click="closeDrawer" class="flex cursor-pointer">
|
||||
<Icon icon="iconamoon:close-thin" :size="23" />
|
||||
</span>
|
||||
</div>
|
||||
</ElHeader>
|
||||
<ElDivider />
|
||||
<div class="h-12 flex justify-between mt-3 mr-3 ml-5">
|
||||
<div class="mt-1 text-[#909399]">
|
||||
<span>角色名称:{{ data.name }}</span>
|
||||
</div>
|
||||
<ElButton type="primary" :loading="loading" @click="submit">保存</ElButton>
|
||||
</div>
|
||||
<ElDivider />
|
||||
<ElContainer>
|
||||
<ElAside width="450px">
|
||||
<div class="border-r-1 border-r-[#f0f0f0] b-r-solid h-[100%] p-[20px] box-border">
|
||||
<div>
|
||||
<div class="flex items-center">
|
||||
<div class="yxt-divider"></div>
|
||||
<span>数据权限</span>
|
||||
</div>
|
||||
<div class="ml-4 mt-3">
|
||||
<ElSelect v-model="data.data_range" placeholder="请选择数据范围">
|
||||
<ElOption
|
||||
v-for="item in dataRangeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
<div
|
||||
v-if="data.data_range === '4'"
|
||||
class="mt-3 max-h-[65vh] b-1 b-solid b-[#e5e7eb] p-10px overflow-auto"
|
||||
>
|
||||
<ElTree
|
||||
ref="deptTreeRef"
|
||||
:data="deptTreeData"
|
||||
show-checkbox
|
||||
node-key="value"
|
||||
:props="defaultProps"
|
||||
:default-expand-all="true"
|
||||
:check-strictly="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElAside>
|
||||
<ElMain>
|
||||
<div class="flex items-center">
|
||||
<div class="yxt-divider"></div>
|
||||
<span>菜单权限</span>
|
||||
</div>
|
||||
<div class="mt-5 max-h-[70vh] b-1 b-solid b-[#e5e7eb] p-10px overflow-auto box-border">
|
||||
<ElTree
|
||||
ref="menuTreeRef"
|
||||
:data="menuTreeData"
|
||||
show-checkbox
|
||||
node-key="value"
|
||||
:props="defaultProps"
|
||||
:default-expand-all="true"
|
||||
:check-strictly="false"
|
||||
/>
|
||||
</div>
|
||||
</ElMain>
|
||||
</ElContainer>
|
||||
</ElContainer>
|
||||
<ElDivider />
|
||||
|
||||
<!-- <ElDivider /> -->
|
||||
</ElDrawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.auth-manage-main-view .el-drawer .el-drawer__body {
|
||||
padding: 0;
|
||||
}
|
||||
.auth-manage-main-view .el-divider.el-divider--horizontal {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="less">
|
||||
.yxt-divider {
|
||||
background: #409eff;
|
||||
width: 8px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
@ -1,11 +1,11 @@
|
||||
<script setup lang="tsx">
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { PropType, nextTick, reactive, ref, unref, watch } from 'vue'
|
||||
import { PropType, reactive, watch } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { ElCheckbox, ElTree } from 'element-plus'
|
||||
import { getMenuRoleTreeOptionsApi } from '@/api/vadmin/auth/menu'
|
||||
import { eachTree } from '@/utils/tree'
|
||||
// import { ElTree } from 'element-plus'
|
||||
// import { getMenuRoleTreeOptionsApi } from '@/api/vadmin/auth/menu'
|
||||
// import { eachTree } from '@/utils/tree'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
@ -16,39 +16,38 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
let treeData = ref([] as any[])
|
||||
// let treeData = ref([] as any[])
|
||||
// const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
// const getMenuRoleTreeOptions = async () => {
|
||||
// const res = await getMenuRoleTreeOptionsApi()
|
||||
// if (res) {
|
||||
// treeData.value = res.data
|
||||
// await nextTick()
|
||||
// if (props.currentRow) {
|
||||
// const menu_ids: number[] = props.currentRow.menus.map((item) => item.id)
|
||||
// const checked: number[] = []
|
||||
// // 递归按顺序添加选中的菜单项,用于处理半选状态的菜单项
|
||||
// eachTree(res.data, (v) => {
|
||||
// if (menu_ids.includes(v.value)) {
|
||||
// checked.push(v.value)
|
||||
// }
|
||||
// })
|
||||
// for (const item of checked) {
|
||||
// unref(treeRef)?.setChecked(item, true, false)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
const getMenuRoleTreeOptions = async () => {
|
||||
const res = await getMenuRoleTreeOptionsApi()
|
||||
if (res) {
|
||||
treeData.value = res.data
|
||||
await nextTick()
|
||||
if (props.currentRow) {
|
||||
const menu_ids: number[] = props.currentRow.menus.map((item) => item.id)
|
||||
const checked: number[] = []
|
||||
// 递归按顺序添加选中的菜单项,用于处理半选状态的菜单项
|
||||
eachTree(res.data, (v) => {
|
||||
if (menu_ids.includes(v.value)) {
|
||||
checked.push(v.value)
|
||||
}
|
||||
})
|
||||
for (const item of checked) {
|
||||
unref(treeRef)?.setChecked(item, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// const defaultProps = {
|
||||
// children: 'children',
|
||||
// label: 'label'
|
||||
// }
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
}
|
||||
|
||||
let selectAll = ref(false)
|
||||
let defaultExpandAll = ref(true)
|
||||
let checkStrictly = ref(true)
|
||||
// let selectAll = ref(false)
|
||||
// let defaultExpandAll = ref(true)
|
||||
// let checkStrictly = ref(true)
|
||||
|
||||
// 获取所有节点的key
|
||||
const getTreeNodeKeys = (nodes: Recordable[]): number[] => {
|
||||
@ -62,19 +61,19 @@ const getTreeNodeKeys = (nodes: Recordable[]): number[] => {
|
||||
return keys
|
||||
}
|
||||
|
||||
// 展开/折叠
|
||||
const handleCheckedTreeExpand = (value: boolean) => {
|
||||
defaultExpandAll.value = value
|
||||
for (let i = 0; i < treeData.value.length; i++) {
|
||||
treeRef.value!.store.nodesMap[treeData.value[i].value].expanded = value
|
||||
}
|
||||
}
|
||||
// // 展开/折叠
|
||||
// const handleCheckedTreeExpand = (value: boolean) => {
|
||||
// defaultExpandAll.value = value
|
||||
// for (let i = 0; i < treeData.value.length; i++) {
|
||||
// treeRef.value!.store.nodesMap[treeData.value[i].value].expanded = value
|
||||
// }
|
||||
// }
|
||||
|
||||
//全选/全不选
|
||||
const handleCheckedTreeNodeAll = (value: boolean) => {
|
||||
selectAll.value = value
|
||||
treeRef.value!.setCheckedKeys(value ? getTreeNodeKeys(treeData.value) : [])
|
||||
}
|
||||
// //全选/全不选
|
||||
// const handleCheckedTreeNodeAll = (value: boolean) => {
|
||||
// selectAll.value = value
|
||||
// treeRef.value!.setCheckedKeys(value ? getTreeNodeKeys(treeData.value) : [])
|
||||
// }
|
||||
|
||||
const formSchema = reactive<FormSchema[]>([
|
||||
{
|
||||
@ -156,57 +155,64 @@ const formSchema = reactive<FormSchema[]>([
|
||||
},
|
||||
{
|
||||
field: 'desc',
|
||||
label: '描述',
|
||||
colProps: {
|
||||
span: 12
|
||||
},
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'menu_ids',
|
||||
label: '菜单权限',
|
||||
label: '角色描述',
|
||||
colProps: {
|
||||
span: 24
|
||||
},
|
||||
formItemProps: {
|
||||
slots: {
|
||||
default: () => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div>
|
||||
<ElCheckbox
|
||||
modelValue={defaultExpandAll.value}
|
||||
onChange={handleCheckedTreeExpand}
|
||||
label="展开/折叠"
|
||||
size="large"
|
||||
/>
|
||||
<ElCheckbox
|
||||
modelValue={selectAll.value}
|
||||
onChange={handleCheckedTreeNodeAll}
|
||||
label="全选/全不选"
|
||||
size="large"
|
||||
/>
|
||||
<ElCheckbox v-model={checkStrictly.value} label="父子联动" size="large" />
|
||||
</div>
|
||||
<div class="max-h-420px b-1 b-solid b-[#e5e7eb] p-10px overflow-auto">
|
||||
<ElTree
|
||||
ref={treeRef}
|
||||
data={treeData.value}
|
||||
show-checkbox
|
||||
node-key="value"
|
||||
props={defaultProps}
|
||||
default-expand-all={defaultExpandAll.value}
|
||||
check-strictly={!checkStrictly.value}
|
||||
></ElTree>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
rows: 4,
|
||||
type: 'textarea',
|
||||
style: {
|
||||
width: '600px'
|
||||
}
|
||||
}
|
||||
}
|
||||
// {
|
||||
// field: 'menu_ids',
|
||||
// label: '菜单权限',
|
||||
// colProps: {
|
||||
// span: 24
|
||||
// },
|
||||
// formItemProps: {
|
||||
// slots: {
|
||||
// default: () => {
|
||||
// return (
|
||||
// <>
|
||||
// <div>
|
||||
// <div>
|
||||
// <ElCheckbox
|
||||
// modelValue={defaultExpandAll.value}
|
||||
// onChange={handleCheckedTreeExpand}
|
||||
// label="展开/折叠"
|
||||
// size="large"
|
||||
// />
|
||||
// <ElCheckbox
|
||||
// modelValue={selectAll.value}
|
||||
// onChange={handleCheckedTreeNodeAll}
|
||||
// label="全选/全不选"
|
||||
// size="large"
|
||||
// />
|
||||
// <ElCheckbox v-model={checkStrictly.value} label="父子联动" size="large" />
|
||||
// </div>
|
||||
// <div class="max-h-420px b-1 b-solid b-[#e5e7eb] p-10px overflow-auto">
|
||||
// <ElTree
|
||||
// ref={treeRef}
|
||||
// data={treeData.value}
|
||||
// show-checkbox
|
||||
// node-key="value"
|
||||
// props={defaultProps}
|
||||
// default-expand-all={defaultExpandAll.value}
|
||||
// check-strictly={!checkStrictly.value}
|
||||
// ></ElTree>
|
||||
// </div>
|
||||
// </div>
|
||||
// </>
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
])
|
||||
|
||||
const rules = reactive({
|
||||
@ -223,10 +229,10 @@ const submit = async () => {
|
||||
const valid = await elForm?.validate()
|
||||
if (valid) {
|
||||
const formData = await getFormData()
|
||||
formData.menu_ids = [
|
||||
...(unref(treeRef)?.getCheckedKeys() || []),
|
||||
...(unref(treeRef)?.getHalfCheckedKeys() || [])
|
||||
]
|
||||
// formData.menu_ids = [
|
||||
// ...(unref(treeRef)?.getCheckedKeys() || []),
|
||||
// ...(unref(treeRef)?.getHalfCheckedKeys() || [])
|
||||
// ]
|
||||
return formData
|
||||
}
|
||||
}
|
||||
@ -243,7 +249,7 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
getMenuRoleTreeOptions()
|
||||
// getMenuRoleTreeOptions()
|
||||
|
||||
defineExpose({
|
||||
submit
|
||||
|
@ -84,8 +84,8 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
field: 'id',
|
||||
label: '用户编号',
|
||||
width: '100px',
|
||||
show: true,
|
||||
disabled: true
|
||||
show: false,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
@ -113,7 +113,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
{
|
||||
field: 'gender',
|
||||
label: '性别',
|
||||
show: true,
|
||||
show: false,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
const row = data.row
|
||||
@ -140,10 +140,25 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'depts',
|
||||
label: '部门',
|
||||
show: true,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
const row = data.row
|
||||
return (
|
||||
<>
|
||||
<div class="text-truncate">{row.depts.map((item) => item.name).join()}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'is_active',
|
||||
label: '是否可用',
|
||||
show: true,
|
||||
show: false,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
const row = data.row
|
||||
@ -180,7 +195,7 @@ const tableColumns = reactive<TableColumn[]>([
|
||||
field: 'create_datetime',
|
||||
label: '创建时间',
|
||||
width: '190px',
|
||||
show: true
|
||||
show: false
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
@ -325,6 +340,7 @@ const editAction = async (row: any) => {
|
||||
if (res) {
|
||||
dialogTitle.value = '编辑用户'
|
||||
res.data.role_ids = res.data.roles.map((item: any) => item.id)
|
||||
res.data.dept_ids = res.data.depts.map((item: any) => item.id)
|
||||
actionType.value = 'edit'
|
||||
currentRow.value = res.data
|
||||
dialogVisible.value = true
|
||||
|
@ -4,6 +4,7 @@ import { useForm } from '@/hooks/web/useForm'
|
||||
import { PropType, reactive, watch } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { getRoleOptionsApi } from '@/api/vadmin/auth/role'
|
||||
import { getDeptUserTreeOptionsApi } from '@/api/vadmin/auth/dept'
|
||||
|
||||
const { required, isTelephone, isEmail } = useValidator()
|
||||
|
||||
@ -173,6 +174,28 @@ const formSchema = reactive<FormSchema[]>([
|
||||
},
|
||||
value: [],
|
||||
ifshow: (values) => values.is_staff
|
||||
},
|
||||
{
|
||||
field: 'dept_ids',
|
||||
label: '部门',
|
||||
colProps: {
|
||||
span: 24
|
||||
},
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
multiple: true,
|
||||
checkStrictly: true,
|
||||
defaultExpandAll: true
|
||||
},
|
||||
optionApi: async () => {
|
||||
const res = await getDeptUserTreeOptionsApi()
|
||||
return res.data
|
||||
},
|
||||
value: [],
|
||||
ifshow: (values) => values.is_staff
|
||||
}
|
||||
])
|
||||
|
||||
@ -181,6 +204,7 @@ const rules = reactive({
|
||||
is_active: [required()],
|
||||
is_staff: [required()],
|
||||
role_ids: [required()],
|
||||
dept_ids: [required()],
|
||||
telephone: [required(), { validator: isTelephone, trigger: 'blur' }],
|
||||
email: [{ validator: isEmail, trigger: 'blur' }]
|
||||
})
|
||||
|
@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer
|
||||
"""
|
||||
系统版本
|
||||
"""
|
||||
VERSION = "3.4.2"
|
||||
VERSION = "3.5.0"
|
||||
|
||||
"""安全警告: 不要在生产中打开调试运行!"""
|
||||
DEBUG = False
|
||||
|
@ -50,6 +50,33 @@ class UserDal(DalBase):
|
||||
self.model = models.VadminUser
|
||||
self.schema = schemas.UserSimpleOut
|
||||
|
||||
async def recursion_get_dept_ids(
|
||||
self,
|
||||
user: models.VadminUser,
|
||||
depts: list[models.VadminDept] = None,
|
||||
dept_ids: list[int] = None
|
||||
) -> list:
|
||||
"""
|
||||
递归获取所有关联部门 id
|
||||
:param user:
|
||||
:param depts: 所有部门实例
|
||||
:param dept_ids: 父级部门 id 列表
|
||||
:return:
|
||||
"""
|
||||
if not depts:
|
||||
depts = await DeptDal(self.db).get_datas(limit=0, v_return_objs=True)
|
||||
result = []
|
||||
for i in user.depts:
|
||||
result.append(i.id)
|
||||
result.extend(await self.recursion_get_dept_ids(user, depts, result))
|
||||
return list(set(result))
|
||||
elif dept_ids:
|
||||
result = [i.id for i in filter(lambda item: item.parent_id in dept_ids, depts)]
|
||||
result.extend(await self.recursion_get_dept_ids(user, depts, result))
|
||||
return result
|
||||
else:
|
||||
return []
|
||||
|
||||
async def update_login_info(self, user: models.VadminUser, last_ip: str) -> None:
|
||||
"""
|
||||
更新当前登录信息
|
||||
@ -82,11 +109,15 @@ class UserDal(DalBase):
|
||||
password = data.telephone[5:12] if settings.DEFAULT_PASSWORD == "0" else settings.DEFAULT_PASSWORD
|
||||
data.password = self.model.get_password_hash(password)
|
||||
data.avatar = data.avatar if data.avatar else settings.DEFAULT_AVATAR
|
||||
obj = self.model(**data.model_dump(exclude={'role_ids'}))
|
||||
obj = self.model(**data.model_dump(exclude={'role_ids', "dept_ids"}))
|
||||
if data.role_ids:
|
||||
roles = await RoleDal(self.db).get_datas(limit=0, id=("in", data.role_ids), v_return_objs=True)
|
||||
for role in roles:
|
||||
obj.roles.add(role)
|
||||
if data.dept_ids:
|
||||
depts = await DeptDal(self.db).get_datas(limit=0, id=("in", data.dept_ids), v_return_objs=True)
|
||||
for dept in depts:
|
||||
obj.depts.add(dept)
|
||||
await self.flush(obj)
|
||||
return await self.out_dict(obj, v_options, v_return_obj, v_schema)
|
||||
|
||||
@ -118,6 +149,14 @@ class UserDal(DalBase):
|
||||
for role in roles:
|
||||
obj.roles.add(role)
|
||||
continue
|
||||
elif key == "dept_ids":
|
||||
if value:
|
||||
depts = await DeptDal(self.db).get_datas(limit=0, id=("in", value), v_return_objs=True)
|
||||
if obj.depts:
|
||||
obj.depts.clear()
|
||||
for dept in depts:
|
||||
obj.depts.add(dept)
|
||||
continue
|
||||
setattr(obj, key, value)
|
||||
await self.flush(obj)
|
||||
return await self.out_dict(obj, None, v_return_obj, v_schema)
|
||||
@ -395,11 +434,15 @@ class RoleDal(DalBase):
|
||||
:param v_schema:
|
||||
:return:
|
||||
"""
|
||||
obj = self.model(**data.model_dump(exclude={'menu_ids'}))
|
||||
obj = self.model(**data.model_dump(exclude={'menu_ids', 'dept_ids'}))
|
||||
if data.menu_ids:
|
||||
menus = await MenuDal(db=self.db).get_datas(limit=0, id=("in", data.menu_ids), v_return_objs=True)
|
||||
for menu in menus:
|
||||
obj.menus.add(menu)
|
||||
if data.dept_ids:
|
||||
depts = await DeptDal(db=self.db).get_datas(limit=0, id=("in", data.dept_ids), v_return_objs=True)
|
||||
for dept in depts:
|
||||
obj.depts.add(dept)
|
||||
await self.flush(obj)
|
||||
return await self.out_dict(obj, v_options, v_return_obj, v_schema)
|
||||
|
||||
@ -420,7 +463,7 @@ class RoleDal(DalBase):
|
||||
:param v_schema:
|
||||
:return:
|
||||
"""
|
||||
obj = await self.get_data(data_id, v_options=[joinedload(self.model.menus)])
|
||||
obj = await self.get_data(data_id, v_options=[joinedload(self.model.menus), joinedload(self.model.depts)])
|
||||
obj_dict = jsonable_encoder(data)
|
||||
for key, value in obj_dict.items():
|
||||
if key == "menu_ids":
|
||||
@ -431,6 +474,14 @@ class RoleDal(DalBase):
|
||||
for menu in menus:
|
||||
obj.menus.add(menu)
|
||||
continue
|
||||
elif key == "dept_ids":
|
||||
if value:
|
||||
depts = await DeptDal(db=self.db).get_datas(limit=0, id=("in", value), v_return_objs=True)
|
||||
if obj.depts:
|
||||
obj.depts.clear()
|
||||
for dept in depts:
|
||||
obj.depts.add(dept)
|
||||
continue
|
||||
setattr(obj, key, value)
|
||||
await self.flush(obj)
|
||||
return await self.out_dict(obj, None, v_return_obj, v_schema)
|
||||
@ -559,7 +610,7 @@ class MenuDal(DalBase):
|
||||
"""
|
||||
data = []
|
||||
for root in nodes:
|
||||
router = schemas.TreeListOut.model_validate(root)
|
||||
router = schemas.MenuTreeListOut.model_validate(root)
|
||||
if root.menu_type == "0" or root.menu_type == "1":
|
||||
sons = filter(lambda i: i.parent_id == root.id, menus)
|
||||
router.children = self.generate_tree_list(menus, sons)
|
||||
@ -611,3 +662,103 @@ class MenuDal(DalBase):
|
||||
raise CustomException("无法删除存在角色关联的菜单", code=400)
|
||||
await super(MenuDal, self).delete_datas(ids, v_soft, **kwargs)
|
||||
|
||||
|
||||
class DeptDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(DeptDal, self).__init__()
|
||||
self.db = db
|
||||
self.model = models.VadminDept
|
||||
self.schema = schemas.DeptSimpleOut
|
||||
|
||||
async def get_tree_list(self, mode: int) -> list:
|
||||
"""
|
||||
1:获取部门树列表
|
||||
2:获取部门树选择项,添加/修改部门时使用
|
||||
3:获取部门树列表,用户添加部门权限时使用
|
||||
:param mode:
|
||||
:return:
|
||||
"""
|
||||
if mode == 3:
|
||||
sql = select(self.model).where(self.model.disabled == 0, self.model.is_delete == false())
|
||||
else:
|
||||
sql = select(self.model).where(self.model.is_delete == false())
|
||||
queryset = await self.db.scalars(sql)
|
||||
datas = list(queryset.all())
|
||||
roots = filter(lambda i: not i.parent_id, datas)
|
||||
if mode == 1:
|
||||
menus = self.generate_tree_list(datas, roots)
|
||||
elif mode == 2 or mode == 3:
|
||||
menus = self.generate_tree_options(datas, roots)
|
||||
else:
|
||||
raise CustomException("获取部门失败,无可用选项", code=400)
|
||||
return self.dept_order(menus)
|
||||
|
||||
def generate_tree_list(self, depts: list[models.VadminDept], nodes: filter) -> list:
|
||||
"""
|
||||
生成部门树列表
|
||||
:param depts: 总部门列表
|
||||
:param nodes: 每层节点部门列表
|
||||
:return:
|
||||
"""
|
||||
data = []
|
||||
for root in nodes:
|
||||
router = schemas.DeptTreeListOut.model_validate(root)
|
||||
sons = filter(lambda i: i.parent_id == root.id, depts)
|
||||
router.children = self.generate_tree_list(depts, sons)
|
||||
data.append(router.model_dump())
|
||||
return data
|
||||
|
||||
def generate_tree_options(self, depts: list[models.VadminDept], nodes: filter) -> list:
|
||||
"""
|
||||
生成部门树选择项
|
||||
:param depts: 总部门列表
|
||||
:param nodes: 每层节点部门列表
|
||||
:return:
|
||||
"""
|
||||
data = []
|
||||
for root in nodes:
|
||||
router = {"value": root.id, "label": root.name, "order": root.order}
|
||||
sons = filter(lambda i: i.parent_id == root.id, depts)
|
||||
router["children"] = self.generate_tree_options(depts, sons)
|
||||
data.append(router)
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def dept_order(cls, datas: list, order: str = "order", children: str = "children") -> list:
|
||||
"""
|
||||
部门排序
|
||||
:param datas:
|
||||
:param order:
|
||||
:param children:
|
||||
:return:
|
||||
"""
|
||||
result = sorted(datas, key=lambda dept: dept[order])
|
||||
for item in result:
|
||||
if item[children]:
|
||||
item[children] = sorted(item[children], key=lambda dept: dept[order])
|
||||
return result
|
||||
|
||||
|
||||
class TestDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(TestDal, self).__init__(db, models.VadminUser, schemas.UserSimpleOut)
|
||||
|
||||
async def test(self):
|
||||
# print("-----------------------开始------------------------")
|
||||
options = [joinedload(self.model.roles)]
|
||||
v_join = [[self.model.roles]]
|
||||
v_where = [self.model.id == 1, models.VadminRole.id == 1]
|
||||
v_start_sql = select(self.model)
|
||||
result, count = await self.get_datas(
|
||||
v_start_sql=v_start_sql,
|
||||
v_join=v_join,
|
||||
v_options=options,
|
||||
v_where=v_where,
|
||||
v_return_count=True
|
||||
)
|
||||
if result:
|
||||
print(result)
|
||||
print(count)
|
||||
# print("-----------------------结束------------------------")
|
||||
|
@ -7,7 +7,8 @@
|
||||
# @desc : 简要说明
|
||||
|
||||
|
||||
from .m2m import vadmin_auth_user_roles, vadmin_auth_role_menus
|
||||
from .m2m import vadmin_auth_user_roles, vadmin_auth_role_menus, vadmin_auth_user_depts, vadmin_auth_role_depts
|
||||
from .menu import VadminMenu
|
||||
from .role import VadminRole
|
||||
from .user import VadminUser
|
||||
from .dept import VadminDept
|
||||
|
31
kinit-api/apps/vadmin/auth/models/dept.py
Normal file
31
kinit-api/apps/vadmin/auth/models/dept.py
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/10/23 13:41
|
||||
# @File : dept.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 部门模型
|
||||
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, Integer, ForeignKey
|
||||
|
||||
|
||||
class VadminDept(BaseModel):
|
||||
__tablename__ = "vadmin_auth_dept"
|
||||
__table_args__ = ({'comment': '部门表'})
|
||||
|
||||
name: Mapped[str] = mapped_column(String(50), index=True, nullable=False, comment="部门名称")
|
||||
dept_key: Mapped[str] = mapped_column(String(50), index=True, nullable=False, comment="部门标识")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
order: Mapped[int | None] = mapped_column(Integer, comment="显示排序")
|
||||
desc: Mapped[str | None] = mapped_column(String(255), comment="描述")
|
||||
owner: Mapped[str | None] = mapped_column(String(255), comment="负责人")
|
||||
phone: Mapped[str | None] = mapped_column(String(255), comment="联系电话")
|
||||
email: Mapped[str | None] = mapped_column(String(255), comment="邮箱")
|
||||
|
||||
parent_id: Mapped[int | None] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_auth_dept.id", ondelete='CASCADE'),
|
||||
comment="上级部门"
|
||||
)
|
@ -25,3 +25,17 @@ vadmin_auth_role_menus = Table(
|
||||
Column("menu_id", Integer, ForeignKey("vadmin_auth_menu.id", ondelete="CASCADE")),
|
||||
)
|
||||
|
||||
vadmin_auth_user_depts = Table(
|
||||
"vadmin_auth_user_depts",
|
||||
Base.metadata,
|
||||
Column("user_id", Integer, ForeignKey("vadmin_auth_user.id", ondelete="CASCADE")),
|
||||
Column("dept_id", Integer, ForeignKey("vadmin_auth_dept.id", ondelete="CASCADE")),
|
||||
)
|
||||
|
||||
vadmin_auth_role_depts = Table(
|
||||
"vadmin_auth_role_depts",
|
||||
Base.metadata,
|
||||
Column("role_id", Integer, ForeignKey("vadmin_auth_role.id", ondelete="CASCADE")),
|
||||
Column("dept_id", Integer, ForeignKey("vadmin_auth_dept.id", ondelete="CASCADE")),
|
||||
)
|
||||
|
||||
|
@ -10,18 +10,21 @@ from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, Integer
|
||||
from .menu import VadminMenu
|
||||
from .m2m import vadmin_auth_role_menus
|
||||
from .dept import VadminDept
|
||||
from .m2m import vadmin_auth_role_menus, vadmin_auth_role_depts
|
||||
|
||||
|
||||
class VadminRole(BaseModel):
|
||||
__tablename__ = "vadmin_auth_role"
|
||||
__table_args__ = ({'comment': '角色表'})
|
||||
|
||||
name: Mapped[str] = mapped_column(String(50), index=True, nullable=False, comment="名称")
|
||||
role_key: Mapped[str] = mapped_column(String(50), index=True, nullable=False, comment="权限字符")
|
||||
name: Mapped[str] = mapped_column(String(50), index=True, comment="名称")
|
||||
role_key: Mapped[str] = mapped_column(String(50), index=True, comment="权限字符")
|
||||
data_range: Mapped[int] = mapped_column(Integer, default=4, comment="数据权限范围")
|
||||
disabled: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否禁用")
|
||||
order: Mapped[int | None] = mapped_column(Integer, comment="排序")
|
||||
desc: Mapped[str | None] = mapped_column(String(255), comment="描述")
|
||||
is_admin: Mapped[bool] = mapped_column(Boolean, comment="是否为超级角色", default=False)
|
||||
|
||||
menus: Mapped[set[VadminMenu]] = relationship(secondary=vadmin_auth_role_menus)
|
||||
depts: Mapped[set[VadminDept]] = relationship(secondary=vadmin_auth_role_depts)
|
||||
|
@ -12,7 +12,8 @@ from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, DateTime
|
||||
from passlib.context import CryptContext
|
||||
from .role import VadminRole
|
||||
from .m2m import vadmin_auth_user_roles
|
||||
from .dept import VadminDept
|
||||
from .m2m import vadmin_auth_user_roles, vadmin_auth_user_depts
|
||||
|
||||
pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')
|
||||
|
||||
@ -41,6 +42,7 @@ class VadminUser(BaseModel):
|
||||
is_wx_server_openid: Mapped[bool] = mapped_column(Boolean, default=False, comment="是否已有服务端微信平台openid")
|
||||
|
||||
roles: Mapped[set[VadminRole]] = relationship(secondary=vadmin_auth_user_roles)
|
||||
depts: Mapped[set[VadminDept]] = relationship(secondary=vadmin_auth_user_depts)
|
||||
|
||||
@staticmethod
|
||||
def get_password_hash(password: str) -> str:
|
||||
|
@ -1,2 +1,3 @@
|
||||
from .user import UserParams
|
||||
from .role import RoleParams
|
||||
from .dept import DeptParams
|
||||
|
31
kinit-api/apps/vadmin/auth/params/dept.py
Normal file
31
kinit-api/apps/vadmin/auth/params/dept.py
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/12/18 10:19
|
||||
# @File : dept.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 查询参数-类依赖项
|
||||
|
||||
"""
|
||||
类依赖项-官方文档:https://fastapi.tiangolo.com/zh/tutorial/dependencies/classes-as-dependencies/
|
||||
"""
|
||||
from fastapi import Depends, Query
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class DeptParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str | None = Query(None, title="部门名称"),
|
||||
dept_key: str | None = Query(None, title="部门标识"),
|
||||
disabled: bool | None = Query(None, title="是否禁用"),
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.name = ("like", name)
|
||||
self.dept_key = ("like", dept_key)
|
||||
self.disabled = disabled
|
@ -1,3 +1,4 @@
|
||||
from .user import UserOut, UserUpdate, User, UserIn, UserSimpleOut, ResetPwd, UserUpdateBaseInfo
|
||||
from .role import Role, RoleOut, RoleIn, RoleOptionsOut, RoleSimpleOut
|
||||
from .menu import Menu, MenuSimpleOut, RouterOut, Meta, TreeListOut
|
||||
from .menu import Menu, MenuSimpleOut, RouterOut, Meta, MenuTreeListOut
|
||||
from .dept import Dept, DeptSimpleOut, DeptTreeListOut
|
||||
|
40
kinit-api/apps/vadmin/auth/schemas/dept.py
Normal file
40
kinit-api/apps/vadmin/auth/schemas/dept.py
Normal file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/10/25 12:19
|
||||
# @File : dept.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : pydantic 模型,用于数据库序列化操作
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from core.data_types import DatetimeStr
|
||||
from .menu import MenuSimpleOut
|
||||
|
||||
|
||||
class Dept(BaseModel):
|
||||
name: str
|
||||
dept_key: str
|
||||
disabled: bool = False
|
||||
order: int | None = None
|
||||
desc: str | None = None
|
||||
owner: str | None = None
|
||||
phone: str | None = None
|
||||
email: str | None = None
|
||||
|
||||
parent_id: int | None = None
|
||||
|
||||
|
||||
class DeptSimpleOut(Dept):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
||||
|
||||
class DeptTreeListOut(DeptSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
children: list[dict] = []
|
||||
|
@ -60,7 +60,7 @@ class RouterOut(BaseModel):
|
||||
children: list[dict] = []
|
||||
|
||||
|
||||
class TreeListOut(MenuSimpleOut):
|
||||
class MenuTreeListOut(MenuSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
children: list[dict] = []
|
||||
|
@ -10,6 +10,7 @@
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from core.data_types import DatetimeStr
|
||||
from .menu import MenuSimpleOut
|
||||
from .dept import DeptSimpleOut
|
||||
|
||||
|
||||
class Role(BaseModel):
|
||||
@ -17,6 +18,7 @@ class Role(BaseModel):
|
||||
disabled: bool = False
|
||||
order: int | None = None
|
||||
desc: str | None = None
|
||||
data_range: int = 4
|
||||
role_key: str
|
||||
is_admin: bool = False
|
||||
|
||||
@ -33,10 +35,12 @@ class RoleOut(RoleSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
menus: list[MenuSimpleOut] = []
|
||||
depts: list[DeptSimpleOut] = []
|
||||
|
||||
|
||||
class RoleIn(Role):
|
||||
menu_ids: list[int] = []
|
||||
dept_ids: list[int] = []
|
||||
|
||||
|
||||
class RoleOptionsOut(BaseModel):
|
||||
|
@ -9,9 +9,9 @@
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, field_validator
|
||||
from pydantic_core.core_schema import FieldValidationInfo
|
||||
|
||||
from core.data_types import Telephone, DatetimeStr, Email
|
||||
from .role import RoleSimpleOut
|
||||
from .dept import DeptSimpleOut
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
@ -31,6 +31,7 @@ class UserIn(User):
|
||||
创建用户
|
||||
"""
|
||||
role_ids: list[int] = []
|
||||
dept_ids: list[int] = []
|
||||
password: str | None = ""
|
||||
|
||||
|
||||
@ -58,6 +59,7 @@ class UserUpdate(User):
|
||||
is_staff: bool | None = False
|
||||
gender: str | None = "0"
|
||||
role_ids: list[int] = []
|
||||
dept_ids: list[int] = []
|
||||
|
||||
|
||||
class UserSimpleOut(User):
|
||||
@ -76,6 +78,7 @@ class UserOut(UserSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
roles: list[RoleSimpleOut] = []
|
||||
depts: list[DeptSimpleOut] = []
|
||||
|
||||
|
||||
class ResetPwd(BaseModel):
|
||||
|
@ -40,7 +40,7 @@ class OpenAuth(AuthValidation):
|
||||
try:
|
||||
telephone = self.validate_token(request, token)
|
||||
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
|
||||
return await self.validate_user(request, user, db)
|
||||
return await self.validate_user(request, user, db, is_all=True)
|
||||
except CustomException:
|
||||
return Auth(db=db)
|
||||
|
||||
@ -65,7 +65,7 @@ class AllUserAuth(AuthValidation):
|
||||
return Auth(db=db)
|
||||
telephone = self.validate_token(request, token)
|
||||
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True)
|
||||
return await self.validate_user(request, user, db)
|
||||
return await self.validate_user(request, user, db, is_all=True)
|
||||
|
||||
|
||||
class FullAdminAuth(AuthValidation):
|
||||
@ -94,9 +94,9 @@ class FullAdminAuth(AuthValidation):
|
||||
if not settings.OAUTH_ENABLE:
|
||||
return Auth(db=db)
|
||||
telephone = self.validate_token(request, token)
|
||||
options = [joinedload(VadminUser.roles).subqueryload(VadminRole.menus)]
|
||||
options = [joinedload(VadminUser.roles).subqueryload(VadminRole.menus), joinedload(VadminUser.depts)]
|
||||
user = await UserDal(db).get_data(telephone=telephone, v_return_none=True, v_options=options, is_staff=True)
|
||||
result = await self.validate_user(request, user, db)
|
||||
result = await self.validate_user(request, user, db, is_all=False)
|
||||
permissions = self.get_user_permissions(user)
|
||||
if permissions != {'*.*.*'} and self.permissions:
|
||||
if not (self.permissions & permissions):
|
||||
|
@ -14,11 +14,14 @@ from apps.vadmin.auth.models import VadminUser
|
||||
from core.exception import CustomException
|
||||
from utils import status
|
||||
from datetime import timedelta, datetime
|
||||
from apps.vadmin.auth.crud import UserDal
|
||||
|
||||
|
||||
class Auth(BaseModel):
|
||||
user: VadminUser = None
|
||||
db: AsyncSession
|
||||
data_range: int | None = None
|
||||
dept_ids: list | None = []
|
||||
|
||||
class Config:
|
||||
# 接收任意类型
|
||||
@ -80,9 +83,14 @@ class AuthValidation:
|
||||
return telephone
|
||||
|
||||
@classmethod
|
||||
async def validate_user(cls, request: Request, user: VadminUser, db: AsyncSession) -> Auth:
|
||||
async def validate_user(cls, request: Request, user: VadminUser, db: AsyncSession, is_all: bool = True) -> Auth:
|
||||
"""
|
||||
验证用户信息
|
||||
:param request:
|
||||
:param user:
|
||||
:param db:
|
||||
:param is_all: 是否所有人访问,不加权限
|
||||
:return:
|
||||
"""
|
||||
if user is None:
|
||||
raise CustomException(msg="未认证,请您重新登陆", code=cls.error_code, status_code=cls.error_code)
|
||||
@ -95,12 +103,17 @@ class AuthValidation:
|
||||
request.scope["body"] = await request.body()
|
||||
except RuntimeError:
|
||||
request.scope["body"] = "获取失败"
|
||||
return Auth(user=user, db=db)
|
||||
if is_all:
|
||||
return Auth(user=user, db=db)
|
||||
data_range, dept_ids = await cls.get_user_data_range(user, db)
|
||||
return Auth(user=user, db=db, data_range=data_range, dept_ids=dept_ids)
|
||||
|
||||
@classmethod
|
||||
def get_user_permissions(cls, user: VadminUser) -> set:
|
||||
"""
|
||||
获取员工用户所有权限列表
|
||||
:param user: 用户实例
|
||||
:return:
|
||||
"""
|
||||
if user.is_admin():
|
||||
return {'*.*.*'}
|
||||
@ -110,3 +123,36 @@ class AuthValidation:
|
||||
if menu.perms and not menu.disabled:
|
||||
permissions.add(menu.perms)
|
||||
return permissions
|
||||
|
||||
@classmethod
|
||||
async def get_user_data_range(cls, user: VadminUser, db: AsyncSession) -> tuple:
|
||||
"""
|
||||
获取用户数据范围
|
||||
0 仅本人数据权限 create_user_id 查询
|
||||
1 本部门数据权限 部门 id 左连接查询
|
||||
2 本部门及以下数据权限 部门 id 左连接查询
|
||||
3 自定义数据权限 部门 id 左连接查询
|
||||
4 全部数据权限 无
|
||||
:param user:
|
||||
:param db:
|
||||
:return:
|
||||
"""
|
||||
if user.is_admin():
|
||||
return 4, ["*"]
|
||||
data_range = max([i.data_range for i in user.roles])
|
||||
dept_ids = set()
|
||||
if data_range == 0:
|
||||
pass
|
||||
elif data_range == 1:
|
||||
for dept in user.depts:
|
||||
dept_ids.add(dept.id)
|
||||
elif data_range == 2:
|
||||
# 递归获取部门列表
|
||||
dept_ids = await UserDal(db).recursion_get_dept_ids(user)
|
||||
elif data_range == 3:
|
||||
for role_obj in user.roles:
|
||||
for dept in role_obj.depts:
|
||||
dept_ids.add(dept.id)
|
||||
elif data_range == 4:
|
||||
dept_ids.add("*")
|
||||
return data_range, list(dept_ids)
|
||||
|
@ -13,13 +13,22 @@ from core.database import redis_getter
|
||||
from utils.response import SuccessResponse, ErrorResponse
|
||||
from . import schemas, crud, models
|
||||
from core.dependencies import IdList
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth
|
||||
from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth, OpenAuth
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from .params import UserParams, RoleParams
|
||||
from .params import UserParams, RoleParams, DeptParams
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 接口测试
|
||||
###########################################################
|
||||
@app.get("/test", summary="接口测试")
|
||||
async def test(auth: Auth = Depends(FullAdminAuth())):
|
||||
print(auth)
|
||||
return SuccessResponse()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 用户管理
|
||||
###########################################################
|
||||
@ -29,7 +38,7 @@ async def get_users(
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.list"]))
|
||||
):
|
||||
model = models.VadminUser
|
||||
options = [joinedload(model.roles)]
|
||||
options = [joinedload(model.roles), joinedload(model.depts)]
|
||||
schema = schemas.UserOut
|
||||
datas, count = await crud.UserDal(auth.db).get_datas(
|
||||
**params.dict(),
|
||||
@ -70,7 +79,7 @@ async def get_user(
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.view", "auth.user.update"]))
|
||||
):
|
||||
model = models.VadminUser
|
||||
options = [joinedload(model.roles)]
|
||||
options = [joinedload(model.roles), joinedload(model.depts)]
|
||||
schema = schemas.UserOut
|
||||
return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, v_options=options, v_schema=schema))
|
||||
|
||||
@ -189,7 +198,7 @@ async def get_role(
|
||||
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.view", "auth.role.update"]))
|
||||
):
|
||||
model = models.VadminRole
|
||||
options = [joinedload(model.menus)]
|
||||
options = [joinedload(model.menus), joinedload(model.depts)]
|
||||
schema = schemas.RoleOut
|
||||
return SuccessResponse(await crud.RoleDal(auth.db).get_data(data_id, v_options=options, v_schema=schema))
|
||||
|
||||
@ -254,3 +263,46 @@ async def get_role_menu_tree(
|
||||
tree_data = await crud.MenuDal(auth.db).get_tree_list(mode=3)
|
||||
role_menu_tree = await crud.RoleDal(auth.db).get_role_menu_tree(role_id)
|
||||
return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": tree_data})
|
||||
|
||||
|
||||
###########################################################
|
||||
# 部门管理
|
||||
###########################################################
|
||||
@app.get("/depts", summary="获取部门列表")
|
||||
async def get_depts(
|
||||
params: DeptParams = Depends(),
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
datas = await crud.DeptDal(auth.db).get_tree_list(1)
|
||||
return SuccessResponse(datas)
|
||||
|
||||
|
||||
@app.get("/dept/tree/options", summary="获取部门树选择项,添加/修改部门时使用")
|
||||
async def get_dept_options(auth: Auth = Depends(FullAdminAuth())):
|
||||
datas = await crud.DeptDal(auth.db).get_tree_list(mode=2)
|
||||
return SuccessResponse(datas)
|
||||
|
||||
|
||||
@app.get("/dept/user/tree/options", summary="获取部门树选择项,添加/修改用户时使用")
|
||||
async def get_dept_treeselect(auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.DeptDal(auth.db).get_tree_list(mode=3))
|
||||
|
||||
|
||||
@app.post("/depts", summary="创建部门信息")
|
||||
async def create_dept(data: schemas.Dept, auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.DeptDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/depts", summary="批量删除部门", description="硬删除, 如果存在用户关联则无法删除")
|
||||
async def delete_depts(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth())):
|
||||
await crud.DeptDal(auth.db).delete_datas(ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.put("/depts/{data_id}", summary="更新部门信息")
|
||||
async def put_dept(
|
||||
data_id: int,
|
||||
data: schemas.Dept,
|
||||
auth: Auth = Depends(FullAdminAuth())
|
||||
):
|
||||
return SuccessResponse(await crud.DeptDal(auth.db).put_data(data_id, data))
|
||||
|
Loading…
x
Reference in New Issue
Block a user