|
|
@@ -92,31 +92,17 @@
|
|
|
<div class="custom-list">
|
|
|
<div class="list-header">项目</div>
|
|
|
<div class="list-content">
|
|
|
- <div v-if="leftDataItems && leftDataItems.length > 0">
|
|
|
- <div v-for="item in leftDataItems" :key="item.id">
|
|
|
- <div
|
|
|
- class="list-item level-0"
|
|
|
- :class="{ selected: isSelected(item.id, 'left') }"
|
|
|
- @click="selectItem(item.id, 'left')"
|
|
|
- >
|
|
|
- <span class="item-label">{{ item.label }}</span>
|
|
|
- </div>
|
|
|
- <div
|
|
|
- v-if="item.children && item.children.length > 0"
|
|
|
- class="list-children"
|
|
|
- >
|
|
|
- <div
|
|
|
- v-for="child in item.children"
|
|
|
- :key="child.id"
|
|
|
- class="list-item level-1"
|
|
|
- :class="{ selected: isSelected(child.id, 'left') }"
|
|
|
- @click="selectItem(child.id, 'left')"
|
|
|
- >
|
|
|
- <span class="item-label">{{ child.label }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <el-tree
|
|
|
+ v-if="leftDataItems && leftDataItems.length > 0"
|
|
|
+ ref="leftIndicatorTree"
|
|
|
+ class="indicator-tree"
|
|
|
+ :data="leftDataItems"
|
|
|
+ show-checkbox
|
|
|
+ node-key="id"
|
|
|
+ :props="treeProps"
|
|
|
+ :check-strictly="true"
|
|
|
+ @check="handleLeftCheck"
|
|
|
+ ></el-tree>
|
|
|
<Empty v-else description="暂无数据"></Empty>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -354,6 +340,10 @@
|
|
|
rightProportionAllData: [],
|
|
|
rightProportionTotalPages: 0,
|
|
|
rightProportionCurrentPage: 1,
|
|
|
+ treeProps: {
|
|
|
+ children: 'children',
|
|
|
+ label: 'label',
|
|
|
+ },
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -422,7 +412,7 @@
|
|
|
// 初始化图表
|
|
|
this.initCharts()
|
|
|
// 加载初始数据
|
|
|
- this.loadInitialData()
|
|
|
+ // this.loadInitialData()
|
|
|
// 监听窗口大小变化,调整图表大小
|
|
|
window.addEventListener('resize', this.handleResize)
|
|
|
},
|
|
|
@@ -920,6 +910,16 @@
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ // 左侧树选中变更
|
|
|
+ handleLeftCheck() {
|
|
|
+ const checkedKeys =
|
|
|
+ this.$refs.leftIndicatorTree?.getCheckedKeys?.() || []
|
|
|
+ this.leftSelectedItems = checkedKeys
|
|
|
+ .map((id) => this.findItemById(id, 'left'))
|
|
|
+ .filter(Boolean)
|
|
|
+ this.updateLeftChartsBySelection()
|
|
|
+ },
|
|
|
+
|
|
|
// 判断节点是否被选中
|
|
|
isSelected(id, type = 'left') {
|
|
|
const selectedItems =
|
|
|
@@ -1060,14 +1060,16 @@
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 获取树的父级(拥有子节点)id 列表
|
|
|
+ // 获取根节点(parentId 为 -1)的 id 列表与节点列表(与 history 页一致)
|
|
|
getParentIds(tree = []) {
|
|
|
const ids = []
|
|
|
const items = []
|
|
|
const dfs = (node) => {
|
|
|
- if (node.children && node.children.length > 0) {
|
|
|
+ if (String(node.parentId) === '-1') {
|
|
|
ids.push(node.id)
|
|
|
items.push(node)
|
|
|
+ }
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
node.children.forEach(dfs)
|
|
|
}
|
|
|
}
|
|
|
@@ -1075,16 +1077,6 @@
|
|
|
return { ids, items }
|
|
|
},
|
|
|
|
|
|
- // 获取根节点(parentId 为 -1 或未定义)的列表
|
|
|
- getRootItems(tree = []) {
|
|
|
- return (tree || []).filter(
|
|
|
- (item) =>
|
|
|
- item.parentId === '-1' ||
|
|
|
- item.parentId === null ||
|
|
|
- item.parentId === undefined
|
|
|
- )
|
|
|
- },
|
|
|
-
|
|
|
// 加载初始数据和树形结构
|
|
|
async loadInitialData() {
|
|
|
// 如果组件已销毁,不执行查询
|
|
|
@@ -1123,27 +1115,26 @@
|
|
|
// 构建左右侧指标树结构
|
|
|
this.leftDataItems = this.buildIndicatorTree(dedupedList)
|
|
|
this.rightDataItems = this.buildIndicatorTree(dedupedList)
|
|
|
- // 设置默认选中状态
|
|
|
- setTimeout(() => {
|
|
|
- // 从数据中获取所有父级节点作为默认选中项
|
|
|
- if (this.leftDataItems && this.leftDataItems.length > 0) {
|
|
|
+ // 设置默认选中状态(左侧与 history 页一致)
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.setLeftDefaultSelection()
|
|
|
+ if (this.rightDataItems && this.rightDataItems.length > 0) {
|
|
|
const { items: parentItems } = this.getParentIds(
|
|
|
- this.leftDataItems
|
|
|
+ this.rightDataItems
|
|
|
)
|
|
|
-
|
|
|
- // 设置左右侧默认选中项(单选,优先第一个父级,否则第一个节点)
|
|
|
const defaultItem =
|
|
|
- parentItems.length > 0 ? parentItems[0] : this.leftDataItems[0]
|
|
|
- this.leftSelectedItems = defaultItem ? [defaultItem] : []
|
|
|
+ parentItems.length > 0
|
|
|
+ ? parentItems[0]
|
|
|
+ : this.rightDataItems[0] || null
|
|
|
this.rightSelectedItems = defaultItem ? [defaultItem] : []
|
|
|
-
|
|
|
- // 更新图表
|
|
|
- if (!this.isDestroyed) {
|
|
|
- this.updateLeftChartsBySelection()
|
|
|
- this.updateRightChartsBySelection()
|
|
|
- }
|
|
|
+ } else {
|
|
|
+ this.rightSelectedItems = []
|
|
|
+ }
|
|
|
+ if (!this.isDestroyed) {
|
|
|
+ this.updateLeftChartsBySelection()
|
|
|
+ this.updateRightChartsBySelection()
|
|
|
}
|
|
|
- }, 100)
|
|
|
+ })
|
|
|
} catch (error) {
|
|
|
// 如果是请求中止错误,静默处理
|
|
|
if (error && error.message && !error.message.includes('aborted')) {
|
|
|
@@ -1204,25 +1195,9 @@
|
|
|
this.leftDataSource = normalizedDataSource
|
|
|
// 更新左侧指标树结构
|
|
|
this.leftDataItems = this.buildIndicatorTree(dedupedList)
|
|
|
- // 校验原选中项;无效则清空
|
|
|
- if (this.leftSelectedItems.length > 0) {
|
|
|
- const selectedId = this.leftSelectedItems[0].id
|
|
|
- const found = this.findItemById(selectedId, 'left')
|
|
|
- if (!found) {
|
|
|
- this.leftSelectedItems = []
|
|
|
- }
|
|
|
- }
|
|
|
- // 若无选中项,默认选中 parentId 为 -1 的根节点(单选),否则用第一个节点
|
|
|
- if (this.leftSelectedItems.length === 0) {
|
|
|
- const rootItems = this.getRootItems(this.leftDataItems)
|
|
|
- const defaultItem =
|
|
|
- (rootItems && rootItems.length > 0
|
|
|
- ? rootItems[0]
|
|
|
- : this.leftDataItems && this.leftDataItems.length > 0
|
|
|
- ? this.leftDataItems[0]
|
|
|
- : null) || null
|
|
|
- this.leftSelectedItems = defaultItem ? [defaultItem] : []
|
|
|
- }
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.setLeftDefaultSelection()
|
|
|
+ })
|
|
|
// 重置左侧占比轮播页
|
|
|
this.leftProportionCurrentPage = 1
|
|
|
// 根据选中的项更新图表
|
|
|
@@ -1299,7 +1274,7 @@
|
|
|
}
|
|
|
// 若无选中项,默认选中 parentId 为 -1 的根节点(单选),否则用第一个节点
|
|
|
if (this.rightSelectedItems.length === 0) {
|
|
|
- const rootItems = this.getRootItems(this.rightDataItems)
|
|
|
+ const { items: rootItems } = this.getParentIds(this.rightDataItems)
|
|
|
const defaultItem =
|
|
|
(rootItems && rootItems.length > 0
|
|
|
? rootItems[0]
|
|
|
@@ -1336,25 +1311,44 @@
|
|
|
endYear: '2025',
|
|
|
}
|
|
|
// 重置左侧树形选择
|
|
|
- if (this.$refs.leftDataTree) {
|
|
|
- this.$refs.leftDataTree.clearChecked()
|
|
|
- }
|
|
|
- // 重置后默认选中第一个父级节点(单选)
|
|
|
- if (this.leftDataItems && this.leftDataItems.length > 0) {
|
|
|
- const { items: parentItems } = this.getParentIds(this.leftDataItems)
|
|
|
- const defaultItem =
|
|
|
- parentItems.length > 0 ? parentItems[0] : this.leftDataItems[0]
|
|
|
- this.leftSelectedItems = defaultItem ? [defaultItem] : []
|
|
|
- } else {
|
|
|
- // 如果没有数据,清空选中项
|
|
|
- this.leftSelectedItems = []
|
|
|
+ if (this.$refs.leftIndicatorTree) {
|
|
|
+ this.$refs.leftIndicatorTree.clearChecked()
|
|
|
}
|
|
|
+ // 重置后默认选中(与 history 页一致)
|
|
|
+ this.setLeftDefaultSelection()
|
|
|
// 重置左侧图表
|
|
|
this.leftProportionCurrentPage = 1
|
|
|
this.updateLeftTrendChart()
|
|
|
this.updateLeftProportionChart()
|
|
|
},
|
|
|
|
|
|
+ // 左侧默认选中:优先根节点(parentId 为 -1),否则第一个叶子
|
|
|
+ setLeftDefaultSelection() {
|
|
|
+ if (!this.$refs.leftIndicatorTree || !this.leftDataItems.length) {
|
|
|
+ this.leftSelectedItems = []
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const { ids: parentIds } = this.getParentIds(this.leftDataItems)
|
|
|
+ if (parentIds.length > 0) {
|
|
|
+ this.leftSelectedItems = parentIds
|
|
|
+ .map((id) => this.findItemById(id, 'left'))
|
|
|
+ .filter(Boolean)
|
|
|
+ this.$refs.leftIndicatorTree.setCheckedKeys(parentIds)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const leafIds = this.getLeafIds(this.leftDataItems)
|
|
|
+ if (leafIds.length > 0) {
|
|
|
+ const firstLeaf = leafIds[0]
|
|
|
+ this.leftSelectedItems = [
|
|
|
+ this.findItemById(firstLeaf, 'left'),
|
|
|
+ ].filter(Boolean)
|
|
|
+ this.$refs.leftIndicatorTree.setCheckedKeys([firstLeaf])
|
|
|
+ } else {
|
|
|
+ this.leftSelectedItems = []
|
|
|
+ this.$refs.leftIndicatorTree.setCheckedKeys([])
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
// 右侧重置按钮点击事件
|
|
|
handleRightReset() {
|
|
|
// 重置右侧搜索表单
|
|
|
@@ -1451,6 +1445,14 @@
|
|
|
overflow-x: hidden;
|
|
|
}
|
|
|
|
|
|
+ .indicator-tree {
|
|
|
+ border: 1px solid #e8e8e8;
|
|
|
+ height: 500px;
|
|
|
+ max-height: 500px;
|
|
|
+ overflow-y: auto;
|
|
|
+ overflow-x: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
.chart-section {
|
|
|
flex: 1;
|
|
|
display: flex;
|