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://www.mzph.cn/pingmian/44019.shtml

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

相关文章

LAMP万字详解(概念、构建步骤)

目录 LAMP Apache 起源 主要特点 软件版本 编译安装httpd服务器 编译安装的优点 操作步骤 准备工作 编译 安装 优化执行路径 添加服务 守护进程 配置httpd 查看 Web 站点的访问情况 虚拟主机 类型 部署基于域名的虚拟主机 为虚拟主机提供域名解析&#xff…

机器人三定律及伦理分析

全世界的机器人定律并没有一个统一的标准或体系&#xff0c;但是在科学文献中&#xff0c;最广为人知的是由科幻小说家阿西莫夫提出的“机器人三定律”。本文将以这些定律为基础&#xff0c;分析现有的机器人伦理和实际应用中的问题&#xff0c;给出若干实例&#xff0c;并对相…

EPSON LQ80KF II驱动 打印机 0x00000003e3

1.添加打印机 2.按名次选择共享打印机,输入共享打印机ip 3.选择创建新端口 4.选择打印机驱动

[安洵杯 2019]easy_serialize_php

源码&#xff1a; <?php$function $_GET[f];function filter($img){$filter_arr array(php,flag,php5,php4,fl1g);$filter /.implode(|,$filter_arr)./i;return preg_replace($filter,,$img); }if($_SESSION){unset($_SESSION); }$_SESSION["user"] guest; …

申请商标用什么颜色:企业和个人申请注册商标攻略!

在申请注册商标到底要用什么颜色&#xff0c;许多初次申请注册主体都不是特别清楚&#xff0c;普推知产商标老杨建议&#xff0c;在一般情况下建议尽量用黑白色&#xff0c;因为商标用黑白色在使用时可以着任何色。 在用黑色申请注册成功&#xff0c;别的主体用其它颜色要在同…

【python】PyQt5事件传递,鼠标动作捕获,键盘按键捕获原理与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

部署前端项目

常见部署方式有&#xff1a;静态托管服务、服务器部署 1. 静态托管服务 使用平台部署代码&#xff0c;比如 GitHub。 | 创建一个仓库&#xff0c;仓库名一般是 yourGithubName.github.io。 | 将打包后的静态文件文件上传到仓库。 | 在“Settings”&#xff08;选项&#xff0…

C语言编程4:复合赋值,递增递减运算符,局部变量与全局变量,本地变量,转义字符

一篇文章带你玩转C语言基础语法4&#xff1a;复合赋值&#xff0c;递增递减运算符&#xff0c;局部变量与全局变量&#xff0c;本地变量&#xff0c;转义字符 一、复合赋值&#x1f33f; 1.1&#x1f4a0;定义 赋值就是给任意一个变量或者常量赋一个值&#xff0c;这个值可以…

javaweb零碎知识3

// 假设您已经导入了 axios import axios from axios;// 获取表单元素 const form document.getElementById(myForm);// 为表单添加 submit 事件监听器 form.addEventListener(submit, function(e) {// 阻止表单的默认提交行为e.preventDefault();// 创建 FormData 对象并从表…

福建 | 南安帝兴混凝土电子签收的困难和突破

01 发展从来都是从困难开始 混凝土发货单实现无纸化签收&#xff0c;众多业内人士认为这个概念很好&#xff0c;但能否落地却大多抱有怀疑态度&#xff0c;理由多种多样&#xff1a; “行业太传统&#xff0c;接受不了新鲜事物。” “驾驶员年龄偏大&#xff0c;玩不来智能手…

【分布式系统管理框架】Zookeeper集群

1、Zookeeper &#xff08;1&#xff09;Zookeeper定义 Zookeeper是一个开源的&#xff0c;为分布式框架提供协调服务的Apache项目。 &#xff08;2&#xff09;Zookeeper工作机制 Zookeeper从设计模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架…

怎么制作gif动图,视频制作GIF动画更简单

在社交媒体和网络交流中&#xff0c;GIF动画以其生动活泼的表现形式成为了表达情感、幽默和创意的热门媒介。无论是分享日常趣事&#xff0c;还是制作专业演示&#xff0c;一个恰到好处的GIF动图总能吸引目光&#xff0c;传递信息。 但你知道吗&#xff1f;即使没有专业的设计背…

工业一体机为数字化工厂带来高效作业指导

随着工业4.0的浪潮席卷全球&#xff0c;数字化工厂的概念深入人心。在这一背景下&#xff0c;工业一体机作为数字化转型的重要一环&#xff0c;凭借其强大的功能和灵活的应用&#xff0c;为工厂实现高效作业指导提供了强大的助力。 一、工业一体机的优势&#xff1a;赋能数字化…

2024年全国青少年信息素养大赛图形化编程小高组复赛真题

2024年全国青少年信息素养大赛图形化编程小高组复赛真题 题目总数&#xff1a;6 总分数&#xff1a;100 编程题 第 1 题 问答题 请对变身鱼进行编程&#xff0c;变身鱼的初始状态已经设置&#xff0c;不需要进行修改&#xff0c; 1.当变身鱼大小大于300时&#xff0…

数据高效交互丨DolphinDB Redis 插件使用指南

DolphinDB 是一个高性能的分布式数据库。通过 Redis 插件&#xff0c;DolphinDB 用户可以轻松地与 Redis 数据库进行交互。用户不仅可以从 DolphinDB 向 Redis 发送数据&#xff0c;实现高速的数据写入操作&#xff1b;还可以从 Redis 读取数据&#xff0c;将实时数据流集成到 …

PaddleVideo:Squeeze Time算法移植

参考PaddleVideo/docs/zh-CN/contribute/add_new_algorithm.md at develop PaddlePaddle/PaddleVideo GitHubAwesome video understanding toolkits based on PaddlePaddle. It supports video data annotation tools, lightweight RGB and skeleton based action recognitio…

17集 如何用ESP-IDF编译ESP-DL深度学习工程-《MCU嵌入式AI开发笔记》

17集 如何用ESP-IDF编译ESP-DL深度学习工程-《MCU嵌入式AI开发笔记》 参考文档&#xff1a;ESP-DL 用户指南&#xff1a; https://docs.espressif.com/projects/esp-dl/zh_CN/latest/esp32/index.html 和https://docs.espressif.com/projects/esp-dl/zh_CN/latest/esp32/get-s…

PHP禁止IP访问和IP段访问(代码实例)

PHP禁止IP和IP段访问 实现IP限制是Web开发中常见的需求之一&#xff0c;它可以用于限制特定IP地址的访问权限。在PHP中&#xff0c;我们可以通过一些方法来实现IP限制。 <?//禁止某个IP$banned_ip array ("127.0.0.1",//"119.6.20.66","192.168.…

C#中简单Socket编程

C#中简单Socket编程 Socket分为面向连接的套接字(TCP套接字)和面向消息的套接字(UDP 套接字)。我们平时的网络编程是对Socket进行操作。 接下来&#xff0c;我用C#语言来进行简单的TCP通信和UDP通信。 一、TCP通信 新建项目SocketTest&#xff0c;首先添加TCP通信的客户端代…

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

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