20260809223428
This commit is contained in:
1 parent
d72ca4193d
commit
c4891538a7
466 files changed
+21000
-3339
No files matched your search
@@ -1,277 +0,0 @@
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { Trash2, Download, Eye, MoreHorizontal } from "@lucide/vue";
|
||||
import { getPreviewUrl } from "@/services/fileService";
|
||||
import {
|
||||
extFromName,
|
||||
extColor,
|
||||
isImageType,
|
||||
formatSize,
|
||||
} from "./fileVisual";
|
||||
|
||||
const props = defineProps({
|
||||
file: { type: Object, required: true },
|
||||
selected: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
const emit = defineEmits(["select", "preview", "download", "delete"]);
|
||||
|
||||
const isPending = computed(() => !!props.file?._pending);
|
||||
const isImage = computed(() => isImageType(props.file?.mx_filename || ""));
|
||||
|
||||
const thumbnailUrl = computed(() => {
|
||||
if (!isImage.value) return "";
|
||||
return props.file?._blobUrl || getPreviewUrl(props.file) || "";
|
||||
});
|
||||
|
||||
const extname = computed(() => extFromName(props.file?.mx_filename || ""));
|
||||
const extnameUpper = computed(() => extname.value.toUpperCase() || "?");
|
||||
const typeColor = computed(() => extColor(props.file?.mx_filename || ""));
|
||||
|
||||
function onCheck(e) {
|
||||
emit("select", props.file, e.target.checked);
|
||||
}
|
||||
|
||||
function onCardClick() {
|
||||
if (!isPending.value) emit("preview", props.file);
|
||||
}
|
||||
|
||||
function onDownload() {
|
||||
if (!isPending.value) emit("download", props.file);
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
emit("delete", props.file);
|
||||
}
|
||||
|
||||
function onMoreClick(info) {
|
||||
if (info?.key === "preview" && !isPending.value) emit("preview", props.file);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fcard"
|
||||
:class="{ 'fcard--selected': selected, 'fcard--pending': isPending }"
|
||||
:title="file.mx_filename"
|
||||
@click="onCardClick"
|
||||
>
|
||||
<!-- 缩略图区域 -->
|
||||
<div class="fcard__thumb">
|
||||
<img v-if="isImage && thumbnailUrl" :src="thumbnailUrl" alt="" />
|
||||
<div v-else class="fcard__type" :style="{ '--c': typeColor }">
|
||||
{{ extnameUpper }}
|
||||
</div>
|
||||
|
||||
<!-- 左上 checkbox(始终可见) -->
|
||||
<span class="fcard__check" @click.stop>
|
||||
<a-checkbox :checked="selected" @change="onCheck" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 文件信息 -->
|
||||
<div class="fcard__info">
|
||||
<div class="fcard__name">
|
||||
<span class="fcard__title" :title="file.mx_filename">{{
|
||||
file.mx_filename
|
||||
}}</span>
|
||||
<a-tag v-if="isPending" class="fcard__tag">待上传</a-tag>
|
||||
</div>
|
||||
<div class="fcard__meta">{{ formatSize(file.mx_filesize) }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作栏:图标按钮(无文字,语言无关,避免多语言下文字长度撑破布局) -->
|
||||
<div class="fcard__footer" @click.stop>
|
||||
<a-button type="text" size="small" :disabled="isPending" @click="onDownload">
|
||||
<template #icon><Download :size="15" /></template>
|
||||
</a-button>
|
||||
<span class="fcard__divider" />
|
||||
<a-button type="text" size="small" danger @click="onDelete">
|
||||
<template #icon><Trash2 :size="15" /></template>
|
||||
</a-button>
|
||||
<span class="fcard__divider" />
|
||||
<a-dropdown :trigger="['click']">
|
||||
<a-button type="text" size="small" :disabled="isPending">
|
||||
<template #icon><MoreHorizontal :size="15" /></template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="onMoreClick">
|
||||
<a-menu-item key="preview">
|
||||
<Eye :size="13" /> 预览
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.fcard {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--fms-border);
|
||||
border-radius: 6px;
|
||||
background: var(--fms-surface);
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
&--selected {
|
||||
border-color: var(--fms-primary);
|
||||
}
|
||||
|
||||
&--pending {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
/* 缩略图 */
|
||||
.fcard__thumb {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 84px;
|
||||
background: var(--fms-hover);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
/* 彩色类型方块(签名元素) */
|
||||
.fcard__type {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--c);
|
||||
background: color-mix(in srgb, var(--c) 14%, #fff);
|
||||
border: 1px solid color-mix(in srgb, var(--c) 32%, #fff);
|
||||
}
|
||||
|
||||
/* checkbox:直接浮在缩略图上,不加底块/阴影,保持轻量 */
|
||||
.fcard__check {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
z-index: 3;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 常驻操作栏:下载 / 删除 / 更多 */
|
||||
.fcard__footer {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid var(--fms-border);
|
||||
background: var(--fms-surface);
|
||||
|
||||
:deep(.ant-btn) {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
height: 32px;
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
/* 预留 1px 透明边框,保证 hover 时宽度恒定,不再因边框出现而收缩 */
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
color: var(--fms-text-secondary);
|
||||
/* 仅过渡颜色,避免 hover 时因 all 过渡引发的闪烁/位移 */
|
||||
transition: background-color 0.15s, color 0.15s;
|
||||
|
||||
/* 关闭 antdv 按钮自带的波纹伪元素,消除 hover/点击时的边框状残影与闪烁 */
|
||||
&::before,
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
color: var(--fms-primary);
|
||||
background: var(--fms-hover);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
&.ant-btn-dangerous:hover:not(:disabled) {
|
||||
color: var(--fms-danger);
|
||||
background: color-mix(in srgb, var(--fms-danger) 10%, #fff);
|
||||
}
|
||||
|
||||
/* 明确的禁用态:覆盖 antdv 默认 + 上面无条件 color 覆盖,
|
||||
让 pending 时的下载/更多按钮图标明显变淡灰、鼠标显示禁止手势,
|
||||
lucide 图标用 currentColor 自动跟随。 */
|
||||
&:disabled,
|
||||
&[disabled] {
|
||||
color: var(--fms-disabled);
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
cursor: not-allowed;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作栏按钮间的竖向分隔线:独立元素,不依赖按钮类名 / 禁用态,
|
||||
避免「更多」按钮被 a-dropdown 包裹且禁用时 border 被抹掉导致分隔线消失。 */
|
||||
.fcard__divider {
|
||||
flex: none;
|
||||
width: 1px;
|
||||
align-self: stretch;
|
||||
background: var(--fms-border);
|
||||
}
|
||||
|
||||
/* 文件信息 */
|
||||
.fcard__info {
|
||||
padding: 7px 10px 9px;
|
||||
}
|
||||
|
||||
.fcard__name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.fcard__title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--fms-text);
|
||||
line-height: 1.5;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fcard__tag {
|
||||
flex: none;
|
||||
margin: 0;
|
||||
padding: 0 5px;
|
||||
font-size: 11px;
|
||||
line-height: 16px;
|
||||
color: var(--fms-warning);
|
||||
background: color-mix(in srgb, var(--fms-warning) 12%, #fff);
|
||||
border: 1px solid color-mix(in srgb, var(--fms-warning) 35%, #fff);
|
||||
}
|
||||
|
||||
.fcard__meta {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: var(--fms-text-secondary);
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user