vue + ts 文件管理层级嵌套文件夹文件切换

<template>
  <div :class="prefixCls" :style="getWrapStyle">
    <!-- 头部导航 -->
    <n-card :bordered="false" style="height: 60px">
      <div class="nav">
        <div class="breadcrumb_all">
          <n-button size="tiny" @click="ann" style="margin-right: 5px">按钮</n-button>
          <n-checkbox v-model:checked="isCheckeds" :indeterminate="indeterminate" @click="allSelectBtn" v-if="!isShow">全选 </n-checkbox>
          <span :class="{ red_color: breadcrumbsList && breadcrumbsList.length > 0, size_bold: breadcrumbsList.length === 0 }" v-on:click="handleAllFolderBtn">文件</span>
          <span class="arrow_right" v-if="breadcrumbsList.length >= 1">› </span>
        </div>
        <div class="breadcrumb_item">
          <ul>
            <li v-for="(item, idx) in breadcrumbsList" :key="item.title" v-on:click="handleBreadcrumbsItem(item, idx)">
              <span class="bc_name" :class="idx == breadcrumbsList.length - 1 ? 'navCol' : ' '"> {{ item.title }}</span>
              <span class="arrow_right" v-if="idx !== breadcrumbsList.length - 1"> › </span>
            </li>
          </ul>
        </div>
      </div>
    </n-card>
    <!-- 文件图表 -->
    <n-scrollbar style="height: calc(100vh - 55px)">
        <div class="table_files" v-if="!isShow">
          <ul>
            <li v-for="item in fileAndFolderList" :key="item.Value || item.ID" :title="item.Text || item.WorkTitle" @contextmenu="handleContextMenu" :class="{ selected_bgc: flieId == item.Value || item.isChecked }" v-on:mouseenter="fileMouseEnter(item)" v-on:mouseleave="fileMouseLeave" v-on:dblclick="handleBreakdown(item)" id="testId">
              <div class="single_checkbox" @click.stop="singleSelect(item)">
                <n-checkbox v-show="flieId === item.Value || item.isChecked" v-model:checked="item.isChecked"></n-checkbox>
              </div>
              <n-icon :component="item.Icons" size="50" :depth="1" />
              <div class="flies_title">{{ item.name || item.title }}</div>
              <n-dropdown placement="bottom-start" trigger="manual" :x="x" :y="y" :options="options" :key="item.path" :show="showDropdown" :on-clickoutside="onClickoutside" @click="handleSelect(item)" />
            </li>
          </ul>
        </div>
        <n-empty description="你什么也找不到" size="huge" class="box" v-else>
          <template #extra>
            <n-button size="small"> 看看别的 </n-button>
          </template>
        </n-empty>
    </n-scrollbar>
  </div>
</template>

<script lang="ts" setup>
import type { StyleValue } from 'vue'
import { useDesign } from '/@/composables/web/useDesign'
import { useContentSizeInject, useAppInject } from '/@/composables/web/useAppInject'
import { getDir } from '/@/api/main/file'
import { fileDownloadIcon, folderDownloadIcon, moreIcon } from '/@/utils/file'
const { getIsMobile } = useAppInject()
const { prefixCls } = useDesign('iframe-page')
const { height } = useContentSizeInject()
const isShow = ref(false)
const title = ref()
const isCheckeds = ref(false)
const indeterminate = ref(false)
const breadcrumbsList: any = ref([]) // 存放头部导航数据
const fileAndFolderList: any = ref([]) // 存放文件夹和文件数据
const flieId = ref('')
const dataInfo = ref([])
const url = ref('/')
const options = ref([
  {
    label: '杰·盖茨比',
    key: 'jay gatsby'
  },
  {
    label: '黛西·布坎南',
    key: 'daisy buchanan'
  },
  {
    type: 'divider',
    key: 'd1'
  },
  {
    label: '尼克·卡拉威',
    key: 'nick carraway'
  }
])

const showDropdown = ref(false)
const x = ref(0)
const y = ref(0)
const onClickoutside = () => {
  showDropdown.value = false
}
const handleSelect = item => {
  showDropdown.value = false
}

const handleContextMenu = (e: MouseEvent) => {
  e.preventDefault()
  showDropdown.value = false
  nextTick().then(() => {
    showDropdown.value = true
    x.value = e.clientX
    y.value = e.clientY
  })
}
const getWrapStyle = computed((): StyleValue => {
  return {
    height: `${unref(height)}px`
  }
})

onMounted(() => {
  getTableData(url.value)
})

const getTableData = url => {
  getDir(url).then(
    res => {
      title.value = res.title
      res.children.forEach(item => {
        ;(item.Icons = item.type == 0 ? folderDownloadIcon : fileDownloadIcon), (item.isChecked = false)
        item.Value = item.name
        item.FileFormat = 'AA'
      })

      fileAndFolderList.value = res.children
      console.log(fileAndFolderList.value)
    },
    error => {
      isShow.value = true
      console.log(error)
    }
  )
}
const ann = () => {
  fileAndFolderList.value &&
    fileAndFolderList.value.forEach(item => {
      if (item.isChecked) {
        dataInfo.value.push(item)
      }
    })
  console.log(dataInfo, './')
}
// 点击头部面包屑
const handleBreadcrumbsItem = (item, index) => {
  console.log(item, 'pp')

  getTableData(item.path)
  breadcrumbsList.value = breadcrumbsList.value.splice(0, index + 1)
  isCheckeds.value = false
  indeterminate.value = false
}
// 点击全部文件
const handleAllFolderBtn = () => {
  getTableData('/')
  breadcrumbsList.value = []
  isShow.value = false
  isCheckeds.value = false
  indeterminate.value = false
}
// 点击全选框
const allSelectBtn = () => {
  fileAndFolderList.value &&
    fileAndFolderList.value.forEach(item => {
      item.isSelected = !item.isChecked
      item.isChecked = !item.isChecked
    })
}
// 鼠标经过 li
const fileMouseEnter = item => {
  flieId.value = item.Value || item.ID
}
// 鼠标离开 li
const fileMouseLeave = () => {
  flieId.value = ''
}
// 双击文件夹进入下一级
const handleBreakdown = (item: any) => {
  getTableData(item.path)
  breadcrumbsList.value.push({ path: item.path, title: item.name })
  isCheckeds.value = false
  indeterminate.value = false
}

// 点击单选框
const singleSelect = item => {
  if (fileAndFolderList.value.every(e => e.isChecked)) {
    isCheckeds.value = true
    indeterminate.value = false
  } else {
    if (!fileAndFolderList.value.every(e => e.isChecked)) {
      indeterminate.value = false
    }
    if (fileAndFolderList.value.some(e => e.isChecked)) {
      indeterminate.value = true
    }
    isCheckeds.value = false
  }
  // item.isSelected = item.isChecked
  // flieId.value = item.Value
}
</script>

<style lang="less" scoped>
.light-green {
  height: 108px;
  background-color: rgba(0, 128, 0, 0.12);
}

.green {
  height: 108px;
  background-color: rgba(0, 128, 0, 0.24);
}
</style>
<style lang="less">
.nav {
  display: flex;
  flex-wrap: nowrap;
  padding: 5px;
  // background-color: #ffffff;
  font-size: 14px;
  font-family: Microsoft YaHei;
  font-weight: 400;
  color: #333333;
  .navCol {
    color: #000;
    font-weight: 900;
  }
}

.red_color {
  color: #ccc;
}

.breadcrumb_all > span.size_bold {
  font-weight: bold;
  padding-left: 0;
}

.bc_name {
  color: #ccc;
  cursor: pointer;
}

.arrow_right {
  padding: 0 10px;
}

.breadcrumb_item > ul {
  display: flex;
}

.table_files {
  height: 100vh;
  background-color: #ffffff;
}
.n-dropdown-menu {
  box-shadow: 1px 1px 1px #ccc;
}
.table_top {
  padding-left: 15px;
  height: 38px;
  line-height: 38px;
  font-size: 14px;
  font-family: Microsoft YaHei;
  font-weight: 400;
  color: #333333;
  background-color: #f5f5f5;
  box-shadow: 0px -1px 0px 0px #e8e8e8;
}

.table_top > label > span {
  padding-left: 10px;
}

.table_files > ul {
  display: flex;
  flex-wrap: wrap;
}

.table_files > ul > li {
  margin: 10px 20px 0px 20px;
  width: calc(10% - 40px);
  min-width: 144px;
  height: 160px;
  text-align: center;
  position: relative;
}
//鼠标移上显示
.table_files > ul > li.selected_bgc {
  background-color: rgba(132, 133, 141, 0.08);
}

.table_files > ul > li > .format_img {
  width: 56px;
  height: 56px;
  margin: auto;
}
.box {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 80vh;
  img {
    width: 30%;
  }
}
.table_files > ul > li > .public_img {
  width: 48px;
  height: 17px;
  position: absolute;
  top: 76px;
  left: 35px;
}

.single_checkbox {
  margin: 10px;
  height: 20px;
  line-height: 20px;
}

.single_checkbox > div {
  display: flex;
  justify-content: space-between;
}

.flies_title {
  margin: 20px 0 20px;
  width: 100%;
  padding: 0px 15px;
  font-size: 14px;
  font-family: PingFang SC;
  font-weight: 400;
  color: #333333;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  word-break: break-all;
}
@media screen and (max-width: 420px) {
  .table_files > ul {
    display: flex;
    justify-content: space-around;
    > li {
      // background-color: red;
      margin: 20px;
      // width: calc(10% - 1px) !important;
      min-width: 124px;
      height: 100px;
      text-align: center;
      position: relative;
      margin: 0 0 5px 0 !important;
    }
  }
}
</style>

Vue 3中使用TypeScript进行文件管理,可以按照以下步骤进行: 1. 将文件放在你想放的项目文件夹下,可以创建一个`src`文件夹用于存放所有的源代码文件。 2. 根据需要,创建不同的文件夹来组织你的代码。例如,可以创建一个`components`文件夹来存放所有的组件,一个`views`文件夹来存放页面组件,一个`utils`文件夹来存放工具类等等。你还可以创建其他文件夹来存放配置信息、样式、图片等。 3. 使用命名约定来命名你的文件。例如,你可以为每个组件创建一个以`.vue`为后缀的文件,并使用PascalCase命名方式来命名文件和组件。 4. 在文件中编写相应的代码。根据需要,你可以使用`import`语句引入其他的文件或模块,例如引入Vue的相关内容、第三方库或自定义的组件等。 5. 在需要使用的地方,使用`export`关键字来导出你的代码。这样可以让其他模块或文件可以引用和使用你的代码。 6. 在其他文件中,使用`import`语句来引入你导出的代码。这样就可以在其他文件中使用你的组件、工具类或其他功能了。 以上是一种基本的文件管理方式,根据项目的具体需求和规模,你可以进一步细化和组织你的文件结构。请根据你的项目需求进行相应的调整和扩展。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [cron定时任务表达式组件(vue3+ts+naiveUI)](https://download.csdn.net/download/weixin_45743541/86729277)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [vue3+ts通用管理后台练习项目](https://blog.csdn.net/weixin_46463984/article/details/127470580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值