index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <template>
  2. <com-head :options="[{ name: '媒体库', value: '2' }]">
  3. <el-form label-width="97px" inline>
  4. <el-form-item label="名称:">
  5. <el-input v-model="state.query.name" placeholder="请输入"></el-input>
  6. </el-form-item>
  7. <el-form-item label="类型:">
  8. <el-select v-model="state.query.fileType" clearable placeholder="请选择">
  9. <el-option label="图片" value="0"></el-option>
  10. <el-option label="视频" value="1"></el-option>
  11. <el-option label="音频" value="2"></el-option>
  12. <el-option label="模型" value="3"></el-option>
  13. <el-option label="其他" value="4"></el-option>
  14. </el-select>
  15. </el-form-item>
  16. <el-form-item label="分组:">
  17. <el-select v-model="state.query.dictId" clearable placeholder="请选择">
  18. <el-option v-for="group in groupList" :key="group.id" :label="group.dictName" :value="group.id"></el-option>
  19. </el-select>
  20. </el-form-item>
  21. <el-form-item class="searh-btns" style="grid-area: 1 / 4 / 2 / 4">
  22. <el-button type="primary" @click="refresh">查询</el-button>
  23. <el-button type="primary" plain @click="queryReset">重置</el-button>
  24. </el-form-item>
  25. </el-form>
  26. </com-head>
  27. <div class="body-layer">
  28. <div class="body-head">
  29. <h3 style="visibility: hidden">媒体列表</h3>
  30. <div class="table-ctrl-right">
  31. <el-button type="primary" @click="groupManage" v-pdpath="'group'">分组管理</el-button>
  32. <el-button type="primary" @click="uploadMediaHandler" v-pdpath="'upload'"> 上传 </el-button>
  33. </div>
  34. </div>
  35. <el-table
  36. ref="multipleTable"
  37. :data="state.table.rows"
  38. size="large"
  39. style="width: 100%"
  40. >
  41. <!-- <el-table-column label="序号" width="70" v-slot:default="{ $index }">
  42. <div style="text-align: center">
  43. {{ state.pag.size * (state.pag.currentPage - 1) + $index + 1 }}
  44. </div>
  45. </el-table-column> -->
  46. <el-table-column label="名称">
  47. <template #default="{ row }">
  48. <p class="oper-span tip clickable" @click="floadileUrl(row)">{{ row.fileName }}</p>
  49. </template>
  50. </el-table-column>
  51. <el-table-column label="文件类型" prop="fileTypeStr"></el-table-column>
  52. <el-table-column label="文件格式" prop="fileFormat"></el-table-column>
  53. <el-table-column label="大小" prop="fileSize">
  54. <template #default="{ row }">
  55. {{ formatFileSize(row.fileSize) }}
  56. </template>
  57. </el-table-column>
  58. <el-table-column label="分组" prop="dictName"></el-table-column>
  59. <el-table-column label="状态" prop="status">
  60. <template #default="{ row }">
  61. {{ formatStatus(row.status) }}
  62. </template>
  63. </el-table-column>
  64. <el-table-column label="上传时间" width="200px" prop="updateTime">
  65. <template #default="{ row }">
  66. {{ formatDateTime(row.updateTime) }}
  67. </template>
  68. </el-table-column>
  69. <el-table-column label="操作" width="200px" v-slot:default="{ row }">
  70. <span class="oper-span" @click="downloadMediaHandler(row)" v-pdpath="'view'">下载</span>
  71. <span class="oper-span" @click="downloadHashHandler(row)" v-pdpath="'view'">hash</span>
  72. <span class="oper-span" v-if="row.useType != 'animation'" @click="editMediaHandler(row)" v-pdpath="'view'">编辑</span>
  73. <span class="oper-span delBtn" v-if="row.useType != 'animation'" @click="confirmDelete(row)" v-pdpath="'view'"> 删除 </span>
  74. </el-table-column>
  75. </el-table>
  76. <com-pagination
  77. @size-change="changPageSize"
  78. @current-change="changPageCurrent"
  79. :current-page="state.pag.currentPage"
  80. :page-size="state.pag.size"
  81. :total="state.pag.total"
  82. />
  83. </div>
  84. <GroupManage
  85. v-model:visible="groupManageVisible"
  86. @refresh="refreshList"
  87. />
  88. <UploadMedia
  89. v-model:visible="uploadVisible"
  90. :groupList="groupList"
  91. @refresh="refreshList"
  92. />
  93. <EditMedia
  94. v-model:visible="editVisible"
  95. :groupList="groupList"
  96. :mediaData="currentMedia"
  97. @refresh="refreshList"
  98. />
  99. </template>
  100. <script setup lang="ts">
  101. import comSelect from "@/components/company-select/index.vue";
  102. import comPagination from "@/components/pagination/index.vue";
  103. import comHead from "@/components/head/index.vue";
  104. import { usePagging } from "@/hook/pagging";
  105. import { confirm } from "@/helper/message";
  106. import { ElMessage, ElMessageBox } from "element-plus";
  107. import { getMediaPagging, uploadNewMedia, deleteMediaItem, Media, getGroupList, downloadMedia, downloadHash } from "@/store/mediaLibrary";
  108. import { ref, onMounted, watch } from "vue";
  109. import GroupManage from './groupManage.vue';
  110. import UploadMedia from './uploadMedia.vue';
  111. import EditMedia from './editMedia.vue';
  112. // 分组列表数据
  113. const groupList = ref<Array<any>>([]);
  114. // 获取分组列表
  115. const fetchGroupList = async () => {
  116. try {
  117. const result = await getGroupList({type: 1});
  118. if (result && Array.isArray(result)) {
  119. groupList.value = result;
  120. }
  121. } catch (error) {
  122. console.error('获取分组列表失败:', error);
  123. }
  124. };
  125. // 在组件挂载时获取分组数据
  126. onMounted(() => {
  127. fetchGroupList();
  128. });
  129. // 格式化文件大小为MB
  130. const formatFileSize = (size: string | number) => {
  131. if (!size && size !== 0) return '-';
  132. // 将字符串转为数字
  133. const sizeNum = typeof size === 'string' ? parseFloat(size) : size;
  134. // 转换为MB并保留两位小数
  135. const sizeMB = (sizeNum / (1024 * 1024)).toFixed(2);
  136. return `${sizeMB} MB`;
  137. };
  138. // 格式化状态为中文
  139. const formatStatus = (status: string | number) => {
  140. if (status === undefined || status === null) return '-';
  141. // 将字符串转为数字
  142. const statusNum = typeof status === 'string' ? parseInt(status) : status;
  143. // 根据状态码返回对应的中文
  144. switch (statusNum) {
  145. case 1:
  146. return '上传成功';
  147. case 0:
  148. return '上传中';
  149. case -1:
  150. return '上传失败';
  151. default:
  152. return '未知状态';
  153. }
  154. };
  155. // 格式化日期时间
  156. const formatDateTime = (dateTimeStr: string) => {
  157. if (!dateTimeStr) return '-';
  158. try {
  159. // 创建日期对象
  160. const date = new Date(dateTimeStr);
  161. // 格式化为 YYYY-MM-DD HH:MM:SS
  162. const year = date.getFullYear();
  163. const month = String(date.getMonth() + 1).padStart(2, '0');
  164. const day = String(date.getDate()).padStart(2, '0');
  165. const hours = String(date.getHours()).padStart(2, '0');
  166. const minutes = String(date.getMinutes()).padStart(2, '0');
  167. const seconds = String(date.getSeconds()).padStart(2, '0');
  168. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  169. } catch (error) {
  170. console.error('日期格式化错误:', error);
  171. return dateTimeStr; // 如果格式化失败,返回原始字符串
  172. }
  173. };
  174. const { state, queryReset, refresh, changPageCurrent, changPageSize, add, del } = usePagging({
  175. get: getMediaPagging,
  176. add: uploadNewMedia,
  177. del: deleteMediaItem,
  178. paramsTemlate: {
  179. name: "",
  180. fileType: "",
  181. dictId: "",
  182. },
  183. mapper: {
  184. delMsg: "确定要删除该媒体吗?"
  185. }
  186. });
  187. // 上传媒体弹窗
  188. const uploadVisible = ref(false);
  189. // 上传媒体处理函数
  190. const uploadMediaHandler = async () => {
  191. uploadVisible.value = true;
  192. };
  193. // 编辑媒体弹窗
  194. const editVisible = ref(false);
  195. const currentMedia = ref({});
  196. // 编辑媒体处理函数
  197. const editMediaHandler = async (row: Media) => {
  198. currentMedia.value = row;
  199. editVisible.value = true;
  200. };
  201. // 通用下载处理函数
  202. const handleDownload = async (row: Media, type: 'media' | 'hash') => {
  203. try {
  204. let url = '';
  205. let fileName = '';
  206. // 根据类型确定URL和文件名
  207. if (type === 'media') {
  208. if (row.downUrl) {
  209. url = row.downUrl;
  210. } else {
  211. // 通过接口获取URL
  212. url = await downloadMedia(row.id);
  213. if (!url) {
  214. ElMessage.error('获取下载链接失败');
  215. return;
  216. }
  217. }
  218. fileName = `${row.fileName}.${row.fileFormat}` || url.split('/').pop() || `file_${row.id}`;
  219. } else {
  220. if (row.hashUrl) {
  221. url = row.hashUrl;
  222. } else {
  223. // 通过接口获取URL
  224. url = await downloadHash(row.id);
  225. if (!url) {
  226. ElMessage.error('获取Hash下载链接失败');
  227. return;
  228. }
  229. }
  230. fileName = `${row.fileName || 'file'}.txt`;
  231. }
  232. // 使用fetch获取文件内容
  233. const response = await fetch(url);
  234. const blob = await response.blob();
  235. // 创建Blob URL
  236. const blobUrl = window.URL.createObjectURL(blob);
  237. // 创建下载链接
  238. const link = document.createElement('a');
  239. link.href = blobUrl;
  240. link.setAttribute('download', fileName);
  241. document.body.appendChild(link);
  242. link.click();
  243. // 清理
  244. document.body.removeChild(link);
  245. window.URL.revokeObjectURL(blobUrl);
  246. ElMessage.success(type === 'media' ? '下载成功' : 'Hash下载成功');
  247. } catch (error) {
  248. console.error(type === 'media' ? '下载失败:' : 'Hash下载失败:', error);
  249. ElMessage.error(type === 'media' ? '下载失败' : 'Hash下载失败');
  250. }
  251. };
  252. // 下载媒体处理函数
  253. const downloadMediaHandler = async (row: Media) => {
  254. await handleDownload(row, 'media');
  255. };
  256. // 下载hash处理函数
  257. const downloadHashHandler = async (row: Media) => {
  258. await handleDownload(row, 'hash');
  259. };
  260. // 分组管理弹窗
  261. const groupManageVisible = ref(false);
  262. // 打开分组管理弹窗
  263. const groupManage = () => {
  264. groupManageVisible.value = true;
  265. };
  266. // 刷新列表
  267. const refreshList = () => {
  268. refresh();
  269. fetchGroupList(); // 刷新分组列表
  270. };
  271. // 跳转
  272. const floadileUrl = (row: any) => {
  273. if (row.fileType == 3) {
  274. let url = `/code/index.html?title=${row.fileName}&type=${row.fileFormat}&fileUrl=${row.fileUrl}#/sign-model`
  275. return window.open(url);
  276. } else {
  277. return window.open(row.fileUrl);
  278. }
  279. }
  280. // 确认删除
  281. const confirmDelete = async (row: Media) => {
  282. try {
  283. await ElMessageBox.confirm('确定删除吗?', '提示', {
  284. confirmButtonText: '确定',
  285. cancelButtonText: '取消',
  286. type: 'warning'
  287. });
  288. // 用户点击确认,执行删除操作
  289. try {
  290. await deleteMediaItem(row.id);
  291. ElMessage.success('删除成功');
  292. refresh(); // 刷新列表
  293. } catch (error) {
  294. console.error('删除失败:', error);
  295. ElMessage.error('删除失败');
  296. }
  297. } catch {
  298. // 用户点击取消,不执行任何操作
  299. }
  300. };
  301. </script>
  302. <style lang="scss" scoped>
  303. .mediaLibrary {
  304. width: 100%;
  305. height: 100%;
  306. }
  307. </style>