整合前端
This commit is contained in:
374
ruoyi-ui/src/views/hrm/org/index.vue
Normal file
374
ruoyi-ui/src/views/hrm/org/index.vue
Normal file
@@ -0,0 +1,374 @@
|
||||
<template>
|
||||
<div class="dept-page">
|
||||
<el-card shadow="never" class="dept-card">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="header-title">组织架构管理</span>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" type="primary" icon="el-icon-plus" @click="handleAdd">新增部门</el-button>
|
||||
<el-button size="small" icon="el-icon-refresh" @click="loadDeptList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dept-content">
|
||||
<div class="dept-tree-wrapper">
|
||||
<el-tree
|
||||
v-loading="loading"
|
||||
:data="deptTree"
|
||||
:props="{ label: 'deptName', children: 'children' }"
|
||||
node-key="deptId"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
:expand-on-click-node="false"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<span slot-scope="{ node, data }" class="tree-node">
|
||||
<span class="node-label">{{ node.label }}</span>
|
||||
<span class="node-actions">
|
||||
<el-button type="text" size="mini" icon="el-icon-plus" @click.stop="handleAdd(data)">添加</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-edit" @click.stop="handleEdit(data)">编辑</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-delete" @click.stop="handleDelete(data)">删除</el-button>
|
||||
</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
:visible.sync="dialogVisible"
|
||||
width="600px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
ref="deptFormRef"
|
||||
:model="deptForm"
|
||||
:rules="deptRules"
|
||||
label-width="100px"
|
||||
size="small"
|
||||
>
|
||||
<el-form-item label="部门名称" prop="deptName">
|
||||
<el-input
|
||||
v-model="deptForm.deptName"
|
||||
placeholder="请输入部门名称,如:技术部、市场部等"
|
||||
clearable
|
||||
/>
|
||||
<div class="form-hint">建议使用简洁明了的部门名称</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="上级部门" prop="parentId">
|
||||
<el-cascader
|
||||
v-model="deptForm.parentId"
|
||||
:options="deptTree"
|
||||
:props="{ label: 'deptName', value: 'deptId', children: 'children', emitPath: false, checkStrictly: true }"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="选择上级部门,留空则为顶级部门"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<div class="form-hint">留空则创建为顶级部门</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="显示顺序" prop="orderNum">
|
||||
<el-input-number
|
||||
v-model="deptForm.orderNum"
|
||||
:min="0"
|
||||
placeholder="数字越小越靠前"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<div class="form-hint">用于控制部门在列表中的显示顺序</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="负责人">
|
||||
<el-input
|
||||
v-model="deptForm.leader"
|
||||
placeholder="请输入负责人姓名(可选)"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="联系电话">
|
||||
<el-input
|
||||
v-model="deptForm.phone"
|
||||
placeholder="请输入联系电话(可选)"
|
||||
clearable
|
||||
maxlength="11"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="邮箱">
|
||||
<el-input
|
||||
v-model="deptForm.email"
|
||||
placeholder="请输入邮箱地址(可选)"
|
||||
clearable
|
||||
type="email"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="部门状态" prop="status">
|
||||
<el-radio-group v-model="deptForm.status">
|
||||
<el-radio label="0">正常</el-radio>
|
||||
<el-radio label="1">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="deptForm.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="补充说明(可选)"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSubmit">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listDept, addDept, updateDept, delDept } from '@/api/system/dept'
|
||||
|
||||
export default {
|
||||
name: 'HrmDept',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
deptTree: [],
|
||||
dialogVisible: false,
|
||||
dialogTitle: '新增部门',
|
||||
submitting: false,
|
||||
deptForm: {
|
||||
deptId: undefined,
|
||||
deptName: '',
|
||||
parentId: undefined,
|
||||
orderNum: 0,
|
||||
leader: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
status: '0',
|
||||
remark: ''
|
||||
},
|
||||
deptRules: {
|
||||
deptName: [
|
||||
{ required: true, message: '请输入部门名称', trigger: 'blur' },
|
||||
{ min: 2, max: 30, message: '部门名称长度在 2 到 30 个字符', trigger: 'blur' }
|
||||
],
|
||||
orderNum: [
|
||||
{ required: true, message: '请输入显示顺序', trigger: 'blur' }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: '请选择部门状态', trigger: 'change' }
|
||||
],
|
||||
email: [
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadDeptList()
|
||||
},
|
||||
methods: {
|
||||
loadDeptList() {
|
||||
this.loading = true
|
||||
listDept({})
|
||||
.then(res => {
|
||||
const data = res.data || []
|
||||
this.deptTree = this.buildTree(data)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载部门列表失败:', err)
|
||||
this.$message.error('加载部门列表失败')
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
buildTree(list) {
|
||||
const map = {}
|
||||
const roots = []
|
||||
|
||||
// 创建映射
|
||||
list.forEach(item => {
|
||||
map[item.deptId] = { ...item, children: [] }
|
||||
})
|
||||
|
||||
// 构建树结构
|
||||
list.forEach(item => {
|
||||
const node = map[item.deptId]
|
||||
if (item.parentId && map[item.parentId]) {
|
||||
map[item.parentId].children.push(node)
|
||||
} else {
|
||||
roots.push(node)
|
||||
}
|
||||
})
|
||||
|
||||
return roots
|
||||
},
|
||||
handleNodeClick() {
|
||||
// 节点点击事件(预留)
|
||||
},
|
||||
handleAdd(parent) {
|
||||
this.dialogTitle = '新增部门'
|
||||
this.deptForm = {
|
||||
deptId: undefined,
|
||||
deptName: '',
|
||||
parentId: parent ? parent.deptId : undefined,
|
||||
orderNum: 0,
|
||||
leader: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
status: '0',
|
||||
remark: ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.deptFormRef) {
|
||||
this.$refs.deptFormRef.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
handleEdit(data) {
|
||||
this.dialogTitle = '编辑部门'
|
||||
this.deptForm = {
|
||||
deptId: data.deptId,
|
||||
deptName: data.deptName,
|
||||
parentId: data.parentId,
|
||||
orderNum: data.orderNum || 0,
|
||||
leader: data.leader || '',
|
||||
phone: data.phone || '',
|
||||
email: data.email || '',
|
||||
status: data.status || '0',
|
||||
remark: data.remark || ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.deptFormRef) {
|
||||
this.$refs.deptFormRef.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(data) {
|
||||
if (data.children && data.children.length > 0) {
|
||||
this.$message.warning('该部门下存在子部门,请先删除子部门')
|
||||
return
|
||||
}
|
||||
this.$confirm(`确认删除部门"${data.deptName}"吗?`, '提示', {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delDept(data.deptId).then(() => {
|
||||
this.$message.success('删除成功')
|
||||
this.loadDeptList()
|
||||
}).catch(err => {
|
||||
console.error('删除失败:', err)
|
||||
this.$message.error('删除失败')
|
||||
})
|
||||
})
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.deptFormRef.validate(valid => {
|
||||
if (!valid) return
|
||||
|
||||
this.submitting = true
|
||||
const api = this.deptForm.deptId ? updateDept : addDept
|
||||
const payload = { ...this.deptForm }
|
||||
|
||||
api(payload)
|
||||
.then(() => {
|
||||
this.$message.success(this.deptForm.deptId ? '更新成功' : '新增成功')
|
||||
this.dialogVisible = false
|
||||
this.loadDeptList()
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('操作失败:', err)
|
||||
this.$message.error('操作失败')
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitting = false
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dept-page {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dept-card {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.dept-content {
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.dept-tree-wrapper {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
|
||||
.node-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.node-actions {
|
||||
display: none;
|
||||
gap: 4px;
|
||||
|
||||
.el-button {
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .node-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user