vue3+antdv仿百度网盘样式文件夹管理组件

实现:

  1. 默认进入页面时,文件夹全选;
  2. 文件夹状态,以及文件夹内的文件选择状态,与组件联动
  3. 文件夹数量,根据后端数据动态生成

实现思路:

将后端数据存到vuex中,增加(多选框状态控制)的参数

文件夹状态,通过监听vuex 中对应文件夹的,文件选择情况处理

选中/取消  单个文件和文件夹时,更新vuex数据

选中/取消  所有文件夹时,更新vuex数据

Checkbox 多选框状态控制
全选: indeterminate: false,  checkAll: true

半选: indeterminate: true,//半选  checkAll: false,//全选

未选:indeterminate: false,  checkAll: false,//全选

vuex

import { createStore } from 'vuex';export default createStore({state: {ExportData: [],//文件夹数据},mutations: {// 存文件夹数据setExportData(state, file) {console.log('存文件夹数据api数据', file)state.ExportData = file;},updateChooseNum(state, item) {// 首个元素的 typeName 是 '全部文件'  // const firstFileType = state.ExportData.filter((item) => item.typeName === '全部文件');let fileIndex = state.ExportData.findIndex((df) => df.typeName == item.name);if (fileIndex != -1) {// 使用扩展运算符和 Object.assign 合并对象,并添加新字段  idlist:已选文件的id数组const updatedFileType = {...state.ExportData[fileIndex], idlist: item.idlist || [],chooseNum: item.idlist.length || 0};console.log('12221更新数据', updatedFileType)state.ExportData.splice(fileIndex, 1, updatedFileType);}console.log('11更新导出数据', state.ExportData)if (state.ExportData[0]) {// 累加除首个外的 chooseNum  let sum = 0;for (let i = 1; i < state.ExportData.length; i++) {sum += state.ExportData[i].chooseNum;}// 更新首个元素的 chooseNum  state.ExportData[0].chooseNum = sum;}console.log('更新导出数据', state.ExportData)},// 全部文件-文件夹全选,(是否顶部全部文件夹,typename,是否清空)setFilderCheck(state, item) {let { all, name, empty } = itemif (all) {if (empty) {state.ExportData.forEach((obj) => {obj.chooseNum = 0obj.idlist = []});state.ExportData[0].chooseNum = 0} else {state.ExportData.forEach((obj) => {obj.chooseNum = obj.fileIdList.lengthobj.idlist = obj.fileIdList});state.ExportData[0].chooseNum = state.ExportData[0].totalNum}} else {let fileIndex = state.ExportData.findIndex((df) => df.typeCode == name);let currnum = state.ExportData[fileIndex].totalNumlet first = state.ExportData[0].chooseNumconsole.log("vuex|c,totle", currnum, first)if (fileIndex != -1) {// 使用扩展运算符和 Object.assign 合并对象,并添加新字段  const updatedFileType = {...state.ExportData[fileIndex], idlist: empty ? [] : state.ExportData[fileIndex].fileIdList,chooseNum: empty ? 0 : state.ExportData[fileIndex].fileIdList.length};state.ExportData.splice(fileIndex, 1, updatedFileType);if (empty) {state.ExportData[0].chooseNum = first - currnum} else {state.ExportData[0].chooseNum = first + currnum}}console.log('xx更新导出数据', state.ExportData)}},},
});

组件 :fileManager.vue 

<template><div class="top-box"><a-checkbox v-model:checked="allState.checkAll" :indeterminate="allState.indeterminate"@change="onCheckAllChange">全选</a-checkbox><div class="allnum">已选择{{ allnum }}个文件</div></div><div class="file-folder-card"><a-checkbox-group v-model:value="allState.checkedList" :plainOptions="plainOptions"><div v-if="folderlist.length > 1" class="folder" @mouseover="item.isHovered = true"@mouseleave="item.isHovered = false" v-for="(item, index) in folderlist"><div class="filer-box":class="{ seleback: includ(item.typeCode) || item.indeterminate || item.checkAll }"><a-checkbox @change="onCheckChange($event, item, index)" v-if="item.chooseNum == 0 && item.totalNum == 0":value="item.typeCode" v-model:checked="item.checkAll" :indeterminate="item.indeterminate"class="check"></a-checkbox><a-checkbox @change="onCheckChange($event, item, index)"v-else-if="item.isHovered || item.chooseNum || item.indeterminate || item.checkAll || includ(item.typeCode)"class="check" :value="item.typeCode" v-model:checked="item.checkAll":indeterminate="item.indeterminate"></a-checkbox><div class="tip">({{ item.chooseNum }}/{{ item.totalNum }})</div><img src="@/assets/files2x.png" class="fileimg" @click="openFilled(item, index)" /></div><div class="name">{{ item.typeName }} </div><!-- <div class="name">{{ item.indeterminate + '|' + item.checkAll }} </div> --></div><!-- <div class="name">{{ allState.checkedList }} </div> --></a-checkbox-group></div></template><script lang="ts" setup>
import { FolderOpenFilled } from '@ant-design/icons-vue';
import { reactive, watch, ref, onMounted, onUnmounted, computed } from 'vue';
import { useStore } from 'vuex' // 引入useStore 方法
import { arrayEach } from 'xe-utils';
const store = useStore()
const emits = defineEmits<{(e: "custom-event", value?: any): void;(e: "change", value?: any): void;
}>();
const someState = computed(() => store.state.ExportData);const plainOptions = ref([]);
const allnum = ref(0)//选中总数
// 处理过的文件夹数据
const folderlist = ref([])
const allState = reactive({indeterminate: false,//半选checkAll: false,//全选checkedList: []
});
function openFilled(item, index) {emits("custom-event", { tab: item, index });// this.$emit('custom-event');  
}watch(() => someState, (newValue, oldValue) => {// console.log('文件夹监听菜单data', oldValue, 'to', newValue);if (!newValue.value || newValue.value.length == 0) {return}let oldsll=allState.checkedListallState.checkedList = []newValue.value.forEach((item, index, arr) => {// // 处理总文件if (item.typeName == '全部文件') {allnum.value = item.chooseNumlet { indeterminate, checkAll } = setChoose(item.chooseNum, item.totalNum)allState.indeterminate = indeterminateallState.checkAll = checkAllplainOptions.value = item.fileIdListif (checkAll && !indeterminate) {allState.checkedList = item.fileIdList}} else {const first = arr[0]let { indeterminate, checkAll } = setChoose(item.chooseNum, item.totalNum)const updatedFileType = {...folderlist.value[index],...item,indeterminate: indeterminate,checkAll: checkAll,isHovered: false};if (item.typeName == '其他资料') {console.log(updatedFileType)}if ((first.chooseNum > 0 && first.chooseNum < first.totalNum) && item.chooseNum > 0 && item.chooseNum == item.totalNum) {allState.checkedList.push(item.typeCode)}if ((item.chooseNum == 0 && item.totalNum == 0) && (first.chooseNum < first.totalNum) && oldsll.includes(item.typeCode)) {allState.checkedList.push(item.typeCode)}// console.log("|___|",index,folderlist.value,updatedFileType)folderlist.value.splice(--index, 1, updatedFileType);}// console.log("xxx", folderlist.value, allState.checkedList)});
}, {deep: true,immediate: true
})
watch(() => allState.checkedList,val => {allState.indeterminate = val.length > 0 && val.length < plainOptions.value.length;allState.checkAll = val.length === plainOptions.value.length;},
);// 处理选框状态
function setChoose(chooseNum, totalNum) {let indeterminate = falselet checkAll = falseif (chooseNum == 0) {if (totalNum == 0) {indeterminate = falsecheckAll = true} else {indeterminate = falsecheckAll = false}} else if (chooseNum < totalNum && chooseNum > 0) {indeterminate = truecheckAll = false} else {indeterminate = falsecheckAll = true}return { indeterminate, checkAll }
}
const includ = (s) => {if (s) {return allState.checkedList.includes(s)}
}const onCheckAllChange = (e: any) => {//打印checkAllObject.assign(allState, {checkedList: e.target.checked ? plainOptions.value : [],indeterminate: false,});store.commit("setFilderCheck", { all: true, name: '', empty: !e.target.checked });// emits('change',e.target.checked)
};
const onCheckChange = (e, item: any, index) => {if (item.totalNum == 0) {folderlist.value[index].checkAll = !e.target.checkedreturn}//打印sele-checkAllconsole.log("wj", e.target.checked, item)if (e.target) {store.commit("setFilderCheck", { all: false, name: item.typeCode, empty: !e.target.checked });}
};
</script><style lang="less" scoped>
.top-box {display: flex;
}
.allnum {margin-left: 24px;font-family: PingFang SC, PingFang SC;font-weight: 400;font-size: 12px;color: #1D2129;line-height: 22px;text-align: left;font-style: normal;text-transform: none;
}/* 添加你的样式 */
.file-folder-card {width: 100%;padding: 24px 12px;display: flex;gap: 40px;/deep/.ant-checkbox-group {display: flex;gap: 40px;flex-wrap: wrap;max-height: calc(100vh - 400px);overflow-y: auto;}/deep/.ant-checkbox-wrapper::after {height: 0;display: none !important;}.folder {// display: flex;.filer-box {width: 88px;height: 88px;// background: #F4F6F8;border-radius: 4px 4px 4px 4px;position: relative;margin-bottom: 4px;.check {position: absolute;top: 2px;left: 4px;}&:hover {background: #F4F6F8;}.tip {// width: 28px;height: 17px;font-family: PingFang SC, PingFang SC;font-weight: 400;font-size: 12px;color: #606368;line-height: 18px;text-align: right;font-style: normal;text-transform: none;position: absolute;top: 0px;right: 4px;}}.seleback {background: rgba(22, 93, 255, 0.08) !important;}.fileimg {width: 68px;height: 60px;margin: 14px 10px;}.name {width: 88px;height: 36px;font-family: PingFang SC, PingFang SC;font-weight: 400;font-size: 12px;color: #1D2129;line-height: 18px;font-style: normal;text-transform: none;overflow: hidden;text-overflow: ellipsis;text-align: center;}}}
</style>

组件使用:


<file-manager></file-manager>import fileManager from "../components/fileManager.vue";//从后端获取数据后存入vuextabs.value = res.data;store.commit("setExportData", res.data);tabs.value 格式示例:
[{"typeCode": "allFile","typeName": "全部文件","chooseNum": 0,"totalNum": 4,"index": 1,"icon": "icon-tz-icon_wjj","fileIdList": ["declarationDraft","attestationReport","contractVoucher","otherDocuments"]},{"typeCode": "declarationDraft","typeName": "文件夹1","chooseNum": 0,"totalNum": 2,"index": 2,"icon": "icon-tz-icon_sjdg1","fileIdList": ["208c57896ef3_11","208c5f07896ef3_10"]},{"typeCode": "attestationReport","typeName": "文件夹2","chooseNum": 0,"totalNum": 1,"index": 3,"icon": "icon-tz-icon_jzbg","fileIdList": ["208c5f0303644"]},{"typeCode": "contractVoucher","typeName": "文件夹3","chooseNum": 0,"totalNum": 1,"index": 6,"icon": "icon-tz-icon_xmht","fileIdList": ["6402"]},{"typeCode": "otherDocuments","typeName": "其他资料","chooseNum": 0,"totalNum": 0,"index": 7,"icon": "icon-tz-icon_qtcc","fileIdList": []}
]//各文件夹,默认选中 按需使用const arr = res.data.find(item => {return item.typeCode == 'otherDocuments'})selectedRowKeys.value = arr.idliststore.commit("setFilderCheck", { all: true, name: '', empty: false });

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3224487.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

基于vue的可视化大屏

要提前准备一个xinyang.json文件 可以在这个网站下载 DataV.GeoAtlas地理小工具系列 (aliyun.com) 代码结构 总框架代码&#xff1a; <template><div><div class"center"><center-left /><center-map /><center-right /><…

PPI(每英寸像素数)、DPI(每英寸点数)和Pixel(像素)的区别和联系?

一、定义 PPI、DPI和Pixel是图像处理、打印和显示领域中常用的三个概念&#xff0c;它们之间既有区别又有联系。以下是对这三个概念进行分别讲解&#xff1a; 1. PPI&#xff08;Pixels Per Inch&#xff09;&#xff0d;即每英寸像素数&#xff0c;是图像分辨率的一种表示方…

技术速递|VS Code Java 6月更新 - 项目设置功能增强!大量 Spring 新特性

作者&#xff1a;Nick Zhu 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎阅读 Visual Studio Code for Java 的六月更新&#xff01;在这篇博客中&#xff0c;我们将分享项目设置项目的重要更新以及一系列 Spring 的功能改进&#xff0c;让我们开始吧&#xff01; 项目设…

AI论文作图——如何表示模型参数冻结状态

一、LOGO &#x1f525; win10win11 ❄️ win10win11 二、注意事项&#xff1a; 根据电脑系统&#xff0c;选择对应的版本。 参考&#xff1a; 【AI论文作图】如何表示模型参数冻结状态&#xff1f;

微信小程序 - 本地存储 增加有效期

小程序的本地存储API提供了wx.setStorageSync和wx.setStorage来存储数据&#xff0c;注意的是&#xff0c;小程序的本地存储并没有明确的有效期设置&#xff0c;存储的数据在不超过限制的情况下&#xff0c;会一直保留。 一、小程序本地存储API 小程序的本地存储API提供了设置…

容器docker

文章目录 前言一、docker1.1 为什么有docker1.2 docker架构1.3 docker 安装1.4 docker中央仓库1.5 docker 基本指令1.6 docker数据卷&#xff0c;挂载例&#xff1a;nginx 数据卷挂载例&#xff1a;mysql 本地持久化 1.7 镜像制作镜像结构dockerfile基础指令容器生成镜像 1.8 d…

C++笔试强训3

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、选择题1-5题6-10题 二、编程题题目一题目二 一、选择题 1-5题 如图所示&#xff0c;如图所示p-3指向的元素是6&#xff0c;printf里面的是%s&#xff0c;从6开…

带有子节点的树状表的父节点拖动排序#Vue3#Sortable插件

带有子节点的树状表的父节点拖动排序#Vue3#Sortable插件 使用Sortable插件这里要保证获取到的是父节点的下标&#xff0c;属性newDraggableIndex获取到的就是只有父节点的下标。设置子节点不能被拖动&#xff0c;最后在逐个调用接口进行数据库中顺序的更新。 <template>…

层次分析法上课笔记

欢迎来到一夜看尽长安花 博客&#xff0c;您的点赞和收藏是我持续发文的动力 对于文章中出现的任何错误请大家批评指出&#xff0c;一定及时修改。有任何想要讨论的问题可联系我&#xff1a;3329759426qq.com 。发布文章的风格因专栏而异&#xff0c;均自成体系&#xff0c;不足…

Profibus转ModbusTCP网关模块实现Profibus_DP向ModbusTCP转换

Profibus和ModbusTCP是工业控制自动化常用的二种通信协议。Profibus是一种串口通信协议&#xff0c;它提供了迅速靠谱的数据传输和各种拓扑结构&#xff0c;如总线和星型构造。Profibus可以和感应器、执行器、PLC等各类设备进行通信。 ModbusTCP是一种基于TCP/IP协议的通信协议…

使用八股搭建神经网络

神经网络搭建八股 使用tf.keras 六步法搭建模型 1.import 2.train, test 指定输入特征/标签 3.model tf.keras.model.Sequential 在Squential,搭建神经网络 4.model.compile 配置训练方法&#xff0c;选择哪种优化器、损失函数、评测指标 5.model.fit 执行训练过程&a…

python开发prometheus exporter--用于hadoop-yarn监控

首先写python的exporter需要知道Prometheus提供4种类型Metrics 分别是&#xff1a;Counter, Gauge, Summary和Histogram * Counter可以增长&#xff0c;并且在程序重启的时候会被重设为0&#xff0c;常被用于任务个数&#xff0c;总处理时间&#xff0c;错误个数等只增不减的指…

昇思25天学习打卡营第16天|Vision Transformer图像分类

昇思25天学习打卡营第16天|Vision Transformer图像分类 前言Vision Transformer图像分类Vision Transformer&#xff08;ViT&#xff09;简介模型结构模型特点 环境准备与数据读取模型解析Transformer基本原理Attention模块 Transformer EncoderViT模型的输入整体构建ViT 模型训…

xcode项目添加README.md文件并进行编辑

想要给xcode项目添加README.md文件其实还是比较简单的&#xff0c;但是对于不熟悉xcode这个工具的人来讲&#xff0c;还是有些陌生&#xff0c;下面简单给大家讲一下流程。 选择“文件”>“新建”>“文件”&#xff0c;在其他&#xff08;滚动到工作表底部&#xff09;下…

k8s record 20240708

一、PaaS 云平台 web界面 资源利用查看 Rancher 5台 CPU 4核 Mem 4g 100g的机器 映射的目录是指docker重启后&#xff0c;数据还在 Rancher可以创建集群也可以托管已有集群 先docker 部署 Rancher&#xff0c;然后通过 Rancher 部署 k8s 想使用 kubectl 还要yum install 安…

leetcode--验证二叉搜索树

leetcode地址&#xff1a;验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必…

71.WEB渗透测试-信息收集- WAF、框架组件识别(11)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;70.WEB渗透测试-信息收集- WAF、框架组件识别&#xff08;10&#xff09;-CSDN博客 如果有…

人工智能和机器学习 (复旦大学计算机科学与技术实践工作站)20240703(上午场)人工智能初步、mind+人脸识别

前言 在这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经逐渐渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;无一不彰显着AI的强大潜力。而人脸识别技术作为AI领域的一项重要应用&#xff0c;更是以其高效、便捷的特点受到了…

人工智能算法工程师(中级)课程2-Opencv视觉处理之高级操作

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程2-Opencv视觉处理之高级操作。在上一节课中的OpenCV基础操作我们了解到OpenCV是一个开源的计算机视觉软件库。它提供了各种视觉处理函数&#xff0c;并支持多种编程语言&#xff0c;如…

USB眼图eye diagram测试

前言: USB有一种测量称为EYE图或信号完整性测试。考虑数字信号从发射机传输到接收机的过程。到达接收器的信号质量可能受到许多因素的影响,包括发射器、电缆或PCB迹线以及连接器。信号质量也被称为信号完整性。眼图是一种用于快速评估数字信号质量的图形工具。眼图这个名字之…