feat: 完成部门管理前端页面

功能:
- dept.js API文件:部门CRUD接口
- dept.vue 页面:树形表格展示部门层级
- 支持添加子部门、编辑、删除
- 上级部门选择(树形下拉)
- 表单验证和错误处理
- 路由配置:/system/dept
This commit is contained in:
zhangjf 2026-02-15 17:56:23 +08:00
parent 29207d2e3c
commit bcd163a093
3 changed files with 327 additions and 0 deletions

View File

@ -0,0 +1,64 @@
import request from '../utils/request'
/**
* 获取部门树
*/
export const getDeptTree = () => {
return request({
url: '/sys/api/v1/dept/tree',
method: 'get'
})
}
/**
* 获取部门列表
*/
export const getDeptList = (params) => {
return request({
url: '/sys/api/v1/dept/list',
method: 'get',
params
})
}
/**
* 获取部门详情
*/
export const getDeptById = (id) => {
return request({
url: `/sys/api/v1/dept/${id}`,
method: 'get'
})
}
/**
* 创建部门
*/
export const createDept = (data) => {
return request({
url: '/sys/api/v1/dept',
method: 'post',
data
})
}
/**
* 更新部门
*/
export const updateDept = (id, data) => {
return request({
url: `/sys/api/v1/dept/${id}`,
method: 'put',
data
})
}
/**
* 删除部门
*/
export const deleteDept = (id) => {
return request({
url: `/sys/api/v1/dept/${id}`,
method: 'delete'
})
}

View File

@ -31,6 +31,12 @@ const routes = [
component: () => import('../views/system/user.vue'),
meta: { title: '用户管理' }
},
{
path: 'dept',
name: 'Dept',
component: () => import('../views/system/dept.vue'),
meta: { title: '部门管理' }
},
{
path: 'role',
name: 'Role',

View File

@ -0,0 +1,257 @@
<template>
<div class="page-container">
<el-card>
<template #header>
<div class="card-header">
<span>部门管理</span>
<el-button type="primary" @click="handleAdd(null)">新增部门</el-button>
</div>
</template>
<el-table
:data="tableData"
row-key="deptId"
border
v-loading="loading"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
default-expand-all
>
<el-table-column prop="deptName" label="部门名称" width="250" />
<el-table-column prop="deptCode" label="部门编码" width="150" />
<el-table-column prop="leader" label="负责人" width="120" />
<el-table-column prop="phone" label="联系电话" width="140" />
<el-table-column prop="email" label="邮箱" width="200" />
<el-table-column prop="sortOrder" label="排序" width="80" align="center" />
<el-table-column prop="status" label="状态" width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
{{ scope.row.status === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="300" fixed="right">
<template #default="scope">
<el-button type="primary" link @click="handleAdd(scope.row)">添加子部门</el-button>
<el-button type="primary" link @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 新增/编辑对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="上级部门">
<el-tree-select
v-model="form.parentId"
:data="deptTreeOptions"
:props="{ label: 'deptName', value: 'deptId', children: 'children' }"
placeholder="请选择上级部门"
check-strictly
:render-after-expand="false"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="部门名称" prop="deptName">
<el-input v-model="form.deptName" placeholder="请输入部门名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门编码" prop="deptCode">
<el-input v-model="form.deptCode" placeholder="请输入部门编码" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="负责人">
<el-input v-model="form.leader" placeholder="请输入负责人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系电话">
<el-input v-model="form.phone" placeholder="请输入联系电话" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="邮箱">
<el-input v-model="form.email" placeholder="请输入邮箱" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="排序">
<el-input-number v-model="form.sortOrder" :min="0" :max="999" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDeptTree, createDept, updateDept, deleteDept } from '../../api/dept'
const loading = ref(false)
const tableData = ref([])
const deptTreeOptions = ref([])
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formRef = ref(null)
const form = reactive({
deptId: null,
parentId: 0,
deptName: '',
deptCode: '',
leader: '',
phone: '',
email: '',
sortOrder: 0,
remark: ''
})
const rules = {
deptName: [{ required: true, message: '请输入部门名称', trigger: 'blur' }],
deptCode: [{ required: true, message: '请输入部门编码', trigger: 'blur' }]
}
//
const loadDeptTree = async () => {
loading.value = true
try {
const res = await getDeptTree()
tableData.value = res || []
//
deptTreeOptions.value = [
{ deptId: 0, deptName: '顶级部门', children: res || [] }
]
} catch (error) {
console.error('加载部门树失败:', error)
ElMessage.error('加载数据失败')
} finally {
loading.value = false
}
}
//
const handleAdd = (row) => {
dialogTitle.value = row ? '添加子部门' : '新增部门'
resetForm()
if (row) {
form.parentId = row.deptId
}
dialogVisible.value = true
}
//
const handleEdit = (row) => {
dialogTitle.value = '编辑部门'
resetForm()
Object.assign(form, {
deptId: row.deptId,
parentId: row.parentId,
deptName: row.deptName,
deptCode: row.deptCode,
leader: row.leader,
phone: row.phone,
email: row.email,
sortOrder: row.sortOrder,
remark: row.remark
})
dialogVisible.value = true
}
//
const resetForm = () => {
form.deptId = null
form.parentId = 0
form.deptName = ''
form.deptCode = ''
form.leader = ''
form.phone = ''
form.email = ''
form.sortOrder = 0
form.remark = ''
}
//
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
} catch (error) {
ElMessage.warning('请检查表单填写是否完整')
return
}
try {
if (form.deptId) {
await updateDept(form.deptId, form)
ElMessage.success('更新成功')
} else {
await createDept(form)
ElMessage.success('创建成功')
}
dialogVisible.value = false
await loadDeptTree()
} catch (error) {
console.error('保存失败:', error)
ElMessage.error(error.message || '操作失败')
}
}
//
const handleDelete = (row) => {
ElMessageBox.confirm('确定要删除该部门吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
await deleteDept(row.deptId)
ElMessage.success('删除成功')
await loadDeptTree()
} catch (error) {
console.error('删除失败:', error)
ElMessage.error(error.message || '删除失败')
}
})
}
onMounted(() => {
loadDeptTree()
})
</script>
<style scoped>
.page-container {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>