| | |
| | | @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" |
| | |
| | | :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"> |
| | |
| | | |
| | | 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) => { |
| | |
| | | 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 { |
| | |
| | | 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; |