|
|
@@ -1,16 +1,29 @@
|
|
|
<template>
|
|
|
<div class="layout">
|
|
|
<div class="tree-container">
|
|
|
- <el-tree
|
|
|
- :key="treeKey"
|
|
|
- :data="treeData"
|
|
|
- :props="defaultProps"
|
|
|
- lazy
|
|
|
- :load="loadNode"
|
|
|
- @node-click="handleNodeClick"
|
|
|
- highlight-current
|
|
|
- >
|
|
|
- </el-tree>
|
|
|
+ <!-- 搜索框 -->
|
|
|
+ <div class="search-box">
|
|
|
+ <el-input
|
|
|
+ v-model="searchText"
|
|
|
+ placeholder="搜索..."
|
|
|
+ prefix-icon="el-icon-search"
|
|
|
+ clearable
|
|
|
+ @input="handleSearch"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="tree-wrapper">
|
|
|
+ <el-tree
|
|
|
+ :key="treeKey"
|
|
|
+ :data="filteredTreeData"
|
|
|
+ :props="defaultProps"
|
|
|
+ @node-click="handleNodeClick"
|
|
|
+ highlight-current
|
|
|
+ :default-expanded-keys="expandedKeys"
|
|
|
+ node-key="id"
|
|
|
+ ref="tree"
|
|
|
+ >
|
|
|
+ </el-tree>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div class="form-container">
|
|
|
<div v-if="showForm">
|
|
|
@@ -90,7 +103,10 @@ export default {
|
|
|
data() {
|
|
|
return {
|
|
|
treeData: [],
|
|
|
- treeKey: 0, // Add a key to force re-rendering
|
|
|
+ filteredTreeData: [],
|
|
|
+ searchText: '',
|
|
|
+ expandedKeys: [],
|
|
|
+ treeKey: 0,
|
|
|
form: {
|
|
|
methodName: '',
|
|
|
step: '',
|
|
|
@@ -107,71 +123,71 @@ export default {
|
|
|
formId: null,
|
|
|
formLabelWidth: '110px',
|
|
|
defaultProps: {
|
|
|
- label: 'name',
|
|
|
+ label: 'label',
|
|
|
children: 'children',
|
|
|
- isLeaf: 'isLeaf'
|
|
|
+ isLeaf: (data) => !data.children || data.children.length === 0
|
|
|
},
|
|
|
- showForm: false // 控制表单显示
|
|
|
+ showForm: false
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
- async loadNode(node, resolve) {
|
|
|
- const level = node.level
|
|
|
- const params = { level: level + 1 } // 要加载的是下一层
|
|
|
-
|
|
|
- // 构建路径参数
|
|
|
- if (level >= 1) {
|
|
|
- const pathNames = this.getNodePathNames(node)
|
|
|
-
|
|
|
- // 根据层级设置对应的name参数
|
|
|
- for (let i = 0; i < level; i++) {
|
|
|
- if (pathNames[i]) {
|
|
|
- params[`name${i + 1}`] = pathNames[i]
|
|
|
- }
|
|
|
- }
|
|
|
+ // 搜索处理函数
|
|
|
+ handleSearch() {
|
|
|
+ if (!this.searchText.trim()) {
|
|
|
+ // 搜索框为空时,显示完整树数据,收起所有节点
|
|
|
+ this.filteredTreeData = JSON.parse(JSON.stringify(this.treeData))
|
|
|
+ this.expandedKeys = []
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
- try {
|
|
|
- const response = await getExperimentalTree(params)
|
|
|
- const children = response.data.map((item) => ({
|
|
|
- id: item.id || item.name,
|
|
|
- name: item.name,
|
|
|
- isLeaf: level + 1 === 5 // 第五层是叶子节点
|
|
|
- }))
|
|
|
- resolve(children)
|
|
|
- } catch (error) {
|
|
|
- console.error('Failed to fetch tree data:', error)
|
|
|
- resolve([])
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 获取从根节点到当前节点的路径名称数组
|
|
|
- getNodePathNames(node) {
|
|
|
- const pathNames = []
|
|
|
- let currentNode = node
|
|
|
+ const searchText = this.searchText.toLowerCase()
|
|
|
+ const expandedKeysSet = new Set()
|
|
|
|
|
|
- // 从当前节点向上遍历到根节点
|
|
|
- while (currentNode && currentNode.level > 0) {
|
|
|
- pathNames.unshift(currentNode.data.name)
|
|
|
- currentNode = currentNode.parent
|
|
|
+ // 深度搜索函数
|
|
|
+ const deepSearch = (nodes, parentExpanded = false) => {
|
|
|
+ return nodes.filter(node => {
|
|
|
+ const labelMatch = node.label && node.label.toLowerCase().includes(searchText)
|
|
|
+ let childrenMatch = false
|
|
|
+
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
+ const filteredChildren = deepSearch(node.children, labelMatch || parentExpanded)
|
|
|
+ childrenMatch = filteredChildren.length > 0
|
|
|
+
|
|
|
+ if (filteredChildren.length > 0) {
|
|
|
+ node.children = filteredChildren
|
|
|
+ // 如果当前节点或父节点需要展开,或者子节点匹配,则展开当前节点
|
|
|
+ if (labelMatch || parentExpanded || childrenMatch) {
|
|
|
+ expandedKeysSet.add(node.id)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ node.children = []
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保留匹配的叶子节点或其父节点
|
|
|
+ return labelMatch || childrenMatch || parentExpanded
|
|
|
+ })
|
|
|
}
|
|
|
+
|
|
|
+ // 执行搜索
|
|
|
+ this.filteredTreeData = deepSearch(JSON.parse(JSON.stringify(this.treeData)))
|
|
|
|
|
|
- return pathNames
|
|
|
+ // 设置展开的节点
|
|
|
+ this.expandedKeys = Array.from(expandedKeysSet)
|
|
|
},
|
|
|
|
|
|
async handleNodeClick(node) {
|
|
|
- if (node.isLeaf) {
|
|
|
- // Fetch form data for leaf nodes
|
|
|
+ if (node.level === 5) { // 只有第五层是叶子节点,有表单数据
|
|
|
try {
|
|
|
- const response = await getExperimental({ id: node.id })
|
|
|
+ const response = await getExperimental({ id: node.dataId })
|
|
|
this.form = response.data
|
|
|
- this.showForm = true // 显示表单
|
|
|
+ this.showForm = true
|
|
|
} catch (error) {
|
|
|
console.error('Failed to fetch form data:', error)
|
|
|
- this.showForm = false // 获取数据失败也不显示表单
|
|
|
+ this.showForm = false
|
|
|
}
|
|
|
} else {
|
|
|
- this.showForm = false // 非叶子节点不显示表单
|
|
|
+ this.showForm = false
|
|
|
}
|
|
|
},
|
|
|
|
|
|
@@ -222,14 +238,11 @@ export default {
|
|
|
},
|
|
|
async created() {
|
|
|
try {
|
|
|
- this.treeData = []; // Clear treeData before loading
|
|
|
- const response = await getExperimentalTree({ level: 1 })
|
|
|
- this.treeData = response.data.map((item) => ({
|
|
|
- id: item.id || item.name,
|
|
|
- name: item.name,
|
|
|
- isLeaf: false
|
|
|
- }))
|
|
|
- this.treeKey++; // Increment the key to force re-rendering
|
|
|
+ const response = await getExperimentalTree()
|
|
|
+ // 使用接口返回的完整树数据
|
|
|
+ this.treeData = response.data
|
|
|
+ this.filteredTreeData = JSON.parse(JSON.stringify(this.treeData))
|
|
|
+ this.treeKey++
|
|
|
} catch (error) {
|
|
|
console.error('Failed to fetch initial tree data:', error)
|
|
|
}
|
|
|
@@ -240,25 +253,44 @@ export default {
|
|
|
<style lang="less" scoped>
|
|
|
.layout {
|
|
|
display: flex;
|
|
|
- height: 100%;
|
|
|
+ width: 100%;
|
|
|
+ height: 100vh;
|
|
|
}
|
|
|
+
|
|
|
.tree-container {
|
|
|
width: 380px;
|
|
|
+ height: 100vh;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
border-right: 1px solid #dcdfe6;
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.search-box {
|
|
|
padding: 10px;
|
|
|
- overflow-y: auto;
|
|
|
+ border-bottom: 1px solid #dcdfe6;
|
|
|
+}
|
|
|
+
|
|
|
+.tree-wrapper {
|
|
|
+ height: calc(100vh - 170px);
|
|
|
+ overflow: auto;
|
|
|
+ padding: 0 10px 10px 10px;
|
|
|
}
|
|
|
+
|
|
|
.form-container {
|
|
|
- flex: 1;
|
|
|
- overflow-y: auto;
|
|
|
+ width: calc(100% - 380px);
|
|
|
+ height: 100vh;
|
|
|
+ overflow: auto;
|
|
|
padding: 20px;
|
|
|
- height: calc(100vh - 40px); /* 新增:设置固定高度,确保滚动条生效 */
|
|
|
+ background: #fff;
|
|
|
}
|
|
|
+
|
|
|
.title {
|
|
|
font-size: 20px;
|
|
|
font-weight: bold;
|
|
|
color: #636569;
|
|
|
}
|
|
|
+
|
|
|
.header {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
@@ -266,6 +298,7 @@ export default {
|
|
|
padding-bottom: 10px;
|
|
|
border-bottom: 1px solid #dcdfe6;
|
|
|
}
|
|
|
+
|
|
|
.subtitle {
|
|
|
text-align: center;
|
|
|
font-size: 18px;
|
|
|
@@ -274,6 +307,7 @@ export default {
|
|
|
margin-top: 50px;
|
|
|
color: #636569;
|
|
|
}
|
|
|
+
|
|
|
.empty-state {
|
|
|
display: flex;
|
|
|
justify-content: center;
|
|
|
@@ -284,73 +318,106 @@ export default {
|
|
|
}
|
|
|
|
|
|
#pdfDom {
|
|
|
- padding: 20px 100px;
|
|
|
- box-sizing: border-box;
|
|
|
-
|
|
|
- .info-container {
|
|
|
- margin-bottom: 20px !important;
|
|
|
- }
|
|
|
+ padding: 20px 100px;
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ .info-container {
|
|
|
+ margin-bottom: 20px !important;
|
|
|
+ }
|
|
|
|
|
|
- ::v-deep {
|
|
|
- .info-item {
|
|
|
- .title {
|
|
|
- height: 20px;
|
|
|
- line-height: 20px;
|
|
|
- font-size: 16px;
|
|
|
- font-weight: bold;
|
|
|
- margin-bottom: 10px;
|
|
|
- color: #38b7b3;
|
|
|
- font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
|
|
-
|
|
|
- .ibps-icon-star {
|
|
|
- color: #fb9600;
|
|
|
- margin-right: 5px;
|
|
|
- }
|
|
|
+ ::v-deep {
|
|
|
+ .info-item {
|
|
|
+ .title {
|
|
|
+ height: 20px;
|
|
|
+ line-height: 20px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ color: #38b7b3;
|
|
|
+ font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
|
|
+
|
|
|
+ .ibps-icon-star {
|
|
|
+ color: #fb9600;
|
|
|
+ margin-right: 5px;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- .el-form-item {
|
|
|
- margin-bottom: 0 !important;
|
|
|
+ .el-form-item {
|
|
|
+ margin-bottom: 0 !important;
|
|
|
|
|
|
- &__label {
|
|
|
- font-size: 14px !important;
|
|
|
- color: #606266;
|
|
|
- }
|
|
|
+ &__label {
|
|
|
+ font-size: 14px !important;
|
|
|
+ color: #606266;
|
|
|
+ }
|
|
|
|
|
|
- &__content {
|
|
|
- .el-input,
|
|
|
- .el-select,
|
|
|
- .el-input-number {
|
|
|
- width: 100%;
|
|
|
- }
|
|
|
+ &__content {
|
|
|
+ .el-input,
|
|
|
+ .el-select,
|
|
|
+ .el-input-number {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
|
|
|
- .el-textarea .el-input__count {
|
|
|
- padding: 0 5px;
|
|
|
- line-height: initial;
|
|
|
- }
|
|
|
+ .el-textarea .el-input__count {
|
|
|
+ padding: 0 5px;
|
|
|
+ line-height: initial;
|
|
|
+ }
|
|
|
|
|
|
- .el-radio,
|
|
|
- .el-checkbox {
|
|
|
- margin-right: 10px;
|
|
|
- }
|
|
|
+ .el-radio,
|
|
|
+ .el-checkbox {
|
|
|
+ margin-right: 10px;
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- .el-table th.el-table__cell > .cell,
|
|
|
- .el-table td.el-table__cell {
|
|
|
- color: #606266;
|
|
|
- font-size: 14px !important;
|
|
|
- }
|
|
|
+ .el-table th.el-table__cell > .cell,
|
|
|
+ .el-table td.el-table__cell {
|
|
|
+ color: #606266;
|
|
|
+ font-size: 14px !important;
|
|
|
+ }
|
|
|
|
|
|
- .el-button--mini {
|
|
|
- padding: 5px 12px;
|
|
|
- }
|
|
|
+ .el-button--mini {
|
|
|
+ padding: 5px 12px;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
-
|
|
|
// 取消当前页面中 el-tree 的边框
|
|
|
::v-deep .el-tree {
|
|
|
border: none;
|
|
|
}
|
|
|
+
|
|
|
+// 使用统一的、更美观的滚动条样式
|
|
|
+.tree-wrapper,
|
|
|
+.form-container {
|
|
|
+ scrollbar-width: thin;
|
|
|
+ scrollbar-color: #c1c1c1 transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.tree-wrapper::-webkit-scrollbar,
|
|
|
+.form-container::-webkit-scrollbar {
|
|
|
+ width: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.tree-wrapper::-webkit-scrollbar-track,
|
|
|
+.form-container::-webkit-scrollbar-track {
|
|
|
+ background: transparent;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.tree-wrapper::-webkit-scrollbar-thumb,
|
|
|
+.form-container::-webkit-scrollbar-thumb {
|
|
|
+ background: #c1c1c1;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.tree-wrapper::-webkit-scrollbar-thumb:hover,
|
|
|
+.form-container::-webkit-scrollbar-thumb:hover {
|
|
|
+ background: #a8a8a8;
|
|
|
+}
|
|
|
+// 移除滚动条箭头
|
|
|
+.tree-wrapper::-webkit-scrollbar-button,
|
|
|
+.form-container::-webkit-scrollbar-button {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
</style>
|