|
@@ -0,0 +1,345 @@
|
|
|
+<template>
|
|
|
+ <com-head :options="[{ name: '媒体库', value: '2' }]">
|
|
|
+ <el-form label-width="97px" inline>
|
|
|
+ <el-form-item label="名称:">
|
|
|
+ <el-input v-model="state.query.name" placeholder="请输入"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="类型:">
|
|
|
+ <el-select v-model="state.query.fileType" clearable placeholder="请选择">
|
|
|
+ <el-option label="图片" value="0"></el-option>
|
|
|
+ <el-option label="视频" value="1"></el-option>
|
|
|
+ <el-option label="音频" value="2"></el-option>
|
|
|
+ <el-option label="模型" value="3"></el-option>
|
|
|
+ <el-option label="其他" value="4"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="分组:">
|
|
|
+ <el-select v-model="state.query.dictId" clearable placeholder="请选择">
|
|
|
+ <el-option v-for="group in groupList" :key="group.id" :label="group.dictName" :value="group.id"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item class="searh-btns" style="grid-area: 1 / 4 / 2 / 4">
|
|
|
+ <el-button type="primary" @click="refresh">查询</el-button>
|
|
|
+ <el-button type="primary" plain @click="queryReset">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </com-head>
|
|
|
+
|
|
|
+ <div class="body-layer">
|
|
|
+ <div class="body-head">
|
|
|
+ <h3 style="visibility: hidden">媒体列表</h3>
|
|
|
+ <div class="table-ctrl-right">
|
|
|
+ <el-button type="primary" @click="groupManage" v-pdpath="'group'">分组管理</el-button>
|
|
|
+ <el-button type="primary" @click="uploadMediaHandler" v-pdpath="'upload'"> 上传 </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-table
|
|
|
+ ref="multipleTable"
|
|
|
+ :data="state.table.rows"
|
|
|
+ size="large"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <!-- <el-table-column label="序号" width="70" v-slot:default="{ $index }">
|
|
|
+ <div style="text-align: center">
|
|
|
+ {{ state.pag.size * (state.pag.currentPage - 1) + $index + 1 }}
|
|
|
+ </div>
|
|
|
+ </el-table-column> -->
|
|
|
+ <el-table-column label="名称">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <p class="oper-span tip clickable" @click="floadileUrl(row)">{{ row.fileName }}</p>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="文件类型" prop="fileTypeStr"></el-table-column>
|
|
|
+ <el-table-column label="文件格式" prop="fileFormat"></el-table-column>
|
|
|
+ <el-table-column label="大小" prop="fileSize">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatFileSize(row.fileSize) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="分组" prop="dictName"></el-table-column>
|
|
|
+ <el-table-column label="状态" prop="status">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatStatus(row.status) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="上传时间" width="200px" prop="updateTime">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatDateTime(row.updateTime) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="200px" v-slot:default="{ row }">
|
|
|
+ <span class="oper-span" @click="downloadMediaHandler(row)" v-pdpath="'view'">下载</span>
|
|
|
+ <span class="oper-span" @click="downloadHashHandler(row)" v-pdpath="'view'">hash</span>
|
|
|
+ <span class="oper-span" v-if="row.useType != 'animation'" @click="editMediaHandler(row)" v-pdpath="'view'">编辑</span>
|
|
|
+ <span class="oper-span delBtn" v-if="row.useType != 'animation'" @click="confirmDelete(row)" v-pdpath="'view'"> 删除 </span>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <com-pagination
|
|
|
+ @size-change="changPageSize"
|
|
|
+ @current-change="changPageCurrent"
|
|
|
+ :current-page="state.pag.currentPage"
|
|
|
+ :page-size="state.pag.size"
|
|
|
+ :total="state.pag.total"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <GroupManage
|
|
|
+ v-model:visible="groupManageVisible"
|
|
|
+ @refresh="refreshList"
|
|
|
+ />
|
|
|
+ <UploadMedia
|
|
|
+ v-model:visible="uploadVisible"
|
|
|
+ :groupList="groupList"
|
|
|
+ @refresh="refreshList"
|
|
|
+ />
|
|
|
+ <EditMedia
|
|
|
+ v-model:visible="editVisible"
|
|
|
+ :groupList="groupList"
|
|
|
+ :mediaData="currentMedia"
|
|
|
+ @refresh="refreshList"
|
|
|
+ />
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import comSelect from "@/components/company-select/index.vue";
|
|
|
+import comPagination from "@/components/pagination/index.vue";
|
|
|
+import comHead from "@/components/head/index.vue";
|
|
|
+import { usePagging } from "@/hook/pagging";
|
|
|
+import { confirm } from "@/helper/message";
|
|
|
+import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
+import { getMediaPagging, uploadNewMedia, deleteMediaItem, Media, getGroupList, downloadMedia, downloadHash } from "@/store/mediaLibrary";
|
|
|
+import { ref, onMounted, watch } from "vue";
|
|
|
+import GroupManage from './groupManage.vue';
|
|
|
+import UploadMedia from './uploadMedia.vue';
|
|
|
+import EditMedia from './editMedia.vue';
|
|
|
+
|
|
|
+// 分组列表数据
|
|
|
+const groupList = ref<Array<any>>([]);
|
|
|
+
|
|
|
+// 获取分组列表
|
|
|
+const fetchGroupList = async () => {
|
|
|
+ try {
|
|
|
+ const result = await getGroupList({type: 1});
|
|
|
+ if (result && Array.isArray(result)) {
|
|
|
+ groupList.value = result;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取分组列表失败:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 在组件挂载时获取分组数据
|
|
|
+onMounted(() => {
|
|
|
+ fetchGroupList();
|
|
|
+});
|
|
|
+
|
|
|
+// 格式化文件大小为MB
|
|
|
+const formatFileSize = (size: string | number) => {
|
|
|
+ if (!size && size !== 0) return '-';
|
|
|
+
|
|
|
+ // 将字符串转为数字
|
|
|
+ const sizeNum = typeof size === 'string' ? parseFloat(size) : size;
|
|
|
+
|
|
|
+ // 转换为MB并保留两位小数
|
|
|
+ const sizeMB = (sizeNum / (1024 * 1024)).toFixed(2);
|
|
|
+
|
|
|
+ return `${sizeMB} MB`;
|
|
|
+};
|
|
|
+
|
|
|
+// 格式化状态为中文
|
|
|
+const formatStatus = (status: string | number) => {
|
|
|
+ if (status === undefined || status === null) return '-';
|
|
|
+
|
|
|
+ // 将字符串转为数字
|
|
|
+ const statusNum = typeof status === 'string' ? parseInt(status) : status;
|
|
|
+
|
|
|
+ // 根据状态码返回对应的中文
|
|
|
+ switch (statusNum) {
|
|
|
+ case 1:
|
|
|
+ return '上传成功';
|
|
|
+ case 0:
|
|
|
+ return '上传中';
|
|
|
+ case -1:
|
|
|
+ return '上传失败';
|
|
|
+ default:
|
|
|
+ return '未知状态';
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 格式化日期时间
|
|
|
+const formatDateTime = (dateTimeStr: string) => {
|
|
|
+ if (!dateTimeStr) return '-';
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 创建日期对象
|
|
|
+ const date = new Date(dateTimeStr);
|
|
|
+
|
|
|
+ // 格式化为 YYYY-MM-DD HH:MM:SS
|
|
|
+ const year = date.getFullYear();
|
|
|
+ const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
+ const day = String(date.getDate()).padStart(2, '0');
|
|
|
+ const hours = String(date.getHours()).padStart(2, '0');
|
|
|
+ const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
|
+ const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
|
+
|
|
|
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('日期格式化错误:', error);
|
|
|
+ return dateTimeStr; // 如果格式化失败,返回原始字符串
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const { state, queryReset, refresh, changPageCurrent, changPageSize, add, del } = usePagging({
|
|
|
+ get: getMediaPagging,
|
|
|
+ add: uploadNewMedia,
|
|
|
+ del: deleteMediaItem,
|
|
|
+ paramsTemlate: {
|
|
|
+ name: "",
|
|
|
+ fileType: "",
|
|
|
+ dictId: "",
|
|
|
+ },
|
|
|
+ mapper: {
|
|
|
+ delMsg: "确定要删除该媒体吗?"
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 上传媒体弹窗
|
|
|
+const uploadVisible = ref(false);
|
|
|
+
|
|
|
+// 上传媒体处理函数
|
|
|
+const uploadMediaHandler = async () => {
|
|
|
+ uploadVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 编辑媒体弹窗
|
|
|
+const editVisible = ref(false);
|
|
|
+const currentMedia = ref({});
|
|
|
+
|
|
|
+// 编辑媒体处理函数
|
|
|
+const editMediaHandler = async (row: Media) => {
|
|
|
+ currentMedia.value = row;
|
|
|
+ editVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 通用下载处理函数
|
|
|
+const handleDownload = async (row: Media, type: 'media' | 'hash') => {
|
|
|
+ try {
|
|
|
+ let url = '';
|
|
|
+ let fileName = '';
|
|
|
+
|
|
|
+ // 根据类型确定URL和文件名
|
|
|
+ if (type === 'media') {
|
|
|
+ if (row.downUrl) {
|
|
|
+ url = row.downUrl;
|
|
|
+ } else {
|
|
|
+ // 通过接口获取URL
|
|
|
+ url = await downloadMedia(row.id);
|
|
|
+ if (!url) {
|
|
|
+ ElMessage.error('获取下载链接失败');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fileName = `${row.fileName}.${row.fileFormat}` || url.split('/').pop() || `file_${row.id}`;
|
|
|
+ } else {
|
|
|
+ if (row.hashUrl) {
|
|
|
+ url = row.hashUrl;
|
|
|
+ } else {
|
|
|
+ // 通过接口获取URL
|
|
|
+ url = await downloadHash(row.id);
|
|
|
+ if (!url) {
|
|
|
+ ElMessage.error('获取Hash下载链接失败');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fileName = `${row.fileName || 'file'}.txt`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用fetch获取文件内容
|
|
|
+ const response = await fetch(url);
|
|
|
+ const blob = await response.blob();
|
|
|
+
|
|
|
+ // 创建Blob URL
|
|
|
+ const blobUrl = window.URL.createObjectURL(blob);
|
|
|
+
|
|
|
+ // 创建下载链接
|
|
|
+ const link = document.createElement('a');
|
|
|
+ link.href = blobUrl;
|
|
|
+ link.setAttribute('download', fileName);
|
|
|
+ document.body.appendChild(link);
|
|
|
+ link.click();
|
|
|
+
|
|
|
+ // 清理
|
|
|
+ document.body.removeChild(link);
|
|
|
+ window.URL.revokeObjectURL(blobUrl);
|
|
|
+
|
|
|
+ ElMessage.success(type === 'media' ? '下载成功' : 'Hash下载成功');
|
|
|
+ } catch (error) {
|
|
|
+ console.error(type === 'media' ? '下载失败:' : 'Hash下载失败:', error);
|
|
|
+ ElMessage.error(type === 'media' ? '下载失败' : 'Hash下载失败');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 下载媒体处理函数
|
|
|
+const downloadMediaHandler = async (row: Media) => {
|
|
|
+ await handleDownload(row, 'media');
|
|
|
+};
|
|
|
+
|
|
|
+// 下载hash处理函数
|
|
|
+const downloadHashHandler = async (row: Media) => {
|
|
|
+ await handleDownload(row, 'hash');
|
|
|
+};
|
|
|
+
|
|
|
+// 分组管理弹窗
|
|
|
+const groupManageVisible = ref(false);
|
|
|
+
|
|
|
+// 打开分组管理弹窗
|
|
|
+const groupManage = () => {
|
|
|
+ groupManageVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 刷新列表
|
|
|
+const refreshList = () => {
|
|
|
+ refresh();
|
|
|
+ fetchGroupList(); // 刷新分组列表
|
|
|
+};
|
|
|
+
|
|
|
+// 跳转
|
|
|
+const floadileUrl = (row: any) => {
|
|
|
+ if (row.fileType == 3) {
|
|
|
+ let url = `/code/index.html?title=${row.fileName}&type=${row.fileFormat}&fileUrl=${row.fileUrl}#/sign-model`
|
|
|
+ return window.open(url);
|
|
|
+ } else {
|
|
|
+ return window.open(row.fileUrl);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 确认删除
|
|
|
+const confirmDelete = async (row: Media) => {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm('确定删除吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ });
|
|
|
+
|
|
|
+ // 用户点击确认,执行删除操作
|
|
|
+ try {
|
|
|
+ await deleteMediaItem(row.id);
|
|
|
+ ElMessage.success('删除成功');
|
|
|
+ refresh(); // 刷新列表
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除失败:', error);
|
|
|
+ ElMessage.error('删除失败');
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ // 用户点击取消,不执行任何操作
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.mediaLibrary {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+</style>
|