ow
This commit is contained in:
1 parent
8a2adb0a92
commit
bd678b0112
22 files changed
+1968
-81
No files matched your search
@@ -0,0 +1,269 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 文件网格列表:卡片展示文件夹图标 / 文件类型图标、名称、元数据(文件大小 + 修改时间,
|
||||
* 文件夹只有时间、不显示用户),右上角三点「更多」。viewMode 支持 grid(三列卡片)与 list(单行列表)。
|
||||
* 多选模式(selectMode)下点击卡片改为勾选/取消;数据、选中集均由页面持有,这里只渲染与上报。
|
||||
*/
|
||||
import type { PropType } from "vue";
|
||||
import type { FileItem, FileKind } from "@/data/custom/fileBrowser";
|
||||
|
||||
const props = defineProps({
|
||||
/** 已过滤排序好的条目,组件按原顺序渲染 */
|
||||
files: {
|
||||
type: Array as PropType<FileItem[]>,
|
||||
default: () => [],
|
||||
},
|
||||
/** 排布模式:grid 九宫格 / list 列表 */
|
||||
viewMode: {
|
||||
type: String as PropType<"grid" | "list">,
|
||||
default: "grid",
|
||||
},
|
||||
/** 是否处于多选模式 */
|
||||
selectMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/** 多选模式下已勾选的 id 集合 */
|
||||
selectedIds: {
|
||||
type: Array as PropType<number[]>,
|
||||
default: () => [],
|
||||
},
|
||||
/** 空列表提示文案(页面翻译好传入) */
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
/** 点开条目(非多选模式的单击) */
|
||||
(e: "open", file: FileItem): void;
|
||||
/** 卡片右上角「更多」 */
|
||||
(e: "more", file: FileItem): void;
|
||||
/** 多选模式下勾选状态变化 */
|
||||
(e: "toggle-select", id: number): void;
|
||||
}>();
|
||||
|
||||
// 文件类型 -> 图标与配色(文件夹走纯 CSS 造型,不用字体图标)
|
||||
const kindVisual: Record<Exclude<FileKind, "folder">, { icon: string; color: string }> = {
|
||||
image: { icon: "photo-fill", color: "#0ea5e9" },
|
||||
video: { icon: "play-right-fill", color: "#8b5cf6" },
|
||||
document: { icon: "file-text-fill", color: "#3b82f6" },
|
||||
audio: { icon: "volume-up", color: "#f59e0b" },
|
||||
};
|
||||
|
||||
function visualOf(kind: FileKind): { icon: string; color: string } | null {
|
||||
return kind === "folder" ? null : kindVisual[kind];
|
||||
}
|
||||
|
||||
function isSelected(file: FileItem): boolean {
|
||||
return props.selectedIds.includes(file.id);
|
||||
}
|
||||
|
||||
function onCardTap(file: FileItem): void {
|
||||
if (props.selectMode) {
|
||||
emit("toggle-select", file.id);
|
||||
return;
|
||||
}
|
||||
emit("open", file);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="file-grid-list" :class="{ 'file-grid-list--list': viewMode === 'list' }">
|
||||
<view v-if="!files.length" class="file-grid-list__empty">
|
||||
<text class="file-grid-list__empty-text">
|
||||
{{ emptyText }}
|
||||
</text>
|
||||
</view>
|
||||
<view v-for="file in files" :key="file.id" class="fgl-card"
|
||||
:class="{ 'fgl-card--selected': selectMode && isSelected(file) }">
|
||||
<view class="fgl-card__more" @tap.stop="emit('more', file)">
|
||||
<u-icon name="more-dot-fill" size="30" color="#b8bcc4" />
|
||||
</view>
|
||||
|
||||
<view class="fgl-card__body" @tap="onCardTap(file)">
|
||||
<!-- 文件夹:纯 CSS 造型(图标库没有 folder),主体 + 左上页签 -->
|
||||
<view v-if="file.kind === 'folder'" class="fgl-card__folder">
|
||||
<view class="fgl-card__folder-tab" />
|
||||
</view>
|
||||
<u-icon v-else :name="visualOf(file.kind)!.icon" size="88" :color="visualOf(file.kind)!.color" />
|
||||
|
||||
<view class="fgl-card__info">
|
||||
<text class="fgl-card__name">
|
||||
{{ file.name }}
|
||||
</text>
|
||||
<view class="fgl-card__meta">
|
||||
<text v-if="file.sizeText" class="fgl-card__meta-text">
|
||||
{{ file.sizeText }}
|
||||
</text>
|
||||
<text class="fgl-card__meta-text">
|
||||
{{ file.modifiedAt }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="selectMode" class="fgl-card__check">
|
||||
<u-icon :name="isSelected(file) ? 'checkmark-circle-fill' : 'checkmark-circle'" size="40"
|
||||
:color="isSelected(file) ? '#2b8fff' : '#c0c4cc'" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.file-grid-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 16rpx 20rpx 40rpx;
|
||||
}
|
||||
|
||||
.file-grid-list__empty {
|
||||
width: 100%;
|
||||
padding: 120rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file-grid-list__empty-text {
|
||||
font-size: 26rpx;
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
.fgl-card {
|
||||
position: relative;
|
||||
width: calc(100% / 3 - 16rpx);
|
||||
margin: 8rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
background-color: #f7f8fa;
|
||||
|
||||
&--selected {
|
||||
background-color: #eaf4ff;
|
||||
}
|
||||
}
|
||||
|
||||
.fgl-card__more {
|
||||
position: absolute;
|
||||
top: 12rpx;
|
||||
right: 12rpx;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 52rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: rgba(31, 35, 41, 0.05);
|
||||
}
|
||||
|
||||
.fgl-card__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 36rpx 12rpx 0;
|
||||
}
|
||||
|
||||
/* ---- 纯 CSS 文件夹:蓝色渐变圆角主体 + 左上页签 ---- */
|
||||
.fgl-card__folder {
|
||||
position: relative;
|
||||
width: 132rpx;
|
||||
height: 104rpx;
|
||||
margin: 14rpx 0;
|
||||
border-radius: 16rpx;
|
||||
background: linear-gradient(135deg, #6ec2ff 0%, #2b8fff 100%);
|
||||
box-shadow: 0 10rpx 20rpx rgba(43, 143, 255, 0.25);
|
||||
}
|
||||
|
||||
.fgl-card__folder-tab {
|
||||
position: absolute;
|
||||
top: -16rpx;
|
||||
left: 14rpx;
|
||||
width: 56rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 10rpx 10rpx 0 0;
|
||||
background: linear-gradient(135deg, #6ec2ff 0%, #4aa6ff 100%);
|
||||
}
|
||||
|
||||
.fgl-card__info {
|
||||
width: 100%;
|
||||
padding: 0 12rpx;
|
||||
}
|
||||
|
||||
.fgl-card__name {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #1f2329;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fgl-card__meta {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
margin-top: 6rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fgl-card__meta-text {
|
||||
flex-shrink: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
font-size: 22rpx;
|
||||
color: #9aa0a6;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fgl-card__check {
|
||||
position: absolute;
|
||||
right: 12rpx;
|
||||
bottom: 16rpx;
|
||||
}
|
||||
|
||||
/* ---- 列表模式:单行排布,覆盖网格默认样式 ---- */
|
||||
.file-grid-list--list {
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
padding: 8rpx 24rpx 40rpx;
|
||||
}
|
||||
|
||||
.file-grid-list--list .fgl-card {
|
||||
width: 100%;
|
||||
margin: 0 0 16rpx;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.file-grid-list--list .fgl-card__more {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.file-grid-list--list .fgl-card__body {
|
||||
flex-direction: row;
|
||||
padding: 24rpx 64rpx 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.file-grid-list--list .fgl-card__folder {
|
||||
width: 72rpx;
|
||||
height: 56rpx;
|
||||
margin: 0 24rpx 0 0;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.file-grid-list--list .fgl-card__folder-tab {
|
||||
top: -10rpx;
|
||||
left: 8rpx;
|
||||
width: 30rpx;
|
||||
height: 14rpx;
|
||||
border-radius: 6rpx 6rpx 0 0;
|
||||
}
|
||||
|
||||
.file-grid-list--list .fgl-card__info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user