整合前端

This commit is contained in:
砂糖
2026-04-13 17:04:38 +08:00
parent 69609a2cb1
commit 5d4794c9bd
915 changed files with 144259 additions and 0 deletions

View 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>