vue3+antdv仿百度网盘样式文件夹管理组件
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue
免费下载资源
·
实现:
- 默认进入页面时,文件夹全选;
- 文件夹状态,以及文件夹内的文件选择状态,与组件联动
- 文件夹数量,根据后端数据动态生成
实现思路:
将后端数据存到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 } = item
if (all) {
if (empty) {
state.ExportData.forEach((obj) => {
obj.chooseNum = 0
obj.idlist = []
});
state.ExportData[0].chooseNum = 0
} else {
state.ExportData.forEach((obj) => {
obj.chooseNum = obj.fileIdList.length
obj.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].totalNum
let first = state.ExportData[0].chooseNum
console.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.checkedList
allState.checkedList = []
newValue.value.forEach((item, index, arr) => {
// // 处理总文件
if (item.typeName == '全部文件') {
allnum.value = item.chooseNum
let { indeterminate, checkAll } = setChoose(item.chooseNum, item.totalNum)
allState.indeterminate = indeterminate
allState.checkAll = checkAll
plainOptions.value = item.fileIdList
if (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 = false
let checkAll = false
if (chooseNum == 0) {
if (totalNum == 0) {
indeterminate = false
checkAll = true
} else {
indeterminate = false
checkAll = false
}
} else if (chooseNum < totalNum && chooseNum > 0) {
indeterminate = true
checkAll = false
} else {
indeterminate = false
checkAll = true
}
return { indeterminate, checkAll }
}
const includ = (s) => {
if (s) {
return allState.checkedList.includes(s)
}
}
const onCheckAllChange = (e: any) => {
//打印checkAll
Object.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.checked
return
}
//打印sele-checkAll
console.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";
//从后端获取数据后存入vuex
tabs.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.idlist
store.commit("setFilderCheck", { all: true, name: '', empty: false });
GitHub 加速计划 / vu / vue
207.55 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891
Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
更多推荐
已为社区贡献1条内容
所有评论(0)