<template>
<div class="department-container">
<el-card class="box-card">
<template #header>
<div class="card-header">
<h3>部门管理</h3>
<el-button type="primary" @click="handleAdd">新增部门</el-button>
</div>
</template>
<CubeListToolbarSearch
:on-search="SearchData"
:on-reset="ResetData"
:on-callback="callback"
/>
<el-alert
:title="`共找到 ${total} 个部门,其中根部门 ${tableData.length} 个。`"
type="info"
:closable="false"
show-icon
style="margin-bottom: 16px"
/>
<el-table
:data="tableData"
border
style="width: 100%"
v-loading="loading"
row-key="id"
default-expand-all
:tree-props="{ children: 'children' }"
:show-header="true"
:highlight-current-row="true">
<el-table-column prop="name" label="部门名称" min-width="140" show-overflow-tooltip />
<el-table-column prop="code" label="部门代码" width="110" show-overflow-tooltip />
<el-table-column label="部门全名" min-width="160" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.fullName || '-' }}
</template>
</el-table-column>
<el-table-column label="上级部门" min-width="110" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.parentName || '-' }}
</template>
</el-table-column>
<el-table-column label="部门路径" min-width="180" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.path || '-' }}
</template>
</el-table-column>
<el-table-column prop="level" label="层级" width="60" align="center" />
<el-table-column prop="sort" label="排序" width="60" align="center" />
<el-table-column label="启用" width="70" align="center">
<template #default="scope">
<el-tag :type="scope.row.enable ? 'success' : 'danger'" size="small">
{{ scope.row.enable ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="可见" width="70" align="center">
<template #default="scope">
<el-tag :type="scope.row.visible ? 'success' : 'danger'" size="small">
{{ scope.row.visible ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="管理者" width="90" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.managerName || '-' }}
</template>
</el-table-column>
<el-table-column prop="createUser" label="创建者" width="90" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" width="150" show-overflow-tooltip />
<el-table-column label="备注" min-width="120" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.remark || '-' }}
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right" align="center">
<template #default="scope">
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 显示统计信息 -->
<div class="table-footer">
<span>共 {{ total }} 个部门</span>
</div>
</el-card>
<!-- 部门表单对话框 -->
<el-dialog v-model="dialogVisible" :title="formType === 'add' ? '新增部门' : '编辑部门'" width="600px">
<el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
<el-form-item label="部门名称" prop="name">
<el-input v-model="form.name" placeholder="请输入部门名称" />
</el-form-item>
<el-form-item label="部门代码" prop="code">
<el-input v-model="form.code" placeholder="请输入部门代码" />
</el-form-item>
<el-form-item label="部门全名" prop="fullName">
<el-input v-model="form.fullName" placeholder="请输入部门全名" />
</el-form-item>
<el-form-item label="上级部门" prop="parentID">
<el-select v-model="form.parentID" placeholder="请选择上级部门" clearable filterable>
<el-option label="无上级" :value="0" />
<el-option
v-for="dept in departmentOptions"
:key="dept.id"
:label="`${dept.name}(${dept.code || dept.id})`"
:value="dept.id"
/>
</el-select>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" placeholder="同级内排序" />
</el-form-item>
<el-form-item label="管理者" prop="managerId">
<el-select v-model="form.managerId" placeholder="请选择管理者" clearable filterable>
<el-option label="无管理者" :value="0" />
<!-- 这里需要用户列表,暂时留空 -->
</el-select>
</el-form-item>
<el-form-item label="当前路径" v-if="formType === 'edit'">
<el-input :value="form.path" readonly />
</el-form-item>
<el-form-item label="创建信息" v-if="formType === 'edit'">
<el-input :value="`${form.createUser} (${form.createTime})`" readonly />
</el-form-item>
<el-form-item label="启用状态" prop="enable">
<el-switch v-model="form.enable" :active-value="true" :inactive-value="false" />
</el-form-item>
<el-form-item label="可见状态" prop="visible">
<el-switch v-model="form.visible" :active-value="true" :inactive-value="false" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
import { request } from '@core/utils/request';
import { apiDataToList, handleDeleteOperation, handleFormSubmit } from '@core/utils/api-helpers';
import CubeListToolbarSearch from '@core/components/CubeListToolbarSearch.vue';
import type { BaseEntity } from '@core/types/common';
// 定义部门类型接口
interface Department extends BaseEntity {
tenantId: number;
code: string;
name: string;
fullName: string | null;
parentID: number;
level: number;
sort: number;
enable: boolean;
visible: boolean;
managerId: number;
ex1: number;
ex2: number;
ex3: number;
ex4: string | null;
ex5: string | null;
ex6: string | null;
createUserID: number;
createIP: string;
updateUserID: number;
updateIP: string;
tenantName: string | null;
managerName: string | null;
parentName: string | null;
parentPath: string | null;
path: string;
children?: Department[];
hasChildren?: boolean;
}
// 表格数据
const tableData = ref<Department[]>([]);
const loading = ref(false);
const total = ref(0);
const departmentOptions = ref<Department[]>([]);
// 页面请求参数
const queryParams = reactive({
q: '',// 搜索关键字
pageIndex: 1,
pageSize: 10000, // 获取全部数据用于构建树
});
// 表单相关
const dialogVisible = ref(false);
const formType = ref<'add' | 'edit'>('add');
const formRef = ref<FormInstance | null>(null);
const form = reactive<Department>({
id: 0,
tenantId: 0,
code: '',
name: '',
fullName: null,
parentID: 0,
level: 0,
sort: 0,
enable: true,
visible: true,
managerId: 0,
ex1: 0,
ex2: 0,
ex3: 0,
ex4: null,
ex5: null,
ex6: null,
createUser: '',
createUserID: 0,
createIP: '',
createTime: '',
updateUser: '',
updateUserID: 0,
updateIP: '',
updateTime: '',
remark: '',
tenantName: null,
managerName: null,
parentName: null,
parentPath: null,
path: '',
});
// 表单验证规则
const formRules = reactive<FormRules>({
name: [
{ required: true, message: '请输入部门名称', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入部门代码', trigger: 'blur' }
]
});
// 数据预处理函数,确保null值被正确处理
const preprocessDepartmentData = (data: Department[]): Department[] => {
return data.map(item => ({
...item,
fullName: item.fullName || '',
remark: item.remark || '',
managerName: item.managerName || '',
parentName: item.parentName || '',
parentPath: item.parentPath || '',
ex4: item.ex4 || '',
ex5: item.ex5 || '',
ex6: item.ex6 || '',
tenantName: item.tenantName || '',
}));
};
// 构建树结构并排序(参考菜单页面的实现)
const buildTreeData = (data: Department[]): Department[] => {
if (!data || data.length === 0) {
return [];
}
// 创建节点映射表和根节点数组
const nodeMap = new Map<number, Department>();
const roots: Department[] = [];
// 步骤1:初始化所有节点
data.forEach((item) => {
const node = { ...item };
// 确保清理之前的树状属性
node.children = [];
delete node.hasChildren;
nodeMap.set(item.id, node);
});
// 步骤2:建立父子关系
data.forEach((item) => {
const node = nodeMap.get(item.id)!;
if (item.parentID === 0) {
// parentID为0的是根节点
roots.push(node);
} else {
// 找到父节点并建立关系
const parent = nodeMap.get(item.parentID);
if (parent) {
if (!parent.children) {
parent.children = [];
}
parent.children.push(node);
} else {
// 如果找不到父节点,作为根节点处理
roots.push(node);
}
}
});
// 步骤3:清理空的children数组并排序
const processNode = (node: Department) => {
if (node.children && node.children.length === 0) {
delete node.children;
} else if (node.children && node.children.length > 0) {
// 对子节点排序
node.children.sort((a, b) => a.sort - b.sort);
// 递归处理子节点
node.children.forEach((child) => processNode(child));
}
};
// 对根节点排序
roots.sort((a, b) => a.sort - b.sort);
// 处理所有节点
roots.forEach((root) => processNode(root));
return roots;
};
// 组件回调函数
const callback = (e?: Record<string, unknown>) => {
console.log(e?.type, e?.params);
const query = Object.assign(queryParams, e?.params || {});
console.log('queryParams:', query);
loadData();
};
// 搜索数据处理
const SearchData = (e?: Record<string, unknown>) => {
Object.assign(queryParams, e || {});
console.log('SearchData:', queryParams);
};
// 重置数据处理
const ResetData = (e?: Record<string, unknown>) => {
Object.assign(queryParams, e || {});
console.log('ResetData:', queryParams);
};
// 加载数据
const loadData = async () => {
loading.value = true;
try {
const response = await request.get('/Admin/Department', {
params: queryParams
});
let dataList: Department[] = [];
// 处理API响应数据
const { list } = apiDataToList<Department>(response);
dataList = preprocessDepartmentData(list);
// 构建树结构并排序
tableData.value = buildTreeData(dataList);
total.value = dataList.length;
// 同时保存平铺的数据用于部门选项(排除当前编辑的部门)
departmentOptions.value = dataList.filter((item) => item.id !== form.id);
} catch (error) {
console.error('加载部门数据失败:', error);
tableData.value = [];
departmentOptions.value = [];
total.value = 0;
} finally {
loading.value = false;
}
};
// 新增
const handleAdd = () => {
formType.value = 'add';
Object.assign(form, {
id: 0,
tenantId: 0,
code: '',
name: '',
fullName: null,
parentID: 0,
level: 0,
sort: 0,
enable: true,
visible: true,
managerId: 0,
ex1: 0,
ex2: 0,
ex3: 0,
ex4: null,
ex5: null,
ex6: null,
createUser: '',
createUserID: 0,
createIP: '',
createTime: '',
updateUser: '',
updateUserID: 0,
updateIP: '',
updateTime: '',
remark: null,
tenantName: null,
managerName: null,
parentName: null,
parentPath: null,
path: '',
});
dialogVisible.value = true;
};
// 编辑
const handleEdit = (row: Department) => {
formType.value = 'edit';
Object.assign(form, { ...row });
// 更新部门选项,排除自己
departmentOptions.value = departmentOptions.value.filter(item => item.id !== row.id);
dialogVisible.value = true;
};
// 删除
const handleDelete = (row: Department) => {
handleDeleteOperation(
() => request.delete('/Admin/Department', { params: { id: row.id } }),
loadData,
'确认删除该部门吗?'
);
};
// 提交表单
const submitForm = async () => {
const apiCall = async () => {
if (formType.value === 'add') {
await request.post('/Admin/Department', form);
} else {
await request.put('/Admin/Department', form);
}
};
const onSuccess = () => {
dialogVisible.value = false;
loadData();
};
await handleFormSubmit(formRef.value, apiCall, onSuccess);
};
// 初始化加载数据
onMounted(() => {
loadData();
});
</script>
<style scoped>
.department-container {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.table-footer {
margin-top: 20px;
display: flex;
justify-content: flex-end;
color: #606266;
font-size: 14px;
}
</style>
|