您遇到的 `loadOrganizationTree` 方法不存在的问题是因为在 `OrganizationTree` 组件中没有正确定义和暴露这个方法。让我修复这个问题并提供完整的解决方案:
您遇到的 `loadOrganizationTree` 方法不存在的问题是因为在 `OrganizationTree` 组件中没有正确定义和暴露这个方法。让我修复这个问题并提供完整的解决方案:
### 修复后的 `src/components/organization/OrganizationTree.vue`
```vue
<template>
<div class="q-pa-md">
<div class="row justify-between items-center q-mb-md">
<div class="text-h5">组织架构管理</div>
<q-btn icon="add" label="新建部门" color="primary" @click="showCreateDialog(null)" />
</div>
<q-tree
ref="treeRef"
:nodes="treeNodes"
node-key="id"
selected-color="primary"
v-model:selected="selectedNodeId"
v-model:expanded="expandedNodes"
default-expand-all
no-connectors
dense
accordion
>
<template v-slot:default-header="prop">
<div class="row items-center">
<q-icon
:name="getNodeIcon(prop.node)"
:color="getNodeColor(prop.node)"
size="sm"
class="q-mr-sm"
/>
<div class="column">
<div class="text-weight-medium">{{ prop.node.name }}</div>
<div class="text-caption text-blue-grey-5">
{{ prop.node.node_type_display }}
</div>
</div>
<q-space />
<div v-if="prop.node.manager_details" class="text-caption">
负责人: {{ prop.node.manager_details?.full_name || '未指定' }}
</div>
<q-btn flat round icon="more_vert" size="sm">
<q-menu auto-close>
<q-list dense style="min-width: 150px">
<q-item clickable @click="showCreateDialog(prop.node.id)">
<q-item-section>添加子部门</q-item-section>
</q-item>
<q-item clickable @click="editDepartment(prop.node.id)">
<q-item-section>编辑部门</q-item-section>
</q-item>
<q-item clickable @click="moveNode(prop.node.id, null)" v-if="prop.node.parent">
<q-item-section>移动到根节点</q-item-section>
</q-item>
<q-separator />
<q-item clickable @click="deleteDepartment(prop.node.id)" class="text-negative">
<q-item-section>删除部门</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</template>
</q-tree>
<department-dialog
v-model="departmentDialog.show"
:parent-id="departmentDialog.parentId"
:department-id="departmentDialog.departmentId"
@saved="handleDepartmentSaved"
/>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, defineExpose } from 'vue'
import { QTree } from 'quasar'
import useOrganization from 'src/composable/useOrganization'
import type { OrganizationTreeNode } from 'src/types/auth/organization'
import { OrganizationNodeType } from 'src/types/auth/organization'
// 组合式API
const { fetchDepartmentTree } = useOrganization()
// 树组件引用
const treeRef = ref<InstanceType<typeof QTree> | null>(null)
// 状态管理
const treeNodes = ref<OrganizationTreeNode[]>([])
const selectedNodeId = ref<string | null>(null)
const expandedNodes = ref<string[]>([])
// 对话框状态
const departmentDialog = ref({
show: false,
parentId: null as string | null,
departmentId: null as string | null,
})
// 获取组织架构树
const loadOrganizationTree = async () => {
try {
const data = await fetchDepartmentTree()
if (data) {
treeNodes.value = data as OrganizationTreeNode[]
// 展开所有节点
if (data.length > 0) {
expandedNodes.value = getAllNodeIds(data as OrganizationTreeNode[])
}
}
} catch (error) {
console.error('加载组织架构树失败:', error)
}
}
// 递归获取所有节点ID
const getAllNodeIds = (nodes: OrganizationTreeNode[]): string[] => {
let ids: string[] = []
nodes.forEach((node) => {
ids.push(node.id)
if (node.children && node.children.length > 0) {
ids = [...ids, ...getAllNodeIds(node.children)]
}
})
return ids
}
// 节点图标
const getNodeIcon = (node: OrganizationTreeNode) => {
switch (node.node_type) {
case OrganizationNodeType.ROOT_ORGANIZATION:
return 'corporate_fare'
case OrganizationNodeType.SECURITY_SERVICE_PROVIDER:
return 'security'
case OrganizationNodeType.HAZARD_MANAGEMENT_PROVIDER:
return 'construction'
case OrganizationNodeType.PRODUCTION_UNIT:
return 'factory'
case OrganizationNodeType.SAFETY_DEPARTMENT:
return 'verified_user'
case OrganizationNodeType.REGULATORY_AGENCY:
return 'gavel'
case OrganizationNodeType.THIRD_PARTY_AUDITOR:
return 'assignment'
default:
return 'business'
}
}
// 节点颜色
const getNodeColor = (node: OrganizationTreeNode) => {
if (!node.is_active) return 'grey'
switch (node.node_type) {
case OrganizationNodeType.ROOT_ORGANIZATION:
return 'primary'
case OrganizationNodeType.SECURITY_SERVICE_PROVIDER:
return 'teal'
case OrganizationNodeType.HAZARD_MANAGEMENT_PROVIDER:
return 'orange'
case OrganizationNodeType.PRODUCTION_UNIT:
return 'blue-grey'
case OrganizationNodeType.SAFETY_DEPARTMENT:
return 'green'
case OrganizationNodeType.REGULATORY_AGENCY:
return 'red'
case OrganizationNodeType.THIRD_PARTY_AUDITOR:
return 'purple'
default:
return 'blue-grey'
}
}
// 显示创建对话框
const showCreateDialog = (parentId: string | null) => {
departmentDialog.value = {
show: true,
parentId,
departmentId: null,
}
}
// 编辑部门
const editDepartment = (departmentId: string) => {
departmentDialog.value = {
show: true,
parentId: null,
departmentId,
}
}
// 处理部门保存
const handleDepartmentSaved = async () => {
await loadOrganizationTree()
}
// 删除部门
const deleteDepartment = (id: string) => {
// 实际项目中应实现删除逻辑
console.log(`Delete department: ${id}`)
}
// 移动节点
const moveNode = (id: string, parentId: string | null) => {
// 实际项目中应实现移动逻辑
console.log(`Move department ${id} to parent ${parentId}`)
}
// 初始化加载
onMounted(async () => {
await loadOrganizationTree()
})
// 修复:暴露方法供父组件调用
defineExpose({
loadOrganizationTree
})
</script>
<style scoped></style>
```
### 修复后的 `src/pages/organization/OrganizationTreePage.vue`
```vue
<template>
<q-page class="q-pa-md">
<div class="row justify-between items-center q-mb-md">
<div>
<div class="text-h5">组织架构管理</div>
<div class="text-caption text-grey-7">
{{ route.meta.description }}
</div>
</div>
<div class="row q-gutter-sm">
<q-btn icon="add" label="新建部门" color="primary" @click="showCreateDialog(null)" />
<q-btn icon="refresh" label="刷新" color="secondary" @click="refreshTree" />
</div>
</div>
<organization-tree ref="orgTree" />
<q-dialog v-model="showDepartmentDialog">
<DepartmentDialog
:model-value="showDepartmentDialog"
@update:model-value="showDepartmentDialog = $event"
:parent-id="currentParentId"
@saved="handleDepartmentSaved"
/>
</q-dialog>
</q-page>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import OrganizationTree from 'src/components/organization/OrganizationTree.vue'
import DepartmentDialog from 'src/components/organization/DepartmentDialog.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const orgTree = ref<InstanceType<typeof OrganizationTree> | null>(null)
const showDepartmentDialog = ref(false)
const currentParentId = ref<string | null>(null)
// 显示创建对话框
const showCreateDialog = (parentId: string | null) => {
currentParentId.value = parentId
showDepartmentDialog.value = true
}
// 处理部门保存
const handleDepartmentSaved = () => {
refreshTree()
showDepartmentDialog.value = false
}
// 刷新树
const refreshTree = () => {
if (orgTree.value && typeof orgTree.value.loadOrganizationTree === 'function') {
orgTree.value.loadOrganizationTree()
}
}
// 组件挂载后刷新树
onMounted(() => {
refreshTree()
})
</script>
```
### 主要修复点:
1. **在 `OrganizationTree` 组件中暴露方法**:
- 使用 `defineExpose` 暴露 `loadOrganizationTree` 方法
- 确保方法在组件中正确定义
2. **在父组件中正确调用方法**:
- 添加了方法存在性检查 `typeof orgTree.value.loadOrganizationTree === 'function'`
- 使用 `InstanceType<typeof OrganizationTree>` 确保类型正确
3. **错误处理增强**:
- 在 `loadOrganizationTree` 方法中添加了 `try-catch` 错误处理
- 添加了错误日志输出
4. **类型安全改进**:
- 使用 `defineExpose` 明确暴露的方法
- 在父组件中使用类型安全的引用
5. **代码组织优化**:
- 在 `OrganizationTree` 组件中保留了原有的功能
- 在 `OrganizationTreePage` 中简化了刷新逻辑
这个修复方案完全解决了 `loadOrganizationTree` 方法不存在的问题,同时保持了组件的功能完整性。现在当您在父组件中调用 `refreshTree()` 方法时,应该能够成功刷新组织树。