330 lines
10 KiB
Vue
330 lines
10 KiB
Vue
<template>
|
||
<a-row :wrap="false">
|
||
<a-col flex="auto">
|
||
<a-row>
|
||
<a-col flex="400px">
|
||
<a-row justify="start" class="ops-row">
|
||
<a-col flex="32px" style="margin-left: 8px">
|
||
<a-tooltip title="新建文件夹">
|
||
<a-button v-if="canCreate" class="nav-btn" :icon="h(FolderAddOutlined)"
|
||
@click="$emit('create-folder')"></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px">
|
||
<a-tooltip title="上传文件">
|
||
<a-button v-if="canCreate" :icon="h(UploadOutlined)" type="primary" class="nav-btn"
|
||
@click="triggerUpload">
|
||
</a-button> </a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px" v-if="!isDirectorySelected && (isSingleItem || isSameMediaTypeSelected)">
|
||
<a-tooltip title="共享">
|
||
<a-button class="nav-btn" :icon="h(ShareAltOutlined)" @click="onShare"></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px" v-if="!isDirectorySelected && (isSingleItem || isSameMediaTypeSelected)">
|
||
<a-tooltip :title="publishTooltip">
|
||
<a-button class="nav-btn" :icon="h(NotificationOutlined)" @click="onPublish"></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px" v-if="!isDirectorySelected && isSingleItem">
|
||
<a-tooltip title="下载">
|
||
<a-button class="nav-btn" :icon="h(DownloadOutlined)" :disabled="!canAmend"
|
||
@click="onDownload"></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
|
||
<a-col flex="32px" v-if="isSingleItem">
|
||
<a-tooltip title="重命名">
|
||
<a-button class="nav-btn" :icon="h(EditOutlined)" :disabled="!canAmend"
|
||
@click="$emit('rename', selectedItems)"></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px" v-if="isSingleItem">
|
||
<a-tooltip title="移动">
|
||
<a-button class="nav-btn" :icon="h(RotateRightOutlined)" :disabled="!canAmend"
|
||
@click="$emit('move', selectedItems)"></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px" v-if="hasSelection">
|
||
<a-tooltip title="删除">
|
||
<a-popconfirm title="确定是否删除?" ok-text="是" cancel-text="否" @confirm="$emit('delete', selectedItems)">
|
||
<a-button class="nav-btn" :icon="h(DeleteOutlined, { style: { color: 'red' } })"
|
||
:disabled="!canDelete"></a-button>
|
||
</a-popconfirm>
|
||
</a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px" v-if="!isDirectorySelected && isSingleItem">
|
||
<a-tooltip :title="previewTooltip">
|
||
<a-button class="nav-btn" @click="onViewPreview"><cust-icon-font size="16"
|
||
type="icon-qingbaojiankong" /></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px" v-if="!isDirectorySelected && isSingleItem && isSingleVideo">
|
||
<a-tooltip>
|
||
<a-button class="nav-btn" @click="onPlayVideo"><cust-icon-font size="16"
|
||
type="icon-foldervideo" /></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
<a-col flex="32px" v-if="!isDirectorySelected && isSingleItem && isSingleAudio">
|
||
<a-tooltip v-if="selectedItem.IsPlay != 'Y'">
|
||
<a-button class="nav-btn" :icon="h(PlayCircleOutlined, { style: { color: 'red' } })"
|
||
@click="onPlayAudio"></a-button>
|
||
</a-tooltip>
|
||
<a-tooltip v-else-if="selectedItem.IsPause != 'Y'">
|
||
<a-button class="nav-btn" :icon="h(PauseCircleOutlined)" @click="onPauseAudio"></a-button>
|
||
</a-tooltip>
|
||
<a-tooltip v-else>
|
||
<a-button class="nav-btn" :icon="h(PlayCircleOutlined, { style: { color: 'red' } })"
|
||
@click="onContinueAudio"></a-button>
|
||
</a-tooltip>
|
||
</a-col>
|
||
</a-row>
|
||
</a-col>
|
||
</a-row>
|
||
</a-col>
|
||
<a-col :span="8" flex="none">
|
||
|
||
</a-col>
|
||
</a-row>
|
||
<FileUpload v-if="canCreate" ref="fileUploadRef" :action="uploadUrl" :headers="headers"
|
||
:resolve-file-path="resolveFilePath" :multiple="multiple" @start="onUploadStart" @progress="onUploadProgress"
|
||
@success="onUploadSuccess" @complete="onUploadComplete" @interrupted="onUploadInterrupted" @error="onUploadError" />
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, ref, h } from "vue";
|
||
import {
|
||
UploadOutlined,
|
||
FolderAddOutlined,
|
||
DownloadOutlined,
|
||
ShareAltOutlined,
|
||
NotificationOutlined,
|
||
UnorderedListOutlined,
|
||
PlayCircleOutlined,
|
||
PauseCircleOutlined,
|
||
EyeOutlined,
|
||
EditOutlined,
|
||
ScissorOutlined,
|
||
DeleteOutlined,
|
||
RotateRightOutlined,
|
||
DownOutlined,
|
||
} from "@ant-design/icons-vue";
|
||
import { DmsFileUploadViewDisplayData } from "@/data/auto/dmsFileUploadViewDisplayData";
|
||
import FileUpload from "@/components/basic/FileUpload.vue";
|
||
import fileTypePolicy from "@/policy/custom/fileTypePolicy";
|
||
|
||
const props = defineProps<{
|
||
selectedItems: DmsFileUploadViewDisplayData[];
|
||
uploadUrl: string;
|
||
headers: any;
|
||
resolveFilePath?: () => string | Promise<string>;
|
||
/** 上传时是否允许一次选多个文件,默认只允许选一个 */
|
||
multiple?: boolean;
|
||
canCreate: boolean;
|
||
canAmend: boolean;
|
||
canDelete: boolean;
|
||
canView: boolean;
|
||
}>();
|
||
|
||
const emit = defineEmits([
|
||
"create-folder",
|
||
"upload-start",
|
||
"upload-progress",
|
||
"upload-success",
|
||
"upload-complete",
|
||
"upload-interrupted",
|
||
"upload-error",
|
||
"download",
|
||
"share",
|
||
"publish",
|
||
"view-preview",
|
||
"play-video",
|
||
"play-audio",
|
||
"pause-audio",
|
||
"continue-audio",
|
||
"preview",
|
||
"rename",
|
||
"move",
|
||
"delete",
|
||
]);
|
||
|
||
const fileUploadRef = ref<any>(null);
|
||
|
||
const hasSelection = computed(() => props.selectedItems.length > 0);
|
||
const isDirectorySelected = computed(() =>
|
||
props.selectedItems.some((s) => s.IsDirectoryIndc == "Y"),
|
||
);
|
||
const isSingleItem = computed(() => props.selectedItems.length === 1);
|
||
|
||
const selectedItem = computed(() => {
|
||
return props.selectedItems[0] || ({} as DmsFileUploadViewDisplayData);
|
||
});
|
||
|
||
const selectedOrderKey = computed(() => {
|
||
return selectedItem.value?.OrderKey || selectedItem.value?.OrgFileName || "";
|
||
});
|
||
|
||
const isImage = (key: string) => {
|
||
if (!key) return false;
|
||
return /\.(png|jpe?g|gif|bmp|webp)$/i.test(key);
|
||
};
|
||
|
||
const isVideo = (key: string) => {
|
||
if (!key) return false;
|
||
return /\.(mp4|m3u8|rm|mpeg|mov|wmv|avi)$/i.test(key);
|
||
};
|
||
|
||
const isAudio = (key: string) => {
|
||
if (!key) return false;
|
||
return /\.(mp3|wav|flac|aac|ogg|wma)$/i.test(key);
|
||
};
|
||
|
||
const isSingleVideo = computed(() => {
|
||
return isSingleItem.value && isVideo(selectedOrderKey.value);
|
||
});
|
||
|
||
const isSingleAudio = computed(() => {
|
||
return isSingleItem.value && isAudio(selectedOrderKey.value);
|
||
});
|
||
|
||
/**
|
||
* 识别媒体类型,兼顾本地扩展名判断与全局 fileTypePolicy,
|
||
* 保证与发布时的类型判断一致。
|
||
*/
|
||
const mediaKind = (key: string): "image" | "video" | "audio" | "" => {
|
||
if (isImage(key) || fileTypePolicy.isImage(key)) return "image";
|
||
if (isVideo(key) || fileTypePolicy.isVideo(key)) return "video";
|
||
if (isAudio(key) || fileTypePolicy.isAudio(key)) return "audio";
|
||
return "";
|
||
};
|
||
|
||
/**
|
||
* 多选时选中项是否全部属于同一媒体类型(图片/视频/音频)。
|
||
* 满足时允许批量共享与发布。
|
||
*/
|
||
const isSameMediaTypeSelected = computed(() => {
|
||
const items = props.selectedItems;
|
||
if (items.length <= 1) return false;
|
||
const kinds = items.map((x) => mediaKind(x.OrderKey || x.OrgFileName || ""));
|
||
const kind = kinds[0];
|
||
return kind !== "" && kinds.every((k) => k === kind);
|
||
});
|
||
|
||
const publishTooltip = computed(() => {
|
||
if (isImage(selectedOrderKey.value)) return "发布相册";
|
||
if (isVideo(selectedOrderKey.value)) return "发布影视专辑";
|
||
if (isAudio(selectedOrderKey.value)) return "发布音乐专辑";
|
||
return "发布";
|
||
});
|
||
|
||
const previewTooltip = computed(() => {
|
||
if (isImage(selectedOrderKey.value)) return "相册展览";
|
||
if (isVideo(selectedOrderKey.value)) return "影视厅播放";
|
||
if (isAudio(selectedOrderKey.value)) return "音乐厅播放";
|
||
return "预览";
|
||
});
|
||
|
||
const triggerUpload = () => {
|
||
fileUploadRef.value?.trigger();
|
||
};
|
||
|
||
const onUploadStart = (file: File) => {
|
||
emit("upload-start", file);
|
||
};
|
||
|
||
const onUploadProgress = (payload: any) => {
|
||
emit("upload-progress", payload);
|
||
};
|
||
|
||
const onUploadSuccess = (payload: any) => {
|
||
emit("upload-success", payload);
|
||
};
|
||
|
||
/** 一批文件全部传完(多选时用它统一刷新一次,避免每个文件都刷一遍列表) */
|
||
const onUploadComplete = (payload: any) => {
|
||
emit("upload-complete", payload);
|
||
};
|
||
|
||
/** 上传中断:断点还在,交给页面去问用户要不要续传 */
|
||
const onUploadInterrupted = (payload: any) => {
|
||
emit("upload-interrupted", payload);
|
||
};
|
||
|
||
/** 从断点继续上传 */
|
||
const resumeUpload = () => {
|
||
fileUploadRef.value?.resume();
|
||
};
|
||
|
||
/** 放弃这次上传,清掉断点 */
|
||
const discardUpload = () => {
|
||
fileUploadRef.value?.discard();
|
||
};
|
||
|
||
defineExpose({ resumeUpload, discardUpload });
|
||
|
||
const onUploadError = (error: unknown) => {
|
||
emit("upload-error", error);
|
||
};
|
||
|
||
const onDownload = () => {
|
||
emit("download", props.selectedItems);
|
||
};
|
||
|
||
const onShare = () => {
|
||
emit("share", props.selectedItems);
|
||
};
|
||
|
||
const onPublish = () => {
|
||
emit("publish", props.selectedItems);
|
||
};
|
||
|
||
const onViewPreview = () => {
|
||
emit("view-preview", props.selectedItems);
|
||
};
|
||
|
||
const onPlayVideo = () => {
|
||
emit("play-video", selectedItem.value);
|
||
};
|
||
|
||
const onPlayAudio = () => {
|
||
emit("play-audio", selectedItem.value);
|
||
};
|
||
|
||
const onPauseAudio = () => {
|
||
emit("pause-audio", selectedItem.value);
|
||
};
|
||
|
||
const onContinueAudio = () => {
|
||
emit("continue-audio", selectedItem.value);
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 按钮之间统一 8px 间距。
|
||
原来是靠 24 栅格 span=2 撑开的,间距会随容器宽度变化(400px 时只有 1px 左右),
|
||
现在按钮列固定 32px(与 .nav-btn 同宽),真正的间距交给 gap:8px */
|
||
.ops-row {
|
||
gap: 4px;
|
||
margin-top: 4px;
|
||
flex-wrap: nowrap;
|
||
}
|
||
|
||
.nav-btn {
|
||
width: 32px;
|
||
height: 32px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.nav-btn:hover:not(:disabled) {
|
||
background: #e6f4ff;
|
||
color: #1677ff;
|
||
}
|
||
|
||
.nav-btn:disabled {
|
||
color: #bfbfbf;
|
||
}
|
||
</style>
|