上线:用户管理页面中加入部门树,根据部门过滤用户,前后端接口数据完成调试

This commit is contained in:
zhangtao
2025-01-11 01:18:27 +08:00
parent 01c3cb1368
commit f5819f91b2
13 changed files with 135 additions and 116 deletions
@@ -15,6 +15,7 @@ class UserQueryParams:
name: Optional[str] = Query(None, description="名称", min_length=2, max_length=20), name: Optional[str] = Query(None, description="名称", min_length=2, max_length=20),
mobile: Optional[str] = Query(None, description="手机号", pattern=r'^1[3-9]\d{9}$'), mobile: Optional[str] = Query(None, description="手机号", pattern=r'^1[3-9]\d{9}$'),
email: Optional[str] = Query(None, description="邮箱", pattern=r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'), email: Optional[str] = Query(None, description="邮箱", pattern=r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'),
dept_id: Optional[int] = Query(None, description="部门ID"),
available: Optional[bool] = Query(None, description="是否可用"), available: Optional[bool] = Query(None, description="是否可用"),
start_time: Optional[str] = Query(None, description="开始时间"), start_time: Optional[str] = Query(None, description="开始时间"),
end_time: Optional[str] = Query(None, description="结束时间"), end_time: Optional[str] = Query(None, description="结束时间"),
@@ -29,6 +30,7 @@ class UserQueryParams:
self.email = ("like", email) self.email = ("like", email)
# 精确查询字段 # 精确查询字段
self.dept_id = dept_id
self.creator_id = creator self.creator_id = creator
self.available = available self.available = available
Binary file not shown.
+2 -2
View File
@@ -74,7 +74,7 @@
:loading="loading" :loading="loading"
:dataSource="cacheNames" :dataSource="cacheNames"
:pagination="false" :pagination="false"
:scroll="{ y: 600 }" :scroll="{ y: 500 }"
rowKey="cache_name" rowKey="cache_name"
> >
<a-table-column key="cache_name" title="缓存名称" align="center" :ellipsis="true"> <a-table-column key="cache_name" title="缓存名称" align="center" :ellipsis="true">
@@ -149,7 +149,7 @@
<a-form-item label="缓存内容:" name="cache_value"> <a-form-item label="缓存内容:" name="cache_value">
<a-textarea <a-textarea
v-model:value="cacheForm.cache_value" v-model:value="cacheForm.cache_value"
:rows="19" :rows="15"
readonly readonly
/> />
</a-form-item> </a-form-item>
@@ -44,7 +44,6 @@
:data-source="tableData" :data-source="tableData"
:loading="loading" :loading="loading"
:scroll="{ x: 500 }" :scroll="{ x: 500 }"
:style="{ minHeight: '500px' }"
:pagination="pagination"> :pagination="pagination">
<template #bodyCell="{ column, record, index }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'index'"> <template v-if="column.dataIndex === 'index'">
+4 -3
View File
@@ -57,15 +57,16 @@
</a-space> </a-space>
</template> </template>
<a-table <a-table v-if="dataSource.length"
:defaultExpandAllRows="true"
:rowKey="record => record.id" :rowKey="record => record.id"
:columns="columns" :columns="columns"
:data-source="dataSource" :data-source="dataSource"
:loading="tableLoading" :loading="tableLoading"
:scroll="{ x: 500, y: 'calc(100vh - 500px)' }" :scroll="{ x: 500, y: 'calc(100vh - 300px)' }"
:row-selection="rowSelection" :row-selection="rowSelection"
:pagination="false" :pagination="false"
:style="{ minHeight: '500px' }" :style="{ minHeight: '300px' }"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
+2 -1
View File
@@ -57,7 +57,8 @@
@change="handleTableChange" @change="handleTableChange"
:scroll="{ x: 500 }" :scroll="{ x: 500 }"
:pagination="pagination" :pagination="pagination"
:style="{ minHeight: '500px' }"> :style="{ minHeight: '300px'}"
>
<template #bodyCell="{ column, record, index }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'index'"> <template v-if="column.dataIndex === 'index'">
<span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span> <span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span>
+4 -3
View File
@@ -57,15 +57,16 @@
</a-button> </a-button>
</a-dropdown> </a-dropdown>
</template> </template>
<a-table <a-table v-if="dataSource.length"
:defaultExpandAllRows="true"
:rowKey="record => record.id" :rowKey="record => record.id"
:columns="columns" :columns="columns"
:data-source="dataSource" :data-source="dataSource"
:loading="tableLoading" :loading="tableLoading"
:scroll="{ x: 500, y: 'calc(100vh - 500px)' }" :scroll="{ x: 500, y: 'calc(100vh - 300px)' }"
:row-selection="rowSelection" :row-selection="rowSelection"
:pagination="false" :pagination="false"
:style="{ minHeight: '500px' }" :style="{ minHeight: '300px' }"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'type'"> <template v-if="column.dataIndex === 'type'">
+2 -1
View File
@@ -68,7 +68,8 @@
@change="handleTableChange" @change="handleTableChange"
:scroll="{ x: 500 }" :scroll="{ x: 500 }"
:pagination="pagination" :pagination="pagination"
:style="{ minHeight: '500px' }"> :style="{ minHeight: '300px' }"
>
<template #bodyCell="{ column, record, index }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'index'"> <template v-if="column.dataIndex === 'index'">
<span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span> <span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span>
+2 -1
View File
@@ -73,7 +73,8 @@
@change="handleTableChange" @change="handleTableChange"
:scroll="{ x: 500 }" :scroll="{ x: 500 }"
:pagination="pagination" :pagination="pagination"
:style="{ minHeight: '500px' }"> :style="{ minHeight: '300px' }"
>
<template #bodyCell="{ column, record, index }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'index'"> <template v-if="column.dataIndex === 'index'">
<span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span> <span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span>
@@ -33,7 +33,7 @@
:checkedKeys="permissionState.dept_ids" :checkedKeys="permissionState.dept_ids"
:rowKey="record => record.id" :rowKey="record => record.id"
:tree-data="deptTreeData" :tree-data="deptTreeData"
:defaultExpandAll="true" defaultExpandAll=true
:scroll="{ y: 1000 }" :scroll="{ y: 1000 }"
:field-names="{ children: 'children', title: 'name', key: 'id' }" :field-names="{ children: 'children', title: 'name', key: 'id' }"
@check="deptTreeCheck" checkable checkStrictly style="margin-top: 15px;" /> @check="deptTreeCheck" checkable checkStrictly style="margin-top: 15px;" />
@@ -56,7 +56,9 @@
</div> </div>
<div style="margin-top: 15px;"> <div style="margin-top: 15px;">
<a-table :rowKey="record => record.id" <a-table v-if="menuTreeData.length"
:rowKey="record => record.id"
:defaultExpandAllRows="true"
:columns="menuColumns" :columns="menuColumns"
:data-source="menuTreeData" :data-source="menuTreeData"
:row-selection="menuRowSelection" :row-selection="menuRowSelection"
+2 -1
View File
@@ -71,7 +71,8 @@
@change="handleTableChange" @change="handleTableChange"
:scroll="{ x: 500 }" :scroll="{ x: 500 }"
:pagination="pagination" :pagination="pagination"
:style="{ minHeight: '500px' }"> :style="{ minHeight: '420px' }"
>
<template #bodyCell="{ column, record, index }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'index'"> <template v-if="column.dataIndex === 'index'">
<span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span> <span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span>
+110 -101
View File
@@ -39,8 +39,10 @@
<!-- 表格区域 --> <!-- 表格区域 -->
<div class="table-wrapper"> <div class="table-wrapper">
<a-card title="用户列表" :bordered="false" :headStyle="{ borderBottom: 'none', padding: '20px 24px' }" <a-card title="用户管理"
:bodyStyle="{ padding: '0 24px', minHeight: 'calc(100vh - 400px)' }"> :bordered="false"
:headStyle="{ borderBottom: 'none', padding: '20px 24px' }"
:bodyStyle="{ padding: '10px 24px', minHeight: 'calc(100vh - 400px)' }">
<template #extra> <template #extra>
<a-button type="primary" :icon="h(PlusOutlined)" @click="modalHandle('create')" style="margin-right: 10px;"> <a-button type="primary" :icon="h(PlusOutlined)" @click="modalHandle('create')" style="margin-right: 10px;">
新建 新建
@@ -67,52 +69,75 @@
</a-button> </a-button>
</a-dropdown> </a-dropdown>
</template> </template>
<a-table :rowKey="record => record.id"
:columns="columns" <!-- 使用 a-row 和 a-col 划分左右布局 -->
:data-source="dataSource" <a-row :gutter="16">
:row-selection="rowSelection" <!-- 左侧部门树 -->
:loading="tableLoading" <a-col :span="4">
@change="handleTableChange" <a-tree ref="deptTree" v-if="deptTreeData.length"
:scroll="{ x: 500 }" :tree-data="deptTreeData"
:pagination="pagination" :show-line="true"
:style="{ minHeight: '500px' }"> :defaultExpandAll="true"
<template #bodyCell="{ column, record, index }"> :field-names="{ children: 'children', title: 'name', key: 'id' }"
<template v-if="column.dataIndex === 'index'"> :selected-keys="selectedKeys"
<span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span> @select="onSelectDept">
</template> </a-tree>
<template v-if="column.dataIndex === 'dept'"> </a-col>
<span>{{ record.dept_name }}</span>
</template> <!-- 右侧用户列表 -->
<template v-if="column.dataIndex === 'roles'"> <a-col :span="20">
<span>{{ record.roleNames }}</span> <a-card title="用户列表" >
</template> <a-table :rowKey="record => record.id"
<template v-if="column.dataIndex === 'positions'"> :columns="columns"
<span>{{ record.positionNames }}</span> :data-source="dataSource"
</template> :row-selection="rowSelection"
<template v-if="column.dataIndex === 'gender'"> :loading="tableLoading"
<a-tag :color="record.gender === 1 ? 'blue' : 'pink'">{{ record.gender === 1 ? '男' : '女' }}</a-tag> @change="handleTableChange"
</template> :scroll="{ x: 300 }"
<template v-if="column.dataIndex === 'available'"> :pagination="pagination"
<span> :style="{ minHeight: '310px' }">
<a-badge :status="record.available ? 'processing': 'error'" :text="record.available ? '启用' : '停用'" /> <template #bodyCell="{ column, record, index }">
</span> <template v-if="column.dataIndex === 'index'">
</template> <span>{{ (pagination.current - 1) * pagination.pageSize + index + 1 }}</span>
<template v-if="column.dataIndex === 'is_superuser'"> </template>
<span> <template v-if="column.dataIndex === 'dept'">
<a-badge :status="record.is_superuser ? 'processing': 'error'" :text="record.is_superuser ? '是' : '否'" /> <span>{{ record.dept_name }}</span>
</span> </template>
</template> <template v-if="column.dataIndex === 'roles'">
<template v-if="column.dataIndex === 'operation'"> <span>{{ record.roleNames }}</span>
<a-space size="middle"> </template>
<a v-on:click="modalHandle('view', index)">查看</a> <template v-if="column.dataIndex === 'positions'">
<a v-on:click="modalHandle('update', index)">修改</a> <span>{{ record.positionNames }}</span>
<a-popconfirm title="确定删除吗?" ok-text="确定" cancel-text="取消" @confirm="deleteRow(record)"> </template>
<a style="color: red;">删除</a> <template v-if="column.dataIndex === 'gender'">
</a-popconfirm> <a-tag :color="record.gender === 1 ? 'blue' : 'pink'">{{ record.gender === 1 ? '男' : '女' }}</a-tag>
</a-space> </template>
</template> <template v-if="column.dataIndex === 'available'">
</template> <span>
</a-table> <a-badge :status="record.available ? 'processing' : 'error'"
:text="record.available ? '启用' : '停用'" />
</span>
</template>
<template v-if="column.dataIndex === 'is_superuser'">
<span>
<a-badge :status="record.is_superuser ? 'processing' : 'error'"
:text="record.is_superuser ? '是' : '否'" />
</span>
</template>
<template v-if="column.dataIndex === 'operation'">
<a-space size="middle">
<a v-on:click="modalHandle('view', index)">查看</a>
<a v-on:click="modalHandle('update', index)">修改</a>
<a-popconfirm title="确定删除吗?" ok-text="确定" cancel-text="取消" @confirm="deleteRow(record)">
<a style="color: red;">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
</a-card>
</a-col>
</a-row>
</a-card> </a-card>
</div> </div>
@@ -139,10 +164,12 @@
<a-descriptions-item label="邮箱">{{ detailState.email }}</a-descriptions-item> <a-descriptions-item label="邮箱">{{ detailState.email }}</a-descriptions-item>
<a-descriptions-item label="联系电话">{{ detailState.mobile }}</a-descriptions-item> <a-descriptions-item label="联系电话">{{ detailState.mobile }}</a-descriptions-item>
<a-descriptions-item label="是否超管"> <a-descriptions-item label="是否超管">
<a-badge :status="detailState.is_superuser ? 'processing': 'error'" :text="detailState.is_superuser ? '是' : '否'" /> <a-badge :status="detailState.is_superuser ? 'processing': 'error'"
:text="detailState.is_superuser ? '是' : '否'" />
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="状态"> <a-descriptions-item label="状态">
<a-badge :status="detailState.available ? 'processing': 'error'" :text="detailState.available ? '启用' : '停用'" /> <a-badge :status="detailState.available ? 'processing': 'error'"
:text="detailState.available ? '启用' : '停用'" />
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="上次登录时间">{{ detailState.last_login }}</a-descriptions-item> <a-descriptions-item label="上次登录时间">{{ detailState.last_login }}</a-descriptions-item>
<a-descriptions-item label="创建人">{{ detailState.creator ? detailState.creator.name : '-' <a-descriptions-item label="创建人">{{ detailState.creator ? detailState.creator.name : '-'
@@ -283,29 +310,12 @@
</a-modal> </a-modal>
<!-- 导入对话框 --> <!-- 导入对话框 -->
<a-modal v-model:open="upload.open" <a-modal v-model:open="upload.open" title="导入用户" :width="500" @ok="submitFileForm"
title="导入用户" :confirmLoading="upload.isUploading" :bodyStyle="{ padding: '24px' }">
:width="500"
@ok="submitFileForm"
:confirmLoading="upload.isUploading"
:bodyStyle="{ padding: '24px' }">
<div class="import-container"> <div class="import-container">
<a-alert <a-alert message="请先下载模板,按照模板格式填写数据后再导入" type="info" show-icon style="margin-bottom: 16px" />
message="请先下载模板,按照模板格式填写数据后再导入" <a-upload ref="uploadRef" :accept="'.xlsx, .xls'" :showUploadList="true" :beforeUpload="beforeUpload"
type="info" :customRequest="customRequest" :maxCount="1" :fileList="upload.fileList" @remove="handleRemove">
show-icon
style="margin-bottom: 16px"
/>
<a-upload
ref="uploadRef"
:accept="'.xlsx, .xls'"
:showUploadList="true"
:beforeUpload="beforeUpload"
:customRequest="customRequest"
:maxCount="1"
:fileList="upload.fileList"
@remove="handleRemove"
>
<a-button type="primary"> <a-button type="primary">
<upload-outlined></upload-outlined> <upload-outlined></upload-outlined>
选择文件 选择文件
@@ -315,12 +325,8 @@
<a-typography-text> <a-typography-text>
{{ file.name }} {{ file.name }}
</a-typography-text> </a-typography-text>
<a-progress <a-progress v-if="file.status === 'uploading'" :percent="file.percent" size="small"
v-if="file.status === 'uploading'" style="width: 100px" />
:percent="file.percent"
size="small"
style="width: 100px"
/>
</a-space> </a-space>
</template> </template>
</a-upload> </a-upload>
@@ -354,7 +360,7 @@
import { ref, reactive, computed, unref, onMounted, h } from 'vue'; import { ref, reactive, computed, unref, onMounted, h } from 'vue';
import { Table, message, Modal } from 'ant-design-vue'; import { Table, message, Modal } from 'ant-design-vue';
import type { TableColumnsType, MenuProps } from 'ant-design-vue'; import type { TableColumnsType, MenuProps } from 'ant-design-vue';
import { PlusOutlined, DownOutlined, UploadOutlined, DownloadOutlined, CheckOutlined, StopOutlined, SearchOutlined } from '@ant-design/icons-vue'; import { CarryOutOutlined, SmileTwoTone, PlusOutlined, DownOutlined, UploadOutlined, DownloadOutlined, CheckOutlined, StopOutlined, SearchOutlined } from '@ant-design/icons-vue';
import { isEmpty, listToTree } from '@/utils/util'; import { isEmpty, listToTree } from '@/utils/util';
import PageHeader from '@/components/PageHeader.vue'; import PageHeader from '@/components/PageHeader.vue';
import { getDeptList } from '@/api/system/dept' import { getDeptList } from '@/api/system/dept'
@@ -363,6 +369,7 @@ import SelectorModal from './SelectorModal.vue'
import type { searchDataType, tableDataType, deptTreeType, roleSelectorType, positionSelectorType } from './types' import type { searchDataType, tableDataType, deptTreeType, roleSelectorType, positionSelectorType } from './types'
import storage from 'store'; import storage from 'store';
const deptTree = ref(null);
const tableLoading = ref(false); const tableLoading = ref(false);
const openModal = ref(false); const openModal = ref(false);
const modalTitle = ref(''); const modalTitle = ref('');
@@ -476,20 +483,6 @@ const columns = reactive<TableColumnsType>([
align: 'center', align: 'center',
width: 100 width: 100
}, },
{
title: '邮箱',
dataIndex: 'email',
ellipsis: true,
align: 'center',
width: 160
},
{
title: '联系电话',
dataIndex: 'mobile',
align: 'center',
ellipsis: true,
width: 120
},
{ {
title: '性别', title: '性别',
dataIndex: 'gender', dataIndex: 'gender',
@@ -504,13 +497,6 @@ const columns = reactive<TableColumnsType>([
ellipsis: true, ellipsis: true,
width: 100 width: 100
}, },
{
title: '备注',
dataIndex: 'description',
ellipsis: true,
align: 'center',
// width: 200
},
{ {
title: '操作', title: '操作',
dataIndex: 'operation', dataIndex: 'operation',
@@ -536,6 +522,8 @@ const rowSelection = computed(() => {
} }
}); });
const selectedKeys = ref<string[]>([]);
// 生命周期钩子 // 生命周期钩子
onMounted(() => loadingData()); onMounted(() => loadingData());
@@ -559,6 +547,13 @@ const loadingData = () => {
if (queryState.available) { if (queryState.available) {
params['available'] = queryState.available == "true" ? true : false; params['available'] = queryState.available == "true" ? true : false;
} }
if (queryState.dept_id) {
params['dept_id'] = queryState.dept_id; // 添加 dept_id 参数
selectedKeys.value = [queryState.dept_id]; // 选中对应的部门节点
} else {
selectedKeys.value = []; // 清除选中状态
}
params['page_no'] = pagination.current params['page_no'] = pagination.current
params['page_size'] = pagination.pageSize params['page_size'] = pagination.pageSize
@@ -590,7 +585,11 @@ const resetFields = () => {
delete queryState[key]; delete queryState[key];
}); });
pagination.current = 1; pagination.current = 1;
queryState.available = null queryState.available = null;
queryState.dept_id = null;
selectedKeys.value = [];
loadingData(); loadingData();
} }
@@ -790,6 +789,16 @@ const handleSelectorModalEvent = (
} }
} }
// 部门选择器事件
const onSelectDept = (selectedKeys: string[], info: any) => {
if (selectedKeys.length > 0) {
queryState.dept_id = selectedKeys[0]; // 获取选中的部门 ID
} else {
queryState.dept_id = null; // 如果没有选中部门,则删除 dept_id 参数
}
loadingData(); // 重新加载用户列表
};
// 上传配置 // 上传配置
const token = storage.get('Access-Token'); const token = storage.get('Access-Token');
+1
View File
@@ -2,6 +2,7 @@ export interface searchDataType {
username?: string username?: string
name?: string name?: string
available?: string available?: string
dept_id?: number;
} }
export interface searchSelectDataType { export interface searchSelectDataType {