需求背景:公司接到一个项目,是需要做一个族谱微信小程序,需要有族谱树,且可以添加家族人员。

灵感来源:在插件市场中下载了作者 羊羊不想写代码 的插件tree-list族谱,树形列表,可缩放滑动 - DCloud 插件市场,根据作者的代码逻辑增加了横向的树结构,使用简单。

父组件引用:(数据在用的时候从后端请求)

<template><view class=""><view class="switch-btn" @click="switchRow">{{switchName}}</view><!-- 人物关系图 --><template><view class="genealogy-tree"><movable-area :style="{height: '100vh',width: '100vw'}"><movable-view @scale="changeScale" :scale="true" :scale-max="1" :scale-min="0.5" class="max" direction="all":style="{width: `${treeConfig.width}px`,height: `${treeConfig.height}px`}"><div class="tree-content"><columnTreeList v-if="switchName == '竖向'" :tree-data="treeData" :tree-first="true" /><rowTreeList v-else :tree-data="treeData" :tree-first="true" /></div></movable-view></movable-area></view></template></view>
</template><script setup>import {ref,provide,nextTick,watch,getCurrentInstance,onMounted} from 'vue';import columnTreeList from '@/components/column-tree-list.vue'import rowTreeList from '@/components/row-tree-list.vue';const switchName = ref('横向')let scale = ref(1); // 缩放倍率const instance = getCurrentInstance(); // 获取组件实例let treeConfig = ref({ // movable-view移动区域大小width: 0,height: 800})/*** 获取元素信息* @param {String} domID dom元素id* */const getDomInfo = (domID) => {return new Promise((resolve, reject) => {const bar = uni.createSelectorQuery().in(instance);bar.select(domID).boundingClientRect(res => {if (res) resolve(res);else reject()}).exec();})}// 树形结构数据let treeData = ref([{id: 1,name: '祖宗',child: [{id: 2,name: '爷爷',spouse: {id: 2001,name: '奶奶',},child: [{id: 3,name: '父亲',spouse: {id: 3001,name: '妈妈',},child: [{id: 4,name: '自己',},{id: 9,name: '妹妹',}]},{id: 5,name: '二伯',},]},{id: 6,name: '二大爷',child: [{id: 7,name: '大叔',},{id: 8,name: '二叔',},]}]}])// 删除provide('delItem', (item) => {treeData.value = deleteNodeById(treeData.value, item.id);})/*** 添加子级* @param { object } item 当前点击的对象* */provide('addItem', (item) => {const data = {id: Math.floor(Math.random() * 1000), // 唯一键后续自行设置name: '新增子级',}handleData(item.id, treeData.value, data, 1)})/*** 添加配偶* @param { object } item 当前点击的对象* */provide('addSpouse', (item) => {console.log(31231, item);const data = {id: Math.floor(Math.random() * 100), // 唯一键后续自行设置name: '配偶',}handleData(item.id, treeData.value, data, 2)})/*** 递归对树形结构添加节点* @param {number | string} id 唯一键* @param { Array } 树形结构数组* @param { Object } obj 添加的数据 {id,name,...}* @param { number } type 添加的类型 1:子级,2:配偶* */const handleData = (id, data, obj, type) => {data.forEach(item => {if (item.id === id) {// 在这里处理新增子级还是配偶if (type === 1) {item.child ? item.child.push(obj) : item.child = [obj]} else if (type === 2) {// 如果存在配偶这里赋值将进行替换// 如需多配偶需自行改为数组形式(tree-list里的spouse也需要同步修改为数组)item.spouse = obj;}} else {if (item.child) {handleData(id, item.child, obj, type)}}})return data}/*** 递归删除树形结构元素* @param { Array } tree 树形结构数据* @param { number | string } id 唯一键* */const deleteNodeById = (tree, targetId) => {for (let i = 0; i < tree.length; i++) {const node = tree[i];if (node.id === targetId) {console.log('找到了', node);// 使用 splice 删除节点tree.splice(i, 1);return tree; // 返回新的数组}if (node.child && node.child.length > 0) {// 递归查找子节点node.child = deleteNodeById(node.child, targetId);}}return tree; // 没有找到目标节点,返回原数组}// 设置移动缩放大小const setTreeConfig = () => {nextTick(() => {setTimeout(() => {getDomInfo('.tree-content').then(res => {treeConfig.value = {width: res.width / scale.value,height: res.height / scale.value}console.log('返回值',res);})}, 200)})}const changeScale = (e) => {scale.value = e.detail.scale;console.log('缩放',e.detail)}// 转换const switchRow = () => {switchName.value = switchName.value == '横向' ? '竖向' : '横向'}// 监听树形结构数据变化watch(treeData.value, (newVal, oldVal) => {setTreeConfig()})onMounted(() => {setTreeConfig()})
</script><style lang="scss" scoped>.genealogy-tree {min-height: 100%;min-width: 100vw;position: relative;overflow-x: scroll;// overflow: hidden;.tree-content {position: absolute;top: 0;left: 0;transition: all .3s;}}::v-deep .uni-table-th {color: #000 !important;}::v-deep .uni-table-td {color: #000 !important;}.th-bg {background-color: #d9d9d9;}.switch-btn {position: fixed;top: 30rpx;right: 30rpx;height: 50rpx;width: 100rpx;border-radius: 20rpx;background-color: #f8f8f8;box-shadow: 0 6rpx 0rpx 4rpx #00000080;font-size: 20rpx;line-height: 50rpx;text-align: center;z-index: 99;}
</style>

子组件:子组件自我递归调用(原作者代码---竖向树结构)

<template><view class="card"><view class="ul"><view class="li" v-for="(item,index) in treeData" :key="index"><view class="item" :class="{'line-left': index !== 0, 'line-right': index != treeData.length - 1}"><view class="item-name" :class="{'line-bottom':item.child && item.child.length > 0,'line-top':!treeFirst}"><view class="content"><image src="@/static/logo.png" mode="widthFix" style="width: 40rpx;height: auto;border-radius: 50%;"></image><text class="name">{{item.name}}</text><button class="btn" @click="addItem(item)">添加子级</button><button class="btn" @click="addSpouse(item)">添加配偶</button><button class="btn" @click="delItem(item)">删除当前</button></view><!-- 配偶 --><view class="content-2" v-if="item.spouse"><image src="@/static/logo.png" mode="widthFix" style="width: 40rpx;height: auto;border-radius: 50%;"></image><text class="name">{{item.spouse.name}}</text></view></view></view><column-tree-list v-if="item.child && item.child.length > 0" :tree-data="item.child"></column-tree-list></view></view></view>
</template><script setup name="column-tree-list">import columnTreeList from '@/components/column-tree-list.vue'import {inject} from 'vue'const delItem = inject('delItem')const addItem = inject('addItem')const addSpouse = inject('addSpouse')defineProps(['treeData', 'treeFirst'])
</script><style lang="scss" scoped>$line-length: 20px; //线长$spacing: 20px; //间距$extend: calc(#{$spacing}); //延长线// 线样式@mixin line {content: "";display: block;width: 1px;height: $line-length;position: absolute;left: 0;right: 0;margin: auto;background: #e43934;}.card {.ul {display: flex;justify-content: center;.li {.item {display: flex;justify-content: center;align-items: center;position: relative;&-name {position: relative;display: flex;justify-content: center;align-items: center;margin: $spacing 10rpx;.content,.content-2 {display: flex;flex-direction: column;align-items: center;background: #fff;padding: 20rpx;border-radius: 16rpx;box-sizing: border-box;box-shadow: 0px 5rpx 30rpx 5rpx rgba(0, 0, 0, 0.08);.name {margin: 10rpx 0 18rpx;color: #222;font-size: 20rpx;}}.content-2 {display: flex;flex-direction: column;align-self: flex-start;margin-left: 10rpx;}}}}}// 向下的线.line-bottom {&::after {@include line();bottom: -$line-length;}}// 向上的线.line-top {&::before {@include line();top: -$line-length;}}// 向左的线.line-left {&::after {@include line();width: calc(50% + #{$spacing});height: 1px;left: calc(-50% - #{$extend});top: 0;}}// 向右的线.line-right {&::before {@include line();width: calc(50% + #{$spacing});height: 1px;right: calc(-50% - #{$extend});top: 0;}}}.btn {font-size: 18rpx;width: 116rpx;height: 45rpx;}
</style>

横向树结构:

<template><view class="vmPage"><view class="sub-branch" v-for="(item,index) in treeData" :key="index"><view class="item" :class="{'line-top': index !== 0, 'line-bottom': index !== treeData.length - 1}"><view class="item-name" :class="{'line-right':item.child && item.child.length > 0,'line-left':!treeFirst}"><view class="content"><image src="@/static/logo.png" mode="widthFix" style="width: 40rpx;height: auto;border-radius: 50%;"></image><text class="name">{{item.name}}</text><view class="btn" @click="addItem(item)">添加子级</view><view class="btn" @click="addSpouse(item)">添加配偶</view><view class="btn" @click="delItem(item)">删除当前</view></view><!-- 配偶 --><view class="content-2" v-if="item.spouse"><image src="@/static/logo.png" mode="widthFix" style="width: 40rpx;height: auto;border-radius: 50%;"></image><text class="name">{{item.spouse.name}}</text></view></view></view><row-tree-list v-if="item.child && item.child.length > 0" :tree-data="item.child"></row-tree-list></view></view>
</template><script setup name="row-tree-list">import {ref,inject} from 'vue'import rowTreeList from '@/components/row-tree-list.vue';const delItem = inject('delItem')const addItem = inject('addItem')const addSpouse = inject('addSpouse')defineProps(['treeData', 'treeFirst'])
</script><style lang="scss" scoped>$line-length: 30rpx;$spacing: 30rpx;$extend: calc(#{$spacing});$line-color: #e43934;// 线样式@mixin line {content: "";display: block;width: 1rpx;height: $line-length;position: absolute;top: 0;bottom: 0;margin: auto;background: #e43934;}@mixin flex-center {display: flex;justify-content: center;align-items: center;}.vmPage {display: flex;justify-content: center;flex-direction: column;.sub-branch {display: flex;.item {@include flex-center();position: relative;.item-name {position: relative;flex-direction: column;@include flex-center();align-items: flex-start;margin: 10rpx $spacing;}}}}.content,.content-2 {@include flex-center();background: #fff;padding: 20rpx;box-sizing: border-box;border-radius: 16rpx;box-shadow: 0px 5rpx 30rpx 5rpx rgba(0, 0, 0, 0.08);.name {display: inline-block;font-size: 20rpx;margin: 0 8rpx 0 20rpx;width: 30rpx;}.btn {font-size: 18rpx;width: 30rpx;text-align: center;padding: 8rpx;background-color: #f8f8f8;border: 1px solid rgba(0, 0, 0, .2);border-radius: 6rpx;}}// 向右的线.line-right {&::after {@include line();right: - $line-length;width: $line-length;height: 1rpx;}}// 向左的线.line-left {&::before {@include line();left: - $line-length;width: $line-length;height: 1rpx;}}// 向上的线.line-top {&::after {@include line();height: calc(50% + $line-length);left: 0;top: calc(-50% - $line-length);}}// 向下的线.line-bottom {&::before {@include line();height: calc(50% + $line-length);left: 0;bottom: calc(-50% - $line-length);}}
</style>

注:该文章所用代码多是复用原作者羊羊不想写代码 的个人主页 - DCloud问答 的插件tree-list族谱,树形列表,可缩放滑动 - DCloud 插件市场 中的代码,只是在横向树结构中修改了部分代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/91435.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91435.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/91435.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

思途JSP学习 0731

继0730&#xff0c;我们对项目做最后的升级一、删除功能1、新增复选框辅助删除条目的选择修改我们的list.jsp和list.js在列表的第一列增加一列选择框2、给复选框添加全选与行点击选择功能在行选择功能中&#xff0c;因为此时的选择框还未生成&#xff0c;所以我们将事件委托给他…

某讯视频风控参数逆向分析

文章目录1. 写在前面2. 接口分析3. 加密分析4. 扣JS代码【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力…

[Broken IOS] 配置CLI | 终端用户界面TUI

链接&#xff1a;https://palera.in/ docs&#xff1a;palera1n palera1n 是一款专为 Jailbroken苹果设备 设计的强大工具&#xff0c;支持运行 iOS/iPadOS/tvOS 15.0 及更新系统 的 iPhone、iPad 和 Apple TV。 该工具通过 DFU 模式 下的底层 USB 通信引导设备&#xff0c;…

论文阅读|ArxiV 2024|Mamba进一步研究|VSSD

论文地址&#xff1a;pdf 代码地址&#xff1a;code 文章目录1.研究背景与动机2. 核心方法2.1 预备知识:mamba-ssm2.2 非因果状态空间对偶性2.3 视觉状态空间对偶性模型3. 实验结果3.1 图像分类任务3.2 目标检测任务3.3 语义分割任务3.4 消融实验4.局限性与结论4.1 局限性4.2 结…

Flutter中 Provider 的基础用法超详细讲解(二)之ChangeNotifierProvider

目录 前言 一、什么是ChangeNotifierProvider? 二、ChangeNotifier的简单用法 1.定义状态类 2.使用ChangeNotifierProvider提供状态 3.获取状态并监听更新 1.Consumer 2.通过API方式获取 1.Provider.of (context) 2.context.watch () 3.context.read () 4.各种获…

2025电商CPS分销与推客系统小程序开发:趋势、架构与实战解析

一、行业趋势&#xff1a;CPS模式与社交电商的深度融合1.1 电商行业新趋势根据《2025年电子商务行业发展趋势预测报告》&#xff0c;社交电商与内容营销已成为核心增长点。消费者行为呈现三大特征&#xff1a;消费习惯转变&#xff1a;线上购物占比超70%&#xff0c;Z世代用户更…

Conda环境下配置的基本命令

功能命令创建环境conda create -n myenv python3.11激活环境conda activate myenv删除环境conda env remove -n myenv复制环境conda create -n newenv --clone myenv列出所有环境conda env list列出环境所有包conda list彻底清除某个 Conda 环境中的所有已安装包&#xff08;但…

Ps2025

快捷键CShs保存CSw存储为S选取叠加选取,A选取减去选区C回车保存路径内容识别 SF5 ADel填充前景色CDel填充背景色A上下 上下行间距A左右 左右字间距C左键丝滑放大缩小CASE盖印图层C}上移一格CG新建组sF6羽化像素钢笔工具打上抹点&#xff0c;按住shift水平拉调增弧度左右两个手柄…

ceph sc 设置文件系统格式化参数

前言 默认的 sc 文件系统 inode 太少,对于小文件场景,往往会出现容量没满,inode 已经用尽的情况,本文说明如何设置 inode。 说明 本文使用的是 rook-ceph 部署的 ceph 作为存储后端。 xfs 文件系统 sc 创建带格式化参数的 xfs 文件系统的 sc allowVolumeExpansion: t…

【LY88】ubuntu下的常用操作

vscode 下载安装包 在安装包所处文件夹空白区域右键调出终端 输入下行命令安装 c后接tab自动补全安装包名称&#xff08;前提是该文件夹中仅这一个c开头文件&#xff0c;否则得再输点字母&#xff0c;保证其可唯一索引到&#xff09; sudo dpkg -i ctab安装完毕后输入code&…

web应用从服务器主动推动数据到客户端的方式

html5 websocket 全双工交互 全双工通信&#xff1a;建立持久连接&#xff0c;服务端和客户端可随时互相发送消息 低延迟&#xff1a;适合实时应用&#xff08;聊天、游戏、股票行情等&#xff09; socket协议是与HTTP协议平级的&#xff0c;websocket协议是建立在TCP协议之上的…

基于Spring Boot实现中医医学处方管理实践

基于Spring Boot实现中医医学处方管理 以下是基于Spring Boot实现中医医学处方管理的相关示例和资源整理,涵盖基础架构、功能模块及实际应用案例: 基础项目结构 Spring Boot中医处方系统通常采用MVC分层设计: 实体类:定义处方、药材、患者等JPA实体 @Entity public clas…

从“人工核验”到“智能鉴防”:护照鉴伪设备的科技革命

“一本伪造护照的查获成本从72小时降至3秒&#xff0c;背后是光学传感、量子加密与多模态AI的十年协同进化。”2025年全球边检口岸查获伪假护照近500份&#xff0c;其中芯片伪造占比首超40%。当造假技术逼近分子级仿制&#xff0c;传统肉眼鉴别彻底失效&#xff0c;多光谱成像、…

无人机飞控系统3D (C++)实践

大疆无人机飞控系统3D模型开发 大疆无人机飞控系统3D模型开发(C++) 核心架构设计 大疆无人机的飞控系统通常采用分层架构,分为硬件抽象层(HAL)、中间件层和应用层。HAL负责与传感器/执行器直接交互,中间件处理数据融合和通信协议,应用层实现核心控制算法。 典型代码结…

ES6中import与export的用法详解

目录 一、ES6模块化的核心概念 1. 模块化的基本规则 二、export的用法 1. 命名导出&#xff08;Named Export&#xff09; 示例&#xff1a; 2. 默认导出&#xff08;Default Export&#xff09; 示例&#xff1a; 默认导出函数或类&#xff1a; 3. 导出语句的统一声明…

硬核技术协同:x86 生态、机密计算与云原生等技术如何为产业数字化转型筑底赋能

在产业数字化转型的浪潮中&#xff0c;x86 生态构建、机密计算与 AI 融合、高性能网卡突破、云原生 OS 实践、国产数据库优化等技术领域的突破&#xff0c;正成为支撑数字化基础设施升级与业务创新的核心引擎。以下从技术深度与产业实践角度&#xff0c;系统性解析各领域的最新…

Java项目:基于SSM框架实现的网络财务管理系统【ssm+B/S架构+源码+数据库+毕业论文+远程部署】

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本网络财务管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

1.5.Vue v-for 和 指令修饰符

vue v-for当你使用 v-for 指令来渲染列表时&#xff0c;为每个元素提供一个唯一的 key 属性是非常重要的。key 是用来给 Vue 一个提示&#xff0c;以便它能够追踪每个节点的身份&#xff0c;从而更高效地更新虚拟 DOM。key 的作用唯一标识&#xff1a;key 应该是每项数据的唯一…

(RedmiBook)上禁用触摸板或自带键盘

在红米笔记本&#xff08;RedmiBook&#xff09;上禁用触摸板或自带键盘&#xff0c;可以通过以下几种方法实现&#xff1a; 方法一&#xff1a;通过设备管理器禁用&#xff08;Windows 系统&#xff09; 禁用触摸板 打开设备管理器 按 Win X → 选择 “设备管理器”或 Win …

15 - 多模态大语言模型 — 图文 “牵线” 系统 “成长记”:借 CLIP 练本领,从图像与文字里精准 “搭鹊桥” 的全过程 (呆瓜版 - 2 号)

目录 1、基础&#xff1a;它到底是个啥&#xff1f; 1. 1、一句话理解核心 1.2、 为啥厉害&#xff1f; 1.3、怎么发展来的&#xff1f; 2、架构&#xff1a;它的 “身体构造” 是啥样的&#xff1f; 2.1、视觉语言模型架构&#xff1a;让 AI “看懂” 世界的核心系统 2…