fix: 组织架构根部门自动创建为公司名

1. 注册时自动创建根部门(公司名,level=0),作为组织架构顶层节点
2. 部门列表查询时兼容历史组织:若无任何部门,自动补建根部门
3. 创建部门时若未指定父部门,默认挂到根部门下
4. 前端 OrgChart:
   - 根部门显示"公司"标签,不可编辑/删除
   - 新增部门时默认父部门为根部门
   - 上级部门下拉移除"无(根部门)"选项,改为列出所有可选部门

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-16 09:16:31 +08:00
parent c2c99126be
commit 9a826c16b0
3 changed files with 72 additions and 24 deletions
+35 -23
View File
@@ -61,10 +61,13 @@ export default function OrgChart() {
}
const renderTree = (items: any[], parentId: string | null, level: number = 0) => {
return items.filter(d => d.parentId === parentId).map(d => (
return items.filter(d => d.parentId === parentId).map(d => {
// 根部门(level=0 且无父部门)为公司节点,不可编辑/删除
const isRoot = d.level === 0 && !d.parentId
return (
<div key={d.id}>
<div
className="flex items-center gap-2 py-2 px-3 hover:bg-gray-50 rounded-md"
className={`flex items-center gap-2 py-2 px-3 rounded-md ${isRoot ? 'bg-primary/5 font-medium' : 'hover:bg-gray-50'}`}
style={{ marginLeft: level * 24 }}
>
{items.some(c => c.parentId === d.id) ? (
@@ -74,29 +77,35 @@ export default function OrgChart() {
) : (
<span className="w-4" />
)}
<Building2 className="w-4 h-4 text-primary" />
<Building2 className={`w-4 h-4 ${isRoot ? 'text-primary' : 'text-gray-400'}`} />
<span className="flex-1 text-sm">{d.name}</span>
{isRoot && <span className="text-xs text-primary"></span>}
<span className="text-xs text-gray-400">{d._count?.employees || 0}</span>
<button
onClick={() => {
setEditing(d)
setForm({ name: d.name, parentId: d.parentId || '', sortOrder: d.sortOrder, description: d.description || '' })
setShowModal(true)
}}
className="text-gray-400 hover:text-primary"
>
<Edit className="w-3.5 h-3.5" />
</button>
<button
onClick={() => { if (confirm(`确认删除部门"${d.name}"`)) deleteMutation.mutate(d.id) }}
className="text-gray-400 hover:text-danger"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
{!isRoot && (
<>
<button
onClick={() => {
setEditing(d)
setForm({ name: d.name, parentId: d.parentId || '', sortOrder: d.sortOrder, description: d.description || '' })
setShowModal(true)
}}
className="text-gray-400 hover:text-primary"
>
<Edit className="w-3.5 h-3.5" />
</button>
<button
onClick={() => { if (confirm(`确认删除部门"${d.name}"`)) deleteMutation.mutate(d.id) }}
className="text-gray-400 hover:text-danger"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</>
)}
</div>
{expanded.has(d.id) && renderTree(items, d.id, level + 1)}
</div>
))
)
})
}
return (
@@ -130,7 +139,9 @@ export default function OrgChart() {
<span className="text-sm font-medium"></span>
<Button size="sm" onClick={() => {
setEditing(null)
setForm({ name: '', parentId: '', sortOrder: 0, description: '' })
// 默认父部门为根部门(level=0 且 parentId=null
const root = departments.find((d: any) => d.level === 0 && !d.parentId)
setForm({ name: '', parentId: root?.id || '', sortOrder: 0, description: '' })
setShowModal(true)
}}>
<Plus className="w-4 h-4 mr-1" />
@@ -158,9 +169,10 @@ export default function OrgChart() {
<div>
<Label></Label>
<Select value={form.parentId} onChange={(e) => setForm({ ...form, parentId: e.target.value })}>
<option value=""></option>
{departments.filter((d: any) => d.id !== editing?.id).map((d: any) => (
<option key={d.id} value={d.id}>{d.name}</option>
<option key={d.id} value={d.id}>
{d.level === 0 && !d.parentId ? `${d.name}(公司)` : d.name}
</option>
))}
</Select>
</div>