当前位置: 首页> 教育> 就业 > 外贸自建站平台排名_微分销系统定制开发_锦州网站seo_seo优化工作有哪些

外贸自建站平台排名_微分销系统定制开发_锦州网站seo_seo优化工作有哪些

时间:2025/7/27 21:26:31来源:https://blog.csdn.net/weixin_45638884/article/details/147135958 浏览次数:2次
外贸自建站平台排名_微分销系统定制开发_锦州网站seo_seo优化工作有哪些

基于 Element Plus 实现高效树形穿梭框组件

在这里插入图片描述

组件概述

本组件实现了一个基于 Element Plus 的双树形结构穿梭框,支持以下核心功能:

  • 树形结构数据展示
  • 节点多选与批量转移
  • 展开状态记忆
  • 双向数据同步
  • 节点禁用与过滤
  • 全选/全不选功能(待完善)

核心实现思路

1. 组件结构设计

采用经典的左右面板+操作按钮布局:

<div class="transfer-container"><!-- 左侧树形面板 --><div class="tree-panel">...</div><!-- 操作按钮 --><div class="operation-buttons"><el-button @click="moveToRight"></el-button><el-button @click="moveToLeft"></el-button></div><!-- 右侧列表面板 --><div class="list-panel">...</div>
</div>

2. 数据双向绑定

通过 Vue 的响应式系统实现数据同步:

const props = defineProps({checkValue: { type: Array, default: () => [] },titles: { type: Array, default: () => ['选择区域', '已选区域'] },treeData: { type: Array, required: true }
})const emit = defineEmits(['update:checkValue'])

3. 树形数据处理

实现节点过滤和状态同步:

// 左侧树禁用已选节点
const disableSelectedNodes = (treeData, selectedIds) => {// 使用深拷贝处理树形数据// 过滤已选中的叶子节点
}// 右侧树数据过滤
const hadleRightTreeData = (treeData) => {// 仅保留选中节点及其父节点
}

4. 展开状态记忆

通过节点事件实现展开状态管理:

// 左侧树节点展开/折叠
const handleLeftNodeExpand = (node) => {if (!leftNodeExpand.value.includes(node.id)) {leftNodeExpand.value.push(node.id)}
}const handleLeftNodeCollapse = (node) => {const index = leftNodeExpand.value.indexOf(node.id)if (index > -1) {leftNodeExpand.value.splice(index, 1)}
}

核心功能实现

节点转移逻辑

// 向右转移
const moveToRight = () => {const newValue = [...new Set([...props.checkValue,...leftCheckedKeys.value,...leftHalfCheckedKeys.value])]emit('update:checkValue', newValue)
}// 向左转移
const moveToLeft = () => {const newValue = props.checkValue.filter(id => !rightCheckedKeys.value.includes(id))emit('update:checkValue', newValue)
}

状态同步机制

通过 watch 实现数据响应:

watch(() => props.checkValue, (newVal) => {leftTreeData.value = disableSelectedNodes(props.treeData, newVal)rightTreeData.value = hadleRightTreeData(props.treeData)
}, { immediate: true })

样式优化要点

.transfer-container {display: flex;justify-content: space-between;height: 400px;
}.tree-panel, .list-panel {width: 15vw;border: 1px solid #ebeef5;border-radius: 4px;
}.operation-buttons {display: flex;flex-direction: column;gap: 10px;
}

使用示例

<TreeTransfer :treeData="treeData"v-model:checkValue="selectedValues":titles="['可选部门', '已选部门']"
/>

扩展建议

  1. 性能优化:对于大数据量使用虚拟滚动
  2. 搜索功能:增加节点搜索过滤
  3. 自定义节点:支持插槽化内容定制
  4. 拖拽支持:实现节点拖拽转移
  5. 异步加载:支持动态加载子树节点

完整代码

<template><div class="transfer-container"><!-- 左侧树形面板 --><div class="tree-panel"><div class="panel-header"><el-checkbox v-model="leftExpandAll" @change="handleLeftCheckedAll"><span style="font-weight: 600;">{{ titles[0]}}</span></el-checkbox></div><el-tree :data="leftTreeData" :props="defaultProps" show-checkbox node-key="id" @check="handleLeftCheck":default-expanded-keys="defaultLeftNodeExpand" @node-expand="handleLeftNodeExpand"@node-collapse="handleLeftNodeCollapse" /></div><!-- 中间操作按钮 --><div class="operation-buttons"><el-button type="primary" :icon="ArrowRight" :disabled="showMoveToRight" @click="moveToRight"></el-button><el-button style="margin-left: 0px;" type="primary" :icon="ArrowLeft" :disabled="showMoveToLeft"@click="moveToLeft"></el-button></div><!-- 右侧列表面板 --><div class="list-panel"><div class="panel-header"><el-checkbox v-model="leftExpandAll" @change="handleRightCheckedAll"><span style="font-weight: 600;">{{titles[1] }}</span></el-checkbox></div><el-tree :data="rightTreeData" :props="defaultProps" show-checkbox node-key="id" @check="handleRightCheck":default-expanded-keys="defaultRightNodeExpand" @node-expand="handleRightNodeExpand"@node-collapse="handleRightNodeCollapse" /></div></div>
</template><script setup>
import { ref, computed, reactive, watch } from 'vue'
import { ArrowRight, ArrowLeft } from '@element-plus/icons-vue'const props = defineProps({checkValue: { // 已选择的值type: Array,default: () => []},titles: { // type: Array,default: () => ['选择区域', '已选区域']},treeData: { // 原始树数据type: Array,default: () => [],required: true,}
})const defaultProps = {children: 'children',label: 'label'
}
const emit = defineEmits(['update:checkValue'])
const defaultLeftNodeExpand = ref([])
const defaultRightNodeExpand = ref([])
const leftNodeExpand = ref([])
const rightNodeExpand = ref([])
const leftTreeData = ref([])
const rightTreeData = ref([])
const leftExpandAll = ref(false)
const showMoveToRight = ref(true) // 左侧选中数量
const showMoveToLeft = ref(true) // 左侧选中数量
const rightCheckedKeys = ref([])
const rightHalfCheckedKeys = ref([])
const leftCheckedKeys = ref([])
const leftHalfCheckedKeys = ref([])
const disableSelectedNodes = (treeData, selectedIds) => {console.log('disableSelectedNodes', selectedIds);// 使用深拷贝,确保不改变原来的 treeDataconst clonedTreeData = JSON.parse(JSON.stringify(treeData));const processNode = (nodes) => {return nodes.map(node => {// 如果当前节点的 id 在选中的 ID 列表中,且该节点是叶节点(没有子节点)if (selectedIds.includes(node.id) && ((node.type != 3 && node.children?.length == 0) || (!node.children || node.children.length == 0))) {// 不返回该节点,表示删除return null;}// 如果当前节点有子节点,递归处理子节点if (node.children && node.children.length > 0) {node.children = processNode(node.children).filter(child => child !== null); // 过滤掉已删除的节点}if ((selectedIds.includes(node.id) && ((node.type != 3 && node.children?.length == 0) || (!node.children || node.children.length == 0)))) {return null}return node;}).filter(node => node !== null); // 过滤掉已删除的节点};// 处理并返回新的树形数据return processNode(clonedTreeData);
}
const hadleRightTreeData = (treeData) => {const clonedTreeData = JSON.parse(JSON.stringify(treeData));const filterRightTreeData = (data) => {return data.filter(node => {if (!props.checkValue.includes(node.id)) return falseif (node.children) {node.children = filterRightTreeData(node.children)}return true})}return filterRightTreeData(clonedTreeData)
}
watch(() => props.checkValue,(newVal) => {leftTreeData.value = disableSelectedNodes(props.treeData, newVal)rightTreeData.value = hadleRightTreeData(props.treeData)console.log('defaultLeftNodeExpand.value ',defaultLeftNodeExpand.value );},{ immediate: true }
)// 左侧复选框变化
const handleLeftCheck = (node, { checkedKeys, checkedNodes, halfCheckedNodes, halfCheckedKeys }) => {leftCheckedKeys.value = checkedKeysleftHalfCheckedKeys.value = halfCheckedKeysconst selectedCount = checkedKeys.lengthshowMoveToRight.value = selectedCount == 0;
}
// 右侧复选框变化
const handleRightCheck = (node, { checkedKeys, checkedNodes, halfCheckedNodes, halfCheckedKeys }) => {rightCheckedKeys.value = checkedKeysrightHalfCheckedKeys.value = halfCheckedKeysconst selectedCount = checkedKeys.lengthshowMoveToLeft.value = selectedCount == 0;
}
// 移动到右侧
const moveToRight = () => {const newValue = [...new Set([...props.checkValue, ...leftCheckedKeys.value, ...leftHalfCheckedKeys.value])]emit('update:checkValue', newValue)   defaultRightNodeExpand.value = [...leftNodeExpand.value, ...rightNodeExpand.value]defaultLeftNodeExpand.value = [...leftNodeExpand.value, ...rightNodeExpand.value]console.log('defaultRightNodeExpand.value', defaultRightNodeExpand.value);console.log('defaultLeftNodeExpand.value', defaultLeftNodeExpand.value);leftCheckedKeys.value = []showMoveToRight.value = true
}
// 移动到左侧
const moveToLeft = () => {const newValue = []props.checkValue.forEach(id => {if (!rightCheckedKeys.value.includes(id)) {newValue.push(id)}})rightHalfCheckedKeys.value.forEach(id => {if (!newValue.includes(id)) {newValue.push(id)}})emit('update:checkValue', newValue)defaultRightNodeExpand.value = [...leftNodeExpand.value, ...rightNodeExpand.value]defaultLeftNodeExpand.value = [...leftNodeExpand.value, ...rightNodeExpand.value]console.log('defaultRightNodeExpand.value', defaultRightNodeExpand.value);console.log('defaultLeftNodeExpand.value', defaultLeftNodeExpand.value);showMoveToLeft.value = true
}
// 左侧全选/全不选
const handleLeftCheckedAll = () => {
}
// 右侧全选/全不选
const handleRightCheckedAll = () => {
}const handleLeftNodeExpand = (va1) => {const leftNodeExpandId = leftNodeExpand.value// 保存展开节点if (!leftNodeExpandId.includes(va1.id)) {leftNodeExpandId.push(va1.id)}console.log('leftNodeExpandId', leftNodeExpand.value);
}
const handleLeftNodeCollapse = (va1) => {const leftNodeExpandId = leftNodeExpand.value// 去除展开节点if (leftNodeExpandId.includes(va1.id)) {leftNodeExpandId.splice(leftNodeExpandId.indexOf(va1.id), 1)}console.log('leftNodeExpandId', leftNodeExpand.value);}const handleRightNodeExpand = (va1) => {const rightNodeExpandId = rightNodeExpand.value// 保存展开节点if (!rightNodeExpandId.includes(va1.id)) {rightNodeExpandId.push(va1.id)}console.log('rightNodeExpandId', rightNodeExpand.value);
}
const handleRightNodeCollapse = (va1) => {const rightNodeExpandId = rightNodeExpand.value// 去除展开节点if (rightNodeExpandId.includes(va1.id)) {rightNodeExpandId.splice(rightNodeExpandId.indexOf(va1.id), 1)}console.log('rightNodeExpandId', rightNodeExpand.value);}
</script>
<style scoped>
.transfer-container {display: flex;justify-content: space-between;align-items: center;padding: 1px;background: #fff;
}.tree-panel,
.list-panel {width: 15vw;height: 400px;border: 1px solid #ebeef5;border-radius: 4px;
}.panel-header {background-color: #f5f7fa;padding: 4px 10px;border-bottom: 1px solid #ebeef5;display: flex;justify-content: space-between;align-items: center;
}.operation-buttons {display: flex;flex-direction: column;gap: 10px;margin: 7px;
}.el-tree {height: calc(400px - 42px);overflow: auto;
}.el-scrollbar {height: calc(400px - 42px);
}.list-item {padding: 8px 15px;transition: background-color 0.3s;
}.list-item:hover {background-color: #f5f7fa;
}
</style>
关键字:外贸自建站平台排名_微分销系统定制开发_锦州网站seo_seo优化工作有哪些

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: