zhang_nuo
3 小时以前 a6667d3351aea21abc363c35285e066ee37f40b2
feat(qualityManagement): add batch selection for product binding tree

- 新增产品树顶部工具栏,包含全选和清空按钮,支持快速批量
操作所有可选叶子节点
- 为树节点增加自定义模板,在有子节点的项旁添加"全选"按钮,
支持一键选中该节点下的所有叶子节点
- 新增 collectLeafNodeIds、handleCheckAll、handleCheckNone、
handleCheckNodeChildren 方法,实现叶子节点收集与批量勾选逻辑
- 调整搜索框与操作按钮的布局样式,新增自定义树节点相关样式
- 涉及文件:src/views/qualityManagement/metricBinding/index.vue
已修改1个文件
102 ■■■■■ 文件已修改
src/views/qualityManagement/metricBinding/index.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/metricBinding/index.vue
@@ -124,13 +124,19 @@
      @close="closeBindingDialog"
    >
      <div class="binding-dialog">
        <el-input
          v-model="productSearchKeyword"
          placeholder="搜索产品"
          clearable
          prefix-icon="Search"
          class="binding-search"
        />
        <div class="tree-toolbar">
          <el-input
            v-model="productSearchKeyword"
            placeholder="搜索产品"
            clearable
            prefix-icon="Search"
            class="binding-search"
          />
          <div class="tree-actions">
            <el-button size="small" @click="handleCheckAll">全选</el-button>
            <el-button size="small" @click="handleCheckNone">清空</el-button>
          </div>
        </div>
        <el-tree
          ref="productTreeRef"
          :key="productTreeKey"
@@ -143,7 +149,18 @@
          :filter-node-method="filterProductNode"
          class="product-binding-tree"
          @check="handleProductTreeCheck"
        />
        >
          <template #default="{ node, data }">
            <span class="custom-tree-node">
              <span class="node-label">{{ node.label }}</span>
              <span v-if="data.children?.length" class="node-action">
                <el-button link type="primary" size="small" @click.stop="handleCheckNodeChildren(data)">
                  全选
                </el-button>
              </span>
            </span>
          </template>
        </el-tree>
        <div v-if="selectedProductLabels.length" class="selected-products">
          <div class="selected-title">已选择({{ selectedProductLabels.length }})</div>
          <div class="selected-tags">
@@ -343,6 +360,45 @@
const handleProductTreeCheck = () => {
  selectedProductIds.value = productTreeRef.value?.getCheckedKeys(true) || []
}
// 收集所有叶子节点 id
const collectLeafNodeIds = (nodes) => {
  const ids = []
  const walk = (list) => {
    ;(list || []).forEach((node) => {
      if (node.children?.length) {
        walk(node.children)
      } else if (!node.disabled && node.id != null) {
        ids.push(node.id)
      }
    })
  }
  walk(nodes)
  return ids
}
// 全选:选中所有叶子节点
const handleCheckAll = () => {
  const leafIds = collectLeafNodeIds(productTreeData.value)
  productTreeRef.value?.setCheckedKeys(leafIds, false)
  selectedProductIds.value = leafIds
}
// 清空:取消所有选中
const handleCheckNone = () => {
  productTreeRef.value?.setCheckedKeys([], false)
  selectedProductIds.value = []
}
// 选中当前节点下的所有叶子节点
const handleCheckNodeChildren = (data) => {
  const leafIds = collectLeafNodeIds(data.children || [])
  // 合并当前已选中的 id
  const currentChecked = productTreeRef.value?.getCheckedKeys(true) || []
  const merged = Array.from(new Set([...currentChecked, ...leafIds]))
  productTreeRef.value?.setCheckedKeys(merged, false)
  selectedProductIds.value = merged
}
const removeSelectedProduct = (id) => {
@@ -614,8 +670,20 @@
  gap: 12px;
}
.tree-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
}
.binding-search {
  width: 100%;
  flex: 1;
}
.tree-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}
.product-binding-tree {
@@ -626,6 +694,22 @@
  padding: 8px;
}
.custom-tree-node {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding-right: 8px;
}
.node-label {
  flex: 1;
}
.node-action {
  flex-shrink: 0;
}
.selected-products {
  border-top: 1px solid #ebeef5;
  padding-top: 12px;