Files
pqs-9100_client/frontend/src/views/home/components/tree.vue
2025-08-20 08:51:01 +08:00

321 lines
9.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="plan_tree">
<div class="search_view">
<el-input
placeholder="请输入计划名称"
clearable
v-model="searchForm.planName"
show-word-limit
maxlength="32"
></el-input>
<el-tooltip content="检测计划列表" placement="top">
<Menu
style="width: 26px; height: 26px; margin-left: 8px; cursor: pointer; color: var(--el-color-primary)"
@click.stop="detail()"
/>
</el-tooltip>
</div>
<div class="tree_container">
<el-tree
:data="data"
ref="treeRef"
:filter-node-method="filterNode"
:props="defaultProps"
node-key="id"
class="filter-tree"
:highlight-current="true"
default-expand-all
:default-checked-keys="defaultChecked"
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<span class="custom-tree-node" style="display: flex; align-items: center">
<!-- 父节点图标 -->
<Platform
v-if="!data.pid"
style="width: 18px; height: 18px; margin-right: 8px"
:style="{
color: node.label == '未检' ? '#fac858' : node.label == '检测中' ? '#ee6666' : '#91cc75'
}"
/>
<!-- 节点名称 -->
<span>{{ node.label }}</span>
<!-- 子节点右侧图标 + tooltip -->
<el-tooltip
v-if="
node.label != '未检' &&
node.label != '检测中' &&
node.label != '检测完成' &&
hasChildrenInPlanTable(node.data)
"
placement="top"
:manual="true"
content="子计划信息"
>
<List
v-auth.plan="'add_subplan'"
@click.stop="childDetail(node.data)"
style="
width: 16px;
height: 16px;
margin-left: 8px;
cursor: pointer;
color: var(--el-color-primary);
"
/>
</el-tooltip>
</span>
</template>
</el-tree>
</div>
</div>
<SourceOpen ref="openSourceView" :width="width" :height="height + 175"></SourceOpen>
</template>
<script lang="ts" setup>
import { type Plan } from '@/api/plan/interface'
import { List, Menu, Platform } from '@element-plus/icons-vue'
import { nextTick, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useCheckStore } from '@/stores/modules/check'
import { ElTooltip } from 'element-plus'
import SourceOpen from '@/views/plan/planList/components/childrenPlan.vue'
import { getPlanList } from '@/api/plan/plan.ts'
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
import { useDictStore } from '@/stores/modules/dict'
const openSourceView = ref()
const router = useRouter()
const checkStore = useCheckStore()
const filterText = ref('')
const treeRef = ref()
const data: any = ref([])
const modeStore = useModeStore()
const dictStore = useDictStore()
const defaultProps = {
children: 'children',
label: 'name',
pid: 'pid'
}
const searchForm = ref({
planName: ''
})
const defaultChecked = ref<string[]>([]) // 明确类型为 number[]
const tree = ref(false) //确保左侧树高凉只执行一次
const getTreeData = (val: any) => {
defaultChecked.value = []
// 遍历 val 的每个 children过滤掉 pid !== '0'
data.value = val
for (let item of data.value) {
if (item.children.length > 0) {
let node = item.children[0]
defaultChecked.value.push(node.id)
checkStore.setPlan(node)
// 高亮显示第一个节点
// 使用 nextTick 确保在 DOM 更新后调用 setCurrentKey
nextTick(() => {
treeRef.value?.setCurrentKey(node.id)
idd.value = node.id
})
// 找到第一个符合条件的 children 后跳出循环
break
}
}
}
//点击表格后左侧树刷新,高亮显示对应节点
const clickTableToTree = (val: any, id: any) => {
defaultChecked.value = []
data.value = val
let node = ref('')
if (data.value.length > 0) {
for (let i = 0; i < data.value.length; i++) {
for (let j = 0; j < data.value[i].children.length; j++) {
if (data.value[i].children[j].id == id) {
node.value = data.value[i].children[j].id
break
}
}
}
// 使用 nextTick 确保在 DOM 更新后调用 setCurrentKey
nextTick(() => {
treeRef.value?.setCurrentKey(node.value)
idd.value = node.value
})
}
}
const { updateSelectedTreeNode, width, height, planTable } = defineProps<{
updateSelectedTreeNode: Function
width: number
height: number
planTable?: Array<[]>
}>()
watch(
() => searchForm.value.planName,
val => {
treeRef.value!.filter(val)
},
{
deep: true
}
)
const hasChildrenInPlanTable = (nodeData: Plan.ResPlan) => {
try {
// 在 planTable 中查找对应的节点数据
const foundItem = tableData.value.find((item: any) => item.id === nodeData.id)
// 检查是否有 children 且 children 数组不为空
return foundItem && Array.isArray(foundItem.children) && foundItem.children.length > 0
} catch (error) {
console.error('检查子节点时出错:', error)
return false
}
}
const idd = ref('')
const handleNodeClick = (data: Plan.ResPlan) => {
if (data.name === '未检' || data.name === '检测中' || data.name === '检测完成') {
// 如果是父节点,不执行任何操作
//console.log('父节点不执行任何操作');
// 设置当前高亮节点
nextTick(() => {
treeRef.value?.setCurrentKey(idd.value)
})
return
}
idd.value = data.id
checkStore.setPlan(data)
updateSelectedTreeNode(data.id)
}
const filterNode = (value: string, data: any) => {
if (!value) return true
return data.name.includes(value)
}
// 点击详情
const detail = () => {
router.push('/plan/planList')
}
const childDetail = (data: Plan.ResPlan) => {
const filteredPlans = tableData.value.filter(item => item.id === data.id)
// 确保有匹配项再访问 [0]
if (filteredPlans.length > 0 && openSourceView.value) {
openSourceView.value.open('检测计划详情', filteredPlans[0])
}
}
function buildTree(flatList: any[]): any[] {
const map = new Map()
const tree: any[] = []
// First, create a map of all items by id for fast lookup
flatList.forEach(item => {
map.set(item.id, { ...item, children: [] })
})
// Then, assign each item to its parent's children array
flatList.forEach(item => {
if (item.fatherPlanId && map.has(item.fatherPlanId)) {
map.get(item.fatherPlanId).children.push(map.get(item.id))
} else if (item.fatherPlanId === '0') {
// Items with fatherPlanId '0' are root nodes
tree.push(map.get(item.id))
}
})
return tree
}
const tableData = ref<any[]>([])
onMounted(async () => {
if (modeStore.currentMode != '比对式') return
const patternId = dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.id
const result = await getPlanList({ patternId: patternId })
tableData.value = buildTree(result.data as any[])
})
defineExpose({ getTreeData, clickTableToTree })
</script>
<style lang="scss" scoped>
.plan_tree {
height: 100%;
display: flex;
flex-direction: column;
background-color: #fff;
.search_view {
width: 100%;
height: auto;
display: flex;
justify-content: space-between;
padding: 0 5px;
box-sizing: border-box;
align-items: center;
.el-input {
margin-top: 6px;
}
}
.el-input {
width: 100%;
margin: 0 10px 10px 0;
}
.tree_container {
height: 100%;
width: 100%;
flex: 1;
overflow-y: auto;
overflow-x: auto;
.el-tree {
// height: 100%;
width: auto;
}
}
}
.filter-tree {
// border: 1px solid #dcdfe6;
min-width: 100%;
height: 97%;
display: inline-block;
overflow: auto;
margin-top: 12px;
}
//.filter-tree span {
// font-size: 16px;
// display:block;
// overflow:hidden;
// word-break:keep-all;
// white-space:nowrap;
// text-overflow:ellipsis;
// padding-right: 12px;
//}
.leftBox {
// float: left;
// width: 20%;
height: 100%;
width: 100%;
}
.left {
height: calc(100% - 45px);
overflow: auto;
}
/* 设置滚动条宽度 */
:deep(.bodyTwo ::-webkit-scrollbar) {
width: 3px !important;
height: 6px !important;
}
</style>