update FileExplorer

This commit is contained in:
Timi
2026-04-03 14:58:30 +08:00
parent ded231671a
commit 603a503644
9 changed files with 335 additions and 178 deletions

View File

@@ -0,0 +1,136 @@
<template>
<recycle-scroller
class="scroller"
:items="rows"
:item-size="158"
key-field="key"
v-slot="{ item }"
>
<div class="grid-row">
<article
v-for="entry in item.items"
:key="entry.path"
class="card glass-white"
@click="emit('open', entry)"
>
<div :class="['icon', entry.type]">
<t-icon :name="entry.type === 'dir' ? 'folder' : entry.icon" />
</div>
<div class="meta">
<p class="name" v-text="entry.name"></p>
<p class="desc" v-text="formatItemDesc(entry)"></p>
</div>
</article>
<div v-if="item.items.length < columns" class="card ghost"></div>
</div>
</recycle-scroller>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { RecycleScroller } from "vue-virtual-scroller";
import { formatItemDesc, type ExplorerItem } from "./fileExplorer.shared";
interface GridRow {
key: string;
items: ExplorerItem[];
}
const columns = 2;
const props = defineProps<{
items: ExplorerItem[];
}>();
const emit = defineEmits<{
open: [item: ExplorerItem];
}>();
const rows = computed<GridRow[]>(() => {
const nextRows: GridRow[] = [];
for (let index = 0; index < props.items.length; index += columns) {
const chunk = props.items.slice(index, index + columns);
nextRows.push({
key: chunk.map((item) => item.path).join("|"),
items: chunk
});
}
return nextRows;
});
</script>
<style scoped lang="less">
.scroller {
height: 100%;
}
.grid-row {
gap: .75rem;
display: grid;
padding-bottom: .75rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.card {
gap: .75rem;
display: flex;
padding: 1rem .875rem;
border: 1px solid var(--app-line);
border-radius: 1rem;
flex-direction: column;
background: var(--app-card);
box-shadow: 0 .35rem 1rem rgba(17, 32, 56, .05);
cursor: pointer;
&.ghost {
visibility: hidden;
pointer-events: none;
}
}
.icon {
width: 2.75rem;
height: 2.75rem;
display: flex;
border-radius: .9rem;
align-items: center;
justify-content: center;
color: #fff;
background: linear-gradient(135deg, #4d8dff, #76a9ff);
&.dir {
background: linear-gradient(135deg, #ff9c3d, #ffbf69);
}
}
.meta {
gap: .3rem;
min-width: 0;
display: flex;
flex-direction: column;
}
.name,
.desc {
margin: 0;
word-break: break-all;
}
.name {
color: var(--app-text);
font-size: 1rem;
}
.desc {
color: var(--app-sub);
font-size: .8125rem;
}
:global(.theme-dark) {
.card {
box-shadow: 0 .35rem 1rem rgba(0, 0, 0, .22);
}
}
</style>