feat(user-management-relation): 在用户管理页面集成用户带人关系组件,并修复相关的诸多BUG和样式问题
This commit is contained in:
@@ -318,6 +318,15 @@ export function fetchGetUserPage(params?: Api.SystemManage.UserSearchParams) {
|
|||||||
/** 为兼容旧代码保留原函数名 */
|
/** 为兼容旧代码保留原函数名 */
|
||||||
export const fetchGetUserList = fetchGetUserPage;
|
export const fetchGetUserList = fetchGetUserPage;
|
||||||
|
|
||||||
|
/** 通过部门id获取用户详情 */
|
||||||
|
export function fetchGetUserListByDeptId(deptId: any) {
|
||||||
|
return request<Api.SystemManage.UserSimple[]>({
|
||||||
|
url: `${USER_PREFIX}/list-by-dept-id`,
|
||||||
|
method: 'get',
|
||||||
|
params: { deptId }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取用户详情 */
|
/** 获取用户详情 */
|
||||||
export function fetchGetUser(id: number) {
|
export function fetchGetUser(id: number) {
|
||||||
return request<Api.SystemManage.User>({
|
return request<Api.SystemManage.User>({
|
||||||
@@ -487,10 +496,11 @@ export function fetchAssignUserRoles(data: Api.SystemManage.AssignUserRoleParams
|
|||||||
* - 中间节点:有上级也有下级
|
* - 中间节点:有上级也有下级
|
||||||
* - 叶子节点:基层员工,没有下级
|
* - 叶子节点:基层员工,没有下级
|
||||||
*/
|
*/
|
||||||
export function fetchGetUserManagementRelationTree() {
|
export function fetchGetUserManagementRelationTree(query: UserManagementRelationQueryReqVO) {
|
||||||
return request<Api.SystemManage.UserManagementRelationTreeRespVO[]>({
|
return request<Api.SystemManage.UserManagementRelationTreeRespVO[]>({
|
||||||
url: `${USER_MANAGEMENT_RELATION_PREFIX}/tree`,
|
url: `${USER_MANAGEMENT_RELATION_PREFIX}/tree`,
|
||||||
method: 'get'
|
method: 'get',
|
||||||
|
params: query
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
src/typings/api/system-manage.d.ts
vendored
23
src/typings/api/system-manage.d.ts
vendored
@@ -40,8 +40,8 @@ declare namespace Api {
|
|||||||
|
|
||||||
type RoleSearchParams = CommonType.RecordNullable<Pick<Role, 'name' | 'code' | 'status'>> &
|
type RoleSearchParams = CommonType.RecordNullable<Pick<Role, 'name' | 'code' | 'status'>> &
|
||||||
PageParams & {
|
PageParams & {
|
||||||
createTime?: string[];
|
createTime?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type SaveRoleParams = Pick<Role, 'name' | 'code' | 'sort' | 'status'> & {
|
type SaveRoleParams = Pick<Role, 'name' | 'code' | 'sort' | 'status'> & {
|
||||||
remark?: string | null;
|
remark?: string | null;
|
||||||
@@ -143,12 +143,12 @@ declare namespace Api {
|
|||||||
|
|
||||||
type UserSearchParams = CommonType.RecordNullable<
|
type UserSearchParams = CommonType.RecordNullable<
|
||||||
Pick<User, 'status'> &
|
Pick<User, 'status'> &
|
||||||
Pick<PageParams, 'pageNo' | 'pageSize'> & {
|
Pick<PageParams, 'pageNo' | 'pageSize'> & {
|
||||||
username?: string;
|
username?: string;
|
||||||
mobile?: string;
|
mobile?: string;
|
||||||
deptId?: number;
|
deptId?: number;
|
||||||
roleId?: number;
|
roleId?: number;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type UserList = PageResult<User>;
|
type UserList = PageResult<User>;
|
||||||
@@ -387,7 +387,12 @@ declare namespace Api {
|
|||||||
* 对应后端 UserManagementRelationQueryReqVO
|
* 对应后端 UserManagementRelationQueryReqVO
|
||||||
*/
|
*/
|
||||||
type UserManagementRelationQueryReqVO = CommonType.RecordNullable<
|
type UserManagementRelationQueryReqVO = CommonType.RecordNullable<
|
||||||
Pick<UserManagementRelation, 'managerUserId' | 'subordinateUserId'>
|
Pick<UserManagementRelation, 'managerUserId' | 'subordinateUserId'> & {
|
||||||
|
/** 是否来自带人关系的index组件 */
|
||||||
|
fromUserIndex: boolean;
|
||||||
|
/** 部门ID */
|
||||||
|
deptId?: number | null;
|
||||||
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -325,4 +325,8 @@ getMenuTreeData();
|
|||||||
:deep(.el-table__row.current-row > td.el-table__cell) {
|
:deep(.el-table__row.current-row > td.el-table__cell) {
|
||||||
background-color: rgb(64 158 255 / 8%);
|
background-color: rgb(64 158 255 / 8%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-row) {
|
||||||
|
margin: 0 0 -15px 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -15,21 +15,33 @@
|
|||||||
* - 叶子节点:基层员工,没有下级
|
* - 叶子节点:基层员工,没有下级
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { nextTick, onMounted, reactive, ref } from 'vue';
|
import {nextTick, onMounted, reactive, ref} from 'vue';
|
||||||
import type { ElTree } from 'element-plus';
|
import type {ElTree} from 'element-plus';
|
||||||
import { ElButton, ElPopconfirm, ElTag } from 'element-plus';
|
import {ElButton, ElPopconfirm, ElTag} from 'element-plus';
|
||||||
import { useBoolean } from '@sa/hooks';
|
import {useBoolean} from '@sa/hooks';
|
||||||
import {
|
import {
|
||||||
fetchBatchDeleteUserManagementRelation,
|
fetchBatchDeleteUserManagementRelation,
|
||||||
fetchDeleteUserManagementRelation,
|
fetchDeleteUserManagementRelation, fetchGetUserListByDeptId,
|
||||||
fetchGetUserManagementRelationQuery,
|
fetchGetUserManagementRelationQuery,
|
||||||
fetchGetUserManagementRelationTree,
|
fetchGetUserManagementRelationTree,
|
||||||
fetchGetUserSimpleList
|
|
||||||
} from '@/service/api';
|
} from '@/service/api';
|
||||||
import RelationOperateDialog from './modules/relation-operate-dialog.vue';
|
import RelationOperateDialog from './modules/relation-operate-dialog.vue';
|
||||||
import RelationSearch from './modules/relation-search.vue';
|
import RelationSearch from './modules/relation-search.vue';
|
||||||
|
|
||||||
defineOptions({ name: 'UserManagementRelation' });
|
defineOptions({name: 'UserManagementRelation'});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件 userQuery 定义
|
||||||
|
*
|
||||||
|
* @param fromUserIndex 是否不是从带人关系 index 页面访问(从 user 页面访问时为 true)
|
||||||
|
*/
|
||||||
|
interface userQuery {
|
||||||
|
fromUserIndex?: boolean;
|
||||||
|
deptId?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//从user的index组件访问带人关系,fromUserIndex为true,否则false; dept=100是灿能电力的id
|
||||||
|
const {fromUserIndex = false, deptId = 100} = defineProps<userQuery>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化搜索参数
|
* 初始化搜索参数
|
||||||
@@ -74,7 +86,7 @@ const treeProps: any = {
|
|||||||
* 获取用户简单列表,供搜索组件和对话框组件共享使用
|
* 获取用户简单列表,供搜索组件和对话框组件共享使用
|
||||||
*/
|
*/
|
||||||
async function loadUserList() {
|
async function loadUserList() {
|
||||||
const { data, error } = await fetchGetUserSimpleList();
|
const {data, error} = await fetchGetUserListByDeptId(deptId);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
userList.value = data || [];
|
userList.value = data || [];
|
||||||
}
|
}
|
||||||
@@ -89,7 +101,12 @@ async function loadTreeData() {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data, error } = await fetchGetUserManagementRelationTree();
|
// 默认不是来自user的index组件访问且deptId=100,查询灿能电力及其以下所有部门的用户的带人关系
|
||||||
|
const query: Api.SystemManage.UserManagementRelationQueryReqVO = {
|
||||||
|
fromUserIndex: fromUserIndex,
|
||||||
|
deptId: deptId
|
||||||
|
};
|
||||||
|
const {data, error} = await fetchGetUserManagementRelationTree(query);
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
treeData.value = data || [];
|
treeData.value = data || [];
|
||||||
@@ -110,7 +127,7 @@ async function loadTreeDataByQuery(query: Api.SystemManage.UserManagementRelatio
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data, error } = await fetchGetUserManagementRelationQuery(query);
|
const {data, error} = await fetchGetUserManagementRelationQuery(query);
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
treeData.value = data || [];
|
treeData.value = data || [];
|
||||||
@@ -143,13 +160,13 @@ async function handleSearch() {
|
|||||||
|
|
||||||
// 判断是否有搜索条件
|
// 判断是否有搜索条件
|
||||||
const hasSearchCondition = searchParams.subordinateUserId !== undefined && searchParams.subordinateUserId !== null;
|
const hasSearchCondition = searchParams.subordinateUserId !== undefined && searchParams.subordinateUserId !== null;
|
||||||
|
|
||||||
if (hasSearchCondition) {
|
if (hasSearchCondition) {
|
||||||
// 有搜索条件,调用查询接口
|
// 有搜索条件,调用查询接口
|
||||||
const query: Api.SystemManage.UserManagementRelationQueryReqVO = {
|
const query: Api.SystemManage.UserManagementRelationQueryReqVO = {
|
||||||
subordinateUserId: searchParams.subordinateUserId
|
subordinateUserId: searchParams.subordinateUserId,
|
||||||
|
fromUserIndex: fromUserIndex,
|
||||||
|
deptId: deptId
|
||||||
};
|
};
|
||||||
console.log('查询参数 query:', query);
|
|
||||||
await loadTreeDataByQuery(query);
|
await loadTreeDataByQuery(query);
|
||||||
} else {
|
} else {
|
||||||
// 无搜索条件,加载完整树
|
// 无搜索条件,加载完整树
|
||||||
@@ -171,7 +188,7 @@ function resetSearchParams() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 对话框相关状态
|
// 对话框相关状态
|
||||||
const { bool: operateVisible, setTrue: openOperateModal, setFalse: closeOperateModal } = useBoolean();
|
const {bool: operateVisible, setTrue: openOperateModal, setFalse: closeOperateModal} = useBoolean();
|
||||||
const operateType = ref<UI.TableOperateType>('add');
|
const operateType = ref<UI.TableOperateType>('add');
|
||||||
const editingData = ref<Api.SystemManage.UserManagementRelation | null>(null);
|
const editingData = ref<Api.SystemManage.UserManagementRelation | null>(null);
|
||||||
|
|
||||||
@@ -186,14 +203,14 @@ function openAdd(item?: Api.SystemManage.UserManagementRelationTreeRespVO) {
|
|||||||
// 否则默认管理者为当前登录用户(在对话框组件中处理)
|
// 否则默认管理者为当前登录用户(在对话框组件中处理)
|
||||||
editingData.value = item
|
editingData.value = item
|
||||||
? {
|
? {
|
||||||
id: null,
|
id: null,
|
||||||
managerUserId: item.userId,
|
managerUserId: item.userId,
|
||||||
subordinateUserId: null,
|
subordinateUserId: null,
|
||||||
effectiveFrom: null,
|
effectiveFrom: null,
|
||||||
effectiveUntil: null,
|
effectiveUntil: null,
|
||||||
remark: null,
|
remark: null,
|
||||||
createTime: Date.now()
|
createTime: Date.now()
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
openOperateModal();
|
openOperateModal();
|
||||||
}
|
}
|
||||||
@@ -208,14 +225,14 @@ function openEdit(item: Api.SystemManage.UserManagementRelationTreeRespVO) {
|
|||||||
// 构建树节点数据为编辑所需格式
|
// 构建树节点数据为编辑所需格式
|
||||||
editingData.value = item.id
|
editingData.value = item.id
|
||||||
? {
|
? {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
managerUserId: item.managerUserId,
|
managerUserId: item.managerUserId,
|
||||||
subordinateUserId: item.userId,
|
subordinateUserId: item.userId,
|
||||||
effectiveFrom: null,
|
effectiveFrom: null,
|
||||||
effectiveUntil: null,
|
effectiveUntil: null,
|
||||||
remark: null,
|
remark: null,
|
||||||
createTime: Date.now()
|
createTime: Date.now()
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
openOperateModal();
|
openOperateModal();
|
||||||
}
|
}
|
||||||
@@ -226,7 +243,7 @@ function openEdit(item: Api.SystemManage.UserManagementRelationTreeRespVO) {
|
|||||||
* @param item 要删除的关系记录
|
* @param item 要删除的关系记录
|
||||||
*/
|
*/
|
||||||
async function handleDelete(item: Api.SystemManage.UserManagementRelationTreeRespVO) {
|
async function handleDelete(item: Api.SystemManage.UserManagementRelationTreeRespVO) {
|
||||||
const { error } = await fetchDeleteUserManagementRelation(item.id);
|
const {error} = await fetchDeleteUserManagementRelation(item.id);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
@@ -246,7 +263,7 @@ async function handleBatchDelete() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error } = await fetchBatchDeleteUserManagementRelation(checkedNodeKeys.value);
|
const {error} = await fetchBatchDeleteUserManagementRelation(checkedNodeKeys.value);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
@@ -287,6 +304,25 @@ function hasChildren(node: Api.SystemManage.UserManagementRelationTreeRespVO): b
|
|||||||
return Boolean(node.children && node.children.length > 0);
|
return Boolean(node.children && node.children.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算树形数据中所有节点的数量
|
||||||
|
*
|
||||||
|
* 递归遍历树形结构,统计所有节点总数
|
||||||
|
*
|
||||||
|
* @param nodes 树形数据数组
|
||||||
|
* @returns 节点总数
|
||||||
|
*/
|
||||||
|
function countTreeNodes(nodes: Api.SystemManage.UserManagementRelationTreeRespVO[]): number {
|
||||||
|
let count = 0;
|
||||||
|
for (const node of nodes) {
|
||||||
|
count += 1;
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
count += countTreeNodes(node.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadUserList();
|
await loadUserList();
|
||||||
await reloadTreeData();
|
await reloadTreeData();
|
||||||
@@ -309,28 +345,28 @@ onMounted(async () => {
|
|||||||
<div class="flex items-center justify-between gap-12px">
|
<div class="flex items-center justify-between gap-12px">
|
||||||
<div class="flex items-center gap-10px">
|
<div class="flex items-center gap-10px">
|
||||||
<p>用户带人关系树</p>
|
<p>用户带人关系树</p>
|
||||||
<ElTag effect="plain">{{ treeData.length }}</ElTag>
|
<ElTag effect="plain">{{ countTreeNodes(treeData) }}</ElTag>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-10px">
|
<div class="flex items-center gap-10px">
|
||||||
<ElButton plain type="primary" @click="openAdd()">
|
<ElButton plain type="primary" @click="openAdd()">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-plus class="text-icon" />
|
<icon-ic-round-plus class="text-icon"/>
|
||||||
</template>
|
</template>
|
||||||
新增
|
新增
|
||||||
</ElButton>
|
</ElButton>
|
||||||
<ElPopconfirm title="确认删除选中的关系吗?" @confirm="handleBatchDelete">
|
<!-- <ElPopconfirm title="确认删除选中的关系吗?" @confirm="handleBatchDelete">-->
|
||||||
<template #reference>
|
<!-- <template #reference>-->
|
||||||
<ElButton type="danger" plain :disabled="checkedNodeKeys.length === 0">
|
<!-- <ElButton type="danger" plain :disabled="checkedNodeKeys.length === 0">-->
|
||||||
<template #icon>
|
<!-- <template #icon>-->
|
||||||
<icon-ic-round-delete class="text-icon" />
|
<!-- <icon-ic-round-delete class="text-icon"/>-->
|
||||||
</template>
|
<!-- </template>-->
|
||||||
批量删除
|
<!-- 批量删除-->
|
||||||
</ElButton>
|
<!-- </ElButton>-->
|
||||||
</template>
|
<!-- </template>-->
|
||||||
</ElPopconfirm>
|
<!-- </ElPopconfirm>-->
|
||||||
<ElButton @click="reloadTreeData">
|
<ElButton @click="reloadTreeData">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-refresh class="text-icon" />
|
<icon-ic-round-refresh class="text-icon"/>
|
||||||
</template>
|
</template>
|
||||||
刷新
|
刷新
|
||||||
</ElButton>
|
</ElButton>
|
||||||
@@ -346,7 +382,6 @@ onMounted(async () => {
|
|||||||
:data="treeData"
|
:data="treeData"
|
||||||
:props="treeProps"
|
:props="treeProps"
|
||||||
node-key="userId"
|
node-key="userId"
|
||||||
show-checkbox
|
|
||||||
default-expand-all
|
default-expand-all
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
@check="handleNodeCheck"
|
@check="handleNodeCheck"
|
||||||
@@ -355,18 +390,18 @@ onMounted(async () => {
|
|||||||
<div class="flex flex-1 items-center justify-between">
|
<div class="flex flex-1 items-center justify-between">
|
||||||
<span class="flex items-center gap-8px">
|
<span class="flex items-center gap-8px">
|
||||||
<span>{{ node.label }}</span>
|
<span>{{ node.label }}</span>
|
||||||
<ElTag v-if="data.managerNickname" size="small" type="info">上级:{{ data.managerNickname }}</ElTag>
|
<!-- <ElTag v-if="data.managerNickname" size="small" type="info">上级:{{ data.managerNickname }}</ElTag>-->
|
||||||
</span>
|
</span>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<ElButton link type="primary" size="default" @click.stop="openAdd(data)">
|
<ElButton link type="primary" size="default" @click.stop="openAdd(data)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-plus class="text-icon" />
|
<icon-ic-round-plus class="text-icon"/>
|
||||||
</template>
|
</template>
|
||||||
新增
|
新增
|
||||||
</ElButton>
|
</ElButton>
|
||||||
<ElButton link type="primary" size="small" @click.stop="openEdit(data)">
|
<ElButton link type="primary" size="small" @click.stop="openEdit(data)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-edit class="text-icon" />
|
<icon-ic-round-edit class="text-icon"/>
|
||||||
</template>
|
</template>
|
||||||
编辑
|
编辑
|
||||||
</ElButton>
|
</ElButton>
|
||||||
@@ -378,14 +413,13 @@ onMounted(async () => {
|
|||||||
<template #reference>
|
<template #reference>
|
||||||
<ElButton link type="danger" size="small">
|
<ElButton link type="danger" size="small">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-delete class="text-icon" />
|
<icon-ic-round-delete class="text-icon"/>
|
||||||
</template>
|
</template>
|
||||||
删除
|
删除
|
||||||
</ElButton>
|
</ElButton>
|
||||||
</template>
|
</template>
|
||||||
</ElPopconfirm>
|
</ElPopconfirm>
|
||||||
<ElTag v-else-if="hasChildren(data)" size="small" type="info" style="margin-left: 6px">有下级</ElTag>
|
<span v-else-if="hasChildren(data)" style="margin-left: 56px"></span>
|
||||||
<ElTag v-else size="small" type="warning">不可删除</ElTag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -281,28 +281,34 @@ watch(visible, value => {
|
|||||||
</ElSelect>
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElCol>
|
</ElCol>
|
||||||
|
</ElRow>
|
||||||
|
<ElRow :gutter="16">
|
||||||
<ElCol :span="12">
|
<ElCol :span="12">
|
||||||
<ElFormItem label="生效开始时间" prop="effectiveFrom">
|
<ElFormItem label="生效开始时间" prop="effectiveFrom" style="width:100%">
|
||||||
<ElDatePicker
|
<ElDatePicker
|
||||||
v-model="model.effectiveFrom"
|
v-model="model.effectiveFrom"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
type="datetime"
|
type="datetime"
|
||||||
placeholder="请选择生效开始时间"
|
placeholder="请选择生效开始时间"
|
||||||
value-format="x"
|
value-format="x"
|
||||||
|
style="width:100%"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElCol>
|
</ElCol>
|
||||||
<ElCol :span="12">
|
<ElCol :span="12">
|
||||||
<ElFormItem label="生效结束时间" prop="effectiveUntil">
|
<ElFormItem label="生效结束时间" prop="effectiveUntil" style="width:100%">
|
||||||
<ElDatePicker
|
<ElDatePicker
|
||||||
v-model="model.effectiveUntil"
|
v-model="model.effectiveUntil"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
type="datetime"
|
type="datetime"
|
||||||
placeholder="请选择生效结束时间"
|
placeholder="请选择生效结束时间"
|
||||||
value-format="x"
|
value-format="x"
|
||||||
|
style="width:100%"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElCol>
|
</ElCol>
|
||||||
|
</ElRow>
|
||||||
|
<ElRow :gutter="16">
|
||||||
<ElCol :span="24">
|
<ElCol :span="24">
|
||||||
<ElFormItem label="备注" prop="remark">
|
<ElFormItem label="备注" prop="remark">
|
||||||
<ElInput v-model="model.remark" type="textarea" :rows="4" placeholder="请输入备注" />
|
<ElInput v-model="model.remark" type="textarea" :rows="4" placeholder="请输入备注" />
|
||||||
@@ -313,4 +319,6 @@ watch(visible, value => {
|
|||||||
</BusinessFormDialog>
|
</BusinessFormDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
* - 用户列表通过 props 传入,由父组件统一管理
|
* - 用户列表通过 props 传入,由父组件统一管理
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { defineEmits, defineModel, defineOptions } from 'vue';
|
import {defineEmits, defineModel, defineOptions} from 'vue';
|
||||||
|
|
||||||
defineOptions({ name: 'RelationSearch' });
|
defineOptions({name: 'RelationSearch'});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件 Emits 定义
|
* 组件 Emits 定义
|
||||||
@@ -43,7 +43,7 @@ defineProps<Props>();
|
|||||||
/**
|
/**
|
||||||
* 搜索参数模型,支持双向绑定
|
* 搜索参数模型,支持双向绑定
|
||||||
*/
|
*/
|
||||||
const model = defineModel<Api.SystemManage.UserManagementRelationQueryReqVO>('model', { required: true });
|
const model = defineModel<Api.SystemManage.UserManagementRelationQueryReqVO>('model', {required: true});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置搜索
|
* 重置搜索
|
||||||
@@ -81,9 +81,9 @@ function search() {
|
|||||||
<!-- </ElFormItem>-->
|
<!-- </ElFormItem>-->
|
||||||
<!-- </ElCol>-->
|
<!-- </ElCol>-->
|
||||||
<ElCol :lg="8" :md="12" :sm="12">
|
<ElCol :lg="8" :md="12" :sm="12">
|
||||||
<ElFormItem label="用户名" prop="subordinateUserId">
|
<ElFormItem label="用户名" prop="subordinateUserId" style="margin-left: -50px">
|
||||||
<ElSelect v-model="model.subordinateUserId" class="w-full" placeholder="请选择用户名" clearable filterable>
|
<ElSelect v-model="model.subordinateUserId" class="w-full" placeholder="请选择用户名" clearable filterable>
|
||||||
<ElOption v-for="user in userList" :key="user.id" :label="user.nickname" :value="user.id" />
|
<ElOption v-for="user in userList" :key="user.id" :label="user.nickname" :value="user.id"/>
|
||||||
</ElSelect>
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElCol>
|
</ElCol>
|
||||||
@@ -94,4 +94,8 @@ function search() {
|
|||||||
:deep(.el-form-item__label) {
|
:deep(.el-form-item__label) {
|
||||||
width: 100px !important;
|
width: 100px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-row) {
|
||||||
|
margin: 0 0 -15px 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
import {computed, nextTick, onMounted, reactive, ref, watch} from 'vue';
|
||||||
import type { TableInstance } from 'element-plus';
|
import type {TableInstance} from 'element-plus';
|
||||||
import { ElButton, ElPopconfirm, ElSwitch, ElTag } from 'element-plus';
|
import {ElButton, ElPopconfirm, ElSwitch, ElTag} from 'element-plus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import type { FlatResponseData } from '@sa/axios';
|
import type {FlatResponseData} from '@sa/axios';
|
||||||
import { userGenderRecord } from '@/constants/business';
|
import {userGenderRecord} from '@/constants/business';
|
||||||
import {
|
import {
|
||||||
fetchBatchDeleteUser,
|
fetchBatchDeleteUser,
|
||||||
fetchDeleteDept,
|
fetchDeleteDept,
|
||||||
@@ -17,10 +17,11 @@ import {
|
|||||||
fetchUpdateUser,
|
fetchUpdateUser,
|
||||||
fetchUpdateUserStatus
|
fetchUpdateUserStatus
|
||||||
} from '@/service/api';
|
} from '@/service/api';
|
||||||
import { useUIPaginatedTable } from '@/hooks/common/table';
|
import {useUIPaginatedTable} from '@/hooks/common/table';
|
||||||
import BusinessTableActionCell from '@/components/custom/business-table-action-cell';
|
import BusinessTableActionCell from '@/components/custom/business-table-action-cell';
|
||||||
import { $t } from '@/locales';
|
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
|
||||||
import { buildMenuTree } from '@/views/system/shared/menu-tree';
|
import {$t} from '@/locales';
|
||||||
|
import {buildMenuTree} from '@/views/system/shared/menu-tree';
|
||||||
import UserOperateDialog from './modules/user-operate-dialog.vue';
|
import UserOperateDialog from './modules/user-operate-dialog.vue';
|
||||||
import UserOrgLeaderDialog from './modules/user-org-leader-dialog.vue';
|
import UserOrgLeaderDialog from './modules/user-org-leader-dialog.vue';
|
||||||
import UserOrgOperateDialog from './modules/user-org-operate-dialog.vue';
|
import UserOrgOperateDialog from './modules/user-org-operate-dialog.vue';
|
||||||
@@ -28,8 +29,9 @@ import UserOrgPanel from './modules/user-org-panel.vue';
|
|||||||
import UserResignedDialog from './modules/user-resigned-dialog.vue';
|
import UserResignedDialog from './modules/user-resigned-dialog.vue';
|
||||||
import UserResetPasswordDialog from './modules/user-reset-password-dialog.vue';
|
import UserResetPasswordDialog from './modules/user-reset-password-dialog.vue';
|
||||||
import UserSearch from './modules/user-search.vue';
|
import UserSearch from './modules/user-search.vue';
|
||||||
|
import UserManagementRelation from '@/views/system/user-management-relation/index.vue';
|
||||||
|
|
||||||
defineOptions({ name: 'UserManage' });
|
defineOptions({name: 'UserManage'});
|
||||||
|
|
||||||
function getInitSearchParams(): Api.SystemManage.UserSearchParams {
|
function getInitSearchParams(): Api.SystemManage.UserSearchParams {
|
||||||
return {
|
return {
|
||||||
@@ -146,12 +148,13 @@ const editingDeptData = ref<Api.SystemManage.Dept | null>(null);
|
|||||||
const orgParentId = ref<number | null>(0);
|
const orgParentId = ref<number | null>(0);
|
||||||
const orgLeaderVisible = ref(false);
|
const orgLeaderVisible = ref(false);
|
||||||
const leaderDeptData = ref<Api.SystemManage.Dept | null>(null);
|
const leaderDeptData = ref<Api.SystemManage.Dept | null>(null);
|
||||||
|
const userManagementRelationVisible = ref(false);
|
||||||
|
|
||||||
const deptTree = computed(() => buildMenuTree(deptList.value));
|
const deptTree = computed(() => buildMenuTree(deptList.value));
|
||||||
const currentDept = computed(() => deptList.value.find(item => item.id === currentDeptId.value) ?? null);
|
const currentDept = computed(() => deptList.value.find(item => item.id === currentDeptId.value) ?? null);
|
||||||
const deptCount = computed(() => deptList.value.length);
|
const deptCount = computed(() => deptList.value.length);
|
||||||
|
|
||||||
const { columns, columnChecks, data, loading, getDataByPage, mobilePagination } = useUIPaginatedTable<
|
const {columns, columnChecks, data, loading, getDataByPage, mobilePagination} = useUIPaginatedTable<
|
||||||
FlatResponseData<any, Api.SystemManage.UserList>,
|
FlatResponseData<any, Api.SystemManage.UserList>,
|
||||||
Api.SystemManage.User
|
Api.SystemManage.User
|
||||||
>({
|
>({
|
||||||
@@ -175,9 +178,9 @@ const { columns, columnChecks, data, loading, getDataByPage, mobilePagination }
|
|||||||
searchParams.pageSize = params.pageSize ?? 10;
|
searchParams.pageSize = params.pageSize ?? 10;
|
||||||
},
|
},
|
||||||
columns: () => [
|
columns: () => [
|
||||||
{ prop: 'selection', type: 'selection', width: 48 },
|
{prop: 'selection', type: 'selection', width: 48},
|
||||||
{ prop: 'index', type: 'index', label: $t('common.index'), width: 64 },
|
{prop: 'index', type: 'index', label: $t('common.index'), width: 64},
|
||||||
{ prop: 'username', label: $t('page.system.user.userName'), minWidth: 140, showOverflowTooltip: true },
|
{prop: 'username', label: $t('page.system.user.userName'), minWidth: 140, showOverflowTooltip: true},
|
||||||
{
|
{
|
||||||
prop: 'nickname',
|
prop: 'nickname',
|
||||||
label: $t('page.system.user.nickName'),
|
label: $t('page.system.user.nickName'),
|
||||||
@@ -257,9 +260,9 @@ const { columns, columnChecks, data, loading, getDataByPage, mobilePagination }
|
|||||||
formatter: row => {
|
formatter: row => {
|
||||||
const state = getUserResignedState(row);
|
const state = getUserResignedState(row);
|
||||||
const stateMap: Record<UserResignedState, { type: UI.ThemeColor; label: App.I18n.I18nKey }> = {
|
const stateMap: Record<UserResignedState, { type: UI.ThemeColor; label: App.I18n.I18nKey }> = {
|
||||||
active: { type: 'success', label: 'page.system.user.resignedStateEnum.active' },
|
active: {type: 'success', label: 'page.system.user.resignedStateEnum.active'},
|
||||||
pending: { type: 'warning', label: 'page.system.user.resignedStateEnum.pending' },
|
pending: {type: 'warning', label: 'page.system.user.resignedStateEnum.pending'},
|
||||||
resigned: { type: 'info', label: 'page.system.user.resignedStateEnum.resigned' }
|
resigned: {type: 'info', label: 'page.system.user.resignedStateEnum.resigned'}
|
||||||
};
|
};
|
||||||
|
|
||||||
return <ElTag type={stateMap[state].type}>{$t(stateMap[state].label)}</ElTag>;
|
return <ElTag type={stateMap[state].type}>{$t(stateMap[state].label)}</ElTag>;
|
||||||
@@ -320,7 +323,7 @@ const { columns, columnChecks, data, loading, getDataByPage, mobilePagination }
|
|||||||
async function loadDeptTree() {
|
async function loadDeptTree() {
|
||||||
deptLoading.value = true;
|
deptLoading.value = true;
|
||||||
|
|
||||||
const { error, data: deptItems } = await fetchGetDeptList({
|
const {error, data: deptItems} = await fetchGetDeptList({
|
||||||
status: 0
|
status: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -422,7 +425,7 @@ function openOrgLeader(row: Api.SystemManage.Dept) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleDeleteDeptAction(row: Api.SystemManage.Dept) {
|
async function handleDeleteDeptAction(row: Api.SystemManage.Dept) {
|
||||||
const { error } = await fetchDeleteDept(row.id);
|
const {error} = await fetchDeleteDept(row.id);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
@@ -447,7 +450,7 @@ async function handleDeleteAction(row: Api.SystemManage.User) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error } = await fetchDeleteUser(row.id);
|
const {error} = await fetchDeleteUser(row.id);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
@@ -466,7 +469,7 @@ async function updateUserResignedAt(userId: number, value: number | null) {
|
|||||||
|
|
||||||
const user = detailResult.data;
|
const user = detailResult.data;
|
||||||
|
|
||||||
const { error } = await fetchUpdateUser({
|
const {error} = await fetchUpdateUser({
|
||||||
id: userId,
|
id: userId,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
nickname: user.nickname ?? null,
|
nickname: user.nickname ?? null,
|
||||||
@@ -518,7 +521,7 @@ async function handleBatchDelete() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error } = await fetchBatchDeleteUser(userCheckedRowKeys.value);
|
const {error} = await fetchBatchDeleteUser(userCheckedRowKeys.value);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
@@ -531,7 +534,7 @@ async function handleBatchDelete() {
|
|||||||
async function handleToggleStatus(row: Api.SystemManage.User, enabled: boolean) {
|
async function handleToggleStatus(row: Api.SystemManage.User, enabled: boolean) {
|
||||||
statusLoadingIds.value = [...statusLoadingIds.value, row.id];
|
statusLoadingIds.value = [...statusLoadingIds.value, row.id];
|
||||||
|
|
||||||
const { error } = await fetchUpdateUserStatus({
|
const {error} = await fetchUpdateUserStatus({
|
||||||
id: row.id,
|
id: row.id,
|
||||||
status: enabled ? 0 : 1
|
status: enabled ? 0 : 1
|
||||||
});
|
});
|
||||||
@@ -640,15 +643,21 @@ onMounted(async () => {
|
|||||||
<template #default>
|
<template #default>
|
||||||
<ElButton plain type="primary" :disabled="!currentDept" @click="openAdd">
|
<ElButton plain type="primary" :disabled="!currentDept" @click="openAdd">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-plus class="text-icon" />
|
<icon-ic-round-plus class="text-icon"/>
|
||||||
</template>
|
</template>
|
||||||
{{ $t('common.add') }}
|
{{ $t('common.add') }}
|
||||||
</ElButton>
|
</ElButton>
|
||||||
|
<ElButton plain type="primary" :disabled="!currentDept" @click="userManagementRelationVisible = true">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-plus class="text-icon"/>
|
||||||
|
</template>
|
||||||
|
带人关系
|
||||||
|
</ElButton>
|
||||||
<ElPopconfirm :title="$t('common.confirmDelete')" @confirm="handleBatchDelete">
|
<ElPopconfirm :title="$t('common.confirmDelete')" @confirm="handleBatchDelete">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<ElButton type="danger" plain :disabled="userCheckedRowKeys.length === 0">
|
<ElButton type="danger" plain :disabled="userCheckedRowKeys.length === 0">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-delete class="text-icon" />
|
<icon-ic-round-delete class="text-icon"/>
|
||||||
</template>
|
</template>
|
||||||
{{ $t('common.batchDelete') }}
|
{{ $t('common.batchDelete') }}
|
||||||
</ElButton>
|
</ElButton>
|
||||||
@@ -670,7 +679,7 @@ onMounted(async () => {
|
|||||||
:data="data"
|
:data="data"
|
||||||
@selection-change="handleUserSelectionChange"
|
@selection-change="handleUserSelectionChange"
|
||||||
>
|
>
|
||||||
<ElTableColumn v-for="col in columns" :key="String(col.prop)" v-bind="col" />
|
<ElTableColumn v-for="col in columns" :key="String(col.prop)" v-bind="col"/>
|
||||||
</ElTable>
|
</ElTable>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-20px flex justify-end">
|
<div class="mt-20px flex justify-end">
|
||||||
@@ -685,7 +694,7 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-else class="h-full flex items-center justify-center">
|
<div v-else class="h-full flex items-center justify-center">
|
||||||
<ElEmpty :description="$t('page.system.user.emptyOrg')" />
|
<ElEmpty :description="$t('page.system.user.emptyOrg')"/>
|
||||||
</div>
|
</div>
|
||||||
</ElCard>
|
</ElCard>
|
||||||
</div>
|
</div>
|
||||||
@@ -724,7 +733,17 @@ onMounted(async () => {
|
|||||||
@submitted="handleDeptSubmitted"
|
@submitted="handleDeptSubmitted"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UserOrgLeaderDialog v-model:visible="orgLeaderVisible" :dept="leaderDeptData" />
|
<UserOrgLeaderDialog v-model:visible="orgLeaderVisible" :dept="leaderDeptData"/>
|
||||||
|
|
||||||
|
<BusinessFormDialog
|
||||||
|
v-model="userManagementRelationVisible"
|
||||||
|
title="用户带人关系"
|
||||||
|
preset="lg"
|
||||||
|
:show-footer="false"
|
||||||
|
max-body-height="70vh"
|
||||||
|
>
|
||||||
|
<UserManagementRelation :fromUserIndex="true" :deptId="currentDeptId"/>
|
||||||
|
</BusinessFormDialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -734,4 +753,9 @@ onMounted(async () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 带人关系对话框内的搜索框样式优化
|
||||||
|
:deep(.business-form-dialog) {
|
||||||
|
width: 800px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user