zhangsq 8 сар өмнө
parent
commit
0cf6d3567a

+ 2 - 2
.env.development

@@ -5,9 +5,9 @@ NODE_ENV = development
 VITE_TITLE = Snowy
 
 # 接口地址
-# VITE_API_BASEURL = http://192.168.31.81:9003
+VITE_API_BASEURL = http://192.168.31.81:9003
 VITE_FILEURL = http://192.168.1.245:10005/education/
-VITE_API_BASEURL = http://192.168.31.14:9003
+# VITE_API_BASEURL = http://192.168.31.14:9003
 # VITE_API_BASEURL = http://192.168.31.6:9003
 
 # 本地端口

+ 10 - 2
src/api/resourceAudit.js

@@ -51,9 +51,9 @@ export default {
 	recentlyRecord(data = {}) {
 		return request('disk/courseauditrecord/recentlyRecord', data, 'get')
 	},
-	//资源上传删除
+	//回收站资源彻底删除
 	deletefile(data = {}) {
-		return request('resourceFile/deletefile', data, 'post')
+		return request('disk/courseauditrecord/delete', data, 'post')
 	},
 	//文件下载资源上传删除
 	downloadfile(data = {}) {
@@ -62,5 +62,13 @@ export default {
 	//文件上传成功转换格式
 	fileFormatConversion(data = {}) {
 		return request('transcodingResource/file', data, 'post')
+	},
+	//资源类型下拉
+	resourceTypeTree(data = {}) {
+		return request('disk/type/tree', data, 'get')
+	},
+	//资源格式下拉
+	fileformat(data = {}) {
+		return request('disk/fileformat/page', data, 'get')
 	}
 }

+ 179 - 51
src/views/myResources/UploadModal.vue

@@ -1,9 +1,4 @@
 <template>
-	<div class="upload-area" @click="handleUpload">
-		<a-icon type="cloud-upload" style="font-size: 60px; color: #3ca9f5" />
-		<p>点击上传</p>
-		<p>按住Ctrl可同时多选,支持上传PPT/word/excel/pdf/mp4/zip/rar,单个文件不能超过2G</p>
-	</div>
 	<!-- 上传组件 -->
 	<uploader
 		class="uploader-app"
@@ -15,6 +10,7 @@
 		@file-success="handleFileSuccess"
 		@file-error="handleFileError"
 		@dragleave="hideUploadMask"
+		@file-progress="handleFileProgress"
 	>
 		<uploader-unsupport></uploader-unsupport>
 		<!-- 选择按钮 在这里隐藏 -->
@@ -22,15 +18,10 @@
 		<uploader-btn class="select-file-btn" :attrs="attrs" :directory="true" ref="uploadDirBtn"> 选择目录 </uploader-btn>
 
 		<!-- 拖拽上传 -->
-		<uploader-drop class="drop-box" id="dropBox" @paste="handlePaste" v-show="dropBoxShow">
-			<div class="paste-img-wrapper" v-show="pasteImg.src">
-				<div class="paste-name">{{ pasteImg.name }}</div>
-				<img class="paste-img" :src="pasteImg.src" :alt="pasteImg.name" v-if="pasteImg.src" />
-			</div>
-			<span class="text" v-show="!pasteImg.src"> 截图粘贴或将文件拖拽至此区域上传 </span>
+		<uploader-drop class="drop-box" id="dropBox" @paste="handlePaste" @click="handleUpload">
+			<span class="text"> 点击上传或者截图粘贴或将文件拖拽至此区域上传 </span>
+			<p class="text">按住Ctrl可同时多选,支持上传PPT/word/excel/pdf/mp4/zip/rar,单个文件不能超过2G</p>
 			<UploadOutlined class="upload-icon" v-show="pasteImg.src" @click="handleUploadPasteImg" />
-			<DeleteOutlined class="delete-icon" @click="handleDeletePasteImg" />
-			<CloseCircleOutlined class="close-icon" @click="dropBoxShow = false" />
 		</uploader-drop>
 
 		<!-- 上传列表 -->
@@ -49,10 +40,11 @@
 								v-for="file in props.fileList"
 								:key="file.id"
 								class="file-item"
-								:class="{ 'custom-status-item': file.statusStr !== '' }"
-							>
+								:class="{ 'custom-status-item': file.statusStr !== '' }">
 								<uploader-file ref="fileItem" :file="file" :list="true" />
 								<span class="custom-status">{{ file.statusStr }}</span>
+								<!-- 添加剩余时间显示 -->
+								<!-- <span class="remaining-time" v-if="file.remainingTime"> 剩余: {{ file.remainingTime }} </span> -->
 							</li>
 							<div class="no-file" v-if="!props.fileList.length"><FileExclamationOutlined /> 暂无待上传文件</div>
 						</ul>
@@ -223,12 +215,21 @@
 	}
 
 	const handleFilesAdded = (filesSource) => {
+		console.log('handleFilesAdded', filesSource)
 		const filesTotalSize = filesSource
-			.map((item) => item.size)
+			.map((item) => {
+				console.log(item, 'itemitemitemitem')
+				// 为每个文件添加上传速度跟踪属性
+				item.speed = 0
+				item.lastLoaded = item.loaded || 0
+				item.lastTime = Date.now()
+				item.remainingTime = '计算中...'
+				return item
+			})
 			.reduce((pre, next) => {
-				return pre + next
+				return pre + next.size
 			}, 0)
-
+		console.log('handleFilesAdded', filesSource)
 		if (remainderStorageValue.value < filesTotalSize) {
 			// 批量选择的文件超出剩余存储空间
 			message.warning(`剩余存储空间不足,请重新选择${filesSource.length > 1 ? '批量' : ''}文件`)
@@ -246,6 +247,85 @@
 		}
 	}
 
+	const handleFileProgress = (file) => {
+		const now = Date.now()
+		const duration = (now - file.lastTime) / 1000 // 秒
+		const loadedDiff = file.lastTime - file.lastLoaded
+		console.log('duration', now, file.lastTime, file.lastLoaded, duration, loadedDiff)
+		if (duration > 0) {
+			console.log('进来了', file)
+			// 计算当前上传速度 (bytes/sec)
+			file.speed = loadedDiff / duration
+
+			// 计算剩余时间
+			if (file.speed > 0) {
+				const remainingBytes = file.size - file.loaded
+				const remainingSeconds = remainingBytes / file.speed
+				console.log('进来了计算剩余时间', remainingBytes, remainingSeconds)
+				// 格式化剩余时间显示
+				if (remainingSeconds < 60) {
+					console.log('进来了', 60)
+					file.remainingTime = `${Math.round(remainingSeconds)}秒`
+				} else if (remainingSeconds < 3600) {
+					console.log('进来了', 600)
+					file.remainingTime = `${Math.round(remainingSeconds / 60)}分钟`
+				} else {
+					console.log('进来了', 3600)
+					file.remainingTime = `${Math.round(remainingSeconds / 3600)}小时`
+				}
+			}
+		}
+
+		// 更新最后记录的时间和已上传量
+		file.lastLoaded = file.loaded
+		file.lastTime = now
+	}
+	// const handleFileProgress = (file) => {
+	// 	console.log(file, '进来了filefilefile')
+	// 	const now = Date.now()
+
+	// 	// 确保文件对象有必要的属性
+	// 	if (!file.lastTime) file.lastTime = now
+	// 	if (file.lastLoaded === undefined) file.lastLoaded = 0
+
+	// 	const duration = (now - file.lastTime) / 1000 // 转换为秒
+	// 	console.log(duration, '进来了duration')
+	// 	// 只有当时间间隔足够大时才计算速度(至少0.1秒)
+	// 	if (duration > 0.1) {
+	// 		console.log(duration, '进来了duration')
+	// 		const loadedDiff = file.loaded - file.lastLoaded
+
+	// 		// 只有当有新的数据上传时才计算
+	// 		if (loadedDiff > 0) {
+	// 			console.log(loadedDiff, '进来了loadedDiff')
+
+	// 			file.speed = loadedDiff / duration
+	// 			// 计算剩余时间(确保speed是有效数字)
+	// 			if (file.speed > 0 && !isNaN(file.speed)) {
+	// 				const remainingBytes = file.size - file.loaded
+	// 				const remainingSeconds = remainingBytes / file.speed
+	// 				// 格式化剩余时间
+	// 				if (remainingSeconds < 60) {
+	// 					file.remainingTime = `${Math.round(remainingSeconds)}秒`
+	// 				} else if (remainingSeconds < 3600) {
+	// 					const mins = Math.floor(remainingSeconds / 60)
+	// 					const secs = Math.round(remainingSeconds % 60)
+	// 					file.remainingTime = `${mins}分${secs}秒`
+	// 				} else {
+	// 					const hours = Math.floor(remainingSeconds / 3600)
+	// 					const mins = Math.round((remainingSeconds % 3600) / 60)
+	// 					file.remainingTime = `${hours}小时${mins}分`
+	// 				}
+	// 			} else {
+	// 				file.remainingTime = '计算中...'
+	// 			}
+
+	// 			// 更新最后记录的时间和已上传量
+	// 			file.lastLoaded = file.loaded
+	// 			file.lastTime = now
+	// 		}
+	// 	}
+	// }
 	const handleFileSuccess = (rootFile, file, response) => {
 		if (response === '') {
 			uploadStatus.value[file.id] = '上传失败'
@@ -334,7 +414,7 @@
 		file.resume()
 		file.statusStr = ''
 		// 文件开始上传时,禁止关闭弹窗
-		canClose.value = false
+		// canClose.value = false
 	}
 
 	const handleCancel = () => {
@@ -391,13 +471,16 @@
 
 <style lang="less" scoped>
 	@import '@/style/myResource/varibles.less';
+
 	.upload-btn-wrapper {
 		display: flex;
 		align-items: center;
 	}
+
 	.uploader-file {
 		width: 560px;
 	}
+
 	.select-file-btn {
 		display: none;
 	}
@@ -416,7 +499,7 @@
 		margin-top: 10px;
 
 		.text {
-			font-size: 16px;
+			font-size: 14px;
 			color: #999;
 		}
 
@@ -466,9 +549,11 @@
 			color: #999;
 		}
 	}
+
 	.uploader-app {
 		width: 560px;
 	}
+
 	.file-panel {
 		width: 100%;
 		margin-top: 15px;
@@ -503,6 +588,7 @@
 			background-color: #fff;
 			font-size: 12px;
 			list-style: none;
+
 			&::-webkit-scrollbar {
 				width: 6px;
 			}
@@ -515,51 +601,80 @@
 			&::-webkit-scrollbar-track {
 				background: @scrollbar-track-color;
 			}
+
 			.file-item {
 				position: relative;
-				padding: 8px 12px;
-				width: 100%;
-				border-bottom: 1px solid #f0f0f0;
-
-				&:last-child {
-					border-bottom: none;
+				background-color: #fff;
+
+				:deep(.uploader-file) {
+					height: 40px;
+					line-height: 40px;
+
+					.uploader-file-progress {
+						border: 1px solid @success-color;
+						border-right: none;
+						border-left: none;
+						background: #e1f3d8;
+					}
+
+					.uploader-file-name {
+						width: 44%;
+					}
+
+					.uploader-file-size {
+						width: 16%;
+					}
+
+					.uploader-file-meta {
+						display: none;
+					}
+
+					.uploader-file-status {
+						width: 30%;
+						text-indent: 0;
+					}
+
+					.uploader-file-actions>span {
+						margin-top: 12px;
+					}
 				}
 
-				.uploader-file-info {
-					width: 540px;
-				}
-
-				.uploader-file-name {
-					width: 44%;
-				}
-
-				.uploader-file-size {
-					width: 16%;
-				}
-				.uploader-file-status {
-					width: 30%;
-					text-indent: 0;
+				:deep(.uploader-file[status='success']) {
+					.uploader-file-progress {
+						border: none;
+					}
 				}
+			}
 
-				&.custom-status-item {
-					padding-right: 100px;
+			.file-item.custom-status-item {
+				:deep(.uploader-file-status) {
+					visibility: hidden;
 				}
 
 				.custom-status {
 					position: absolute;
-					right: 12px;
-					top: 50%;
-					transform: translateY(-50%);
-					color: #1878ff;
+					top: 0;
+					right: 10%;
+					width: 24%;
+					height: 40px;
+					line-height: 40px;
 				}
 			}
-
 			.no-file {
-				padding: 20px 0;
-				text-align: center;
-				color: #999;
+				position: absolute;
+				top: 50%;
+				left: 50%;
+				transform: translate(-50%, -50%);
 				font-size: 14px;
 			}
+
+			:deep(.uploader-file-icon) {
+				display: none;
+			}
+
+			:deep(.uploader-file-actions > span) {
+				margin-right: 6px;
+			}
 		}
 	}
 
@@ -574,6 +689,7 @@
 	.collapse-leave-to {
 		max-height: 0;
 	}
+
 	.upload-area {
 		border: 2px dashed #3ca9f5;
 		padding: 40px;
@@ -599,6 +715,7 @@
 	.ant-form-item {
 		margin-bottom: 16px;
 	}
+
 	.public-status-buttons {
 		display: flex;
 	}
@@ -617,15 +734,26 @@
 		color: #fff;
 		border-color: #40a9ff;
 	}
+
 	.upload-area {
 		border: 2px dashed #3ca9f5;
 		padding: 40px;
 		text-align: center;
-		transition: border-color 0.3s; /* 平滑过渡效果 */
+		transition: border-color 0.3s;
+		/* 平滑过渡效果 */
 	}
 
 	.upload-area.drag-over {
 		border-color: #1890ff;
 		background-color: rgba(24, 144, 255, 0.05);
 	}
+
+	.remaining-time {
+		position: absolute;
+		right: 120px;
+		top: 50%;
+		transform: translateY(-50%);
+		color: #666;
+		font-size: 12px;
+	}
 </style>

+ 20 - 8
src/views/myResources/coverUpload/index.vue

@@ -13,18 +13,23 @@
 		<a-upload
 			ref="upload"
 			:before-upload="beforeUploadCover"
-			:file-list="coverFileList"
 			:remove="handleRemoveCover"
 			:headers="headers"
 			:action="action"
 			:on-change="handleChangeCover"
-			list-type="picture-card"
+			@preview="handlePreview"
+			:file-list="coverFileList"
 		>
-			<div v-if="coverFileList.length < 1">
-				<plus-outlined />
-				<div class="ant-upload-text">上传图片</div>
-			</div>
 		</a-upload>
+		<div class="preview-area" v-if="previewImageUrl">
+			<a-image
+				:width="200"
+				:src="previewImageUrl"
+				:preview="{
+					src: previewImageUrl
+				}"
+			/>
+		</div>
 		<!-- 文件上传状态展示 -->
 		<ul v-if="fileList.length">
 			<li v-for="(file, index) in fileList" :key="index" class="file-item">
@@ -40,6 +45,7 @@
 
 <script setup>
 	import { ref, reactive, toRefs } from 'vue'
+	import { Modal } from 'ant-design-vue'
 	import tool from '@/utils/tool'
 	const emit = defineEmits(['handleRemoveCover', 'handleChangeCover'])
 	const headers = ref({
@@ -60,6 +66,7 @@
 	const action = ref('/api/webapp/dev/file/uploadMinioReturnId')
 	// 封面文件列表
 	const coverFileList = ref([])
+	const previewImageUrl = ref('')
 	const fileList = ref([])
 	const coverImageId = ref(null)
 	// 上传封面前的钩子函数
@@ -74,20 +81,19 @@
 		}
 		return isJpgOrPng && isLt500K
 	}
-
 	// 移除封面文件
 	const handleRemoveCover = (file) => {
 		const index = coverFileList.value.indexOf(file)
 		const newFileList = coverFileList.value.slice()
 		newFileList.splice(index, 1)
 		coverFileList.value = newFileList
+		previewImageUrl.value = ''
 		// 如果移除的是当前封面文件,则清空coverImageId
 		if (coverImageId.value === file.id) {
 			coverImageId.value = null
 			emit('handleRemoveCover')
 		}
 	}
-
 	// 封面文件状态改变时的处理函数
 	const handleChangeCover = ({ file, fileList: newFileList }) => {
 		if (file.status === 'done') {
@@ -97,6 +103,12 @@
 			if (fileId) {
 				coverImageId.value = fileId
 				emit('handleChangeCover', fileId)
+				// 生成预览URL
+				if (file.originFileObj) {
+					previewImageUrl.value = URL.createObjectURL(file.originFileObj)
+				} else if (file.url) {
+					previewImageUrl.value = file.url
+				}
 			}
 		}
 		if (file.status === 'error') {

+ 51 - 10
src/views/myResources/myResources.vue

@@ -31,15 +31,23 @@
 				<a-select
 					v-model:value="formState.resourceType"
 					style="width: 150px; margin-left: 8px"
-					:options="courseTypeOptions"
+					:options="resourceTypeOptions"
+					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
 					placeholder="请选择资源类型"
 				/>
-				<a-select v-model:value="formState.suffix" placeholder="请选择资源格式" style="width: 150px; margin-left: 8px">
+				<a-select
+					v-model:value="formState.suffix"
+					style="width: 150px; margin-left: 8px"
+					:options="fileformatOptions"
+					:fieldNames="{ label: 'fileExtendName', value: 'fileExtendName', children: 'children' }"
+					placeholder="请选择资源格式"
+				/>
+				<!-- <a-select v-model:value="formState.suffix" placeholder="请选择资源格式" style="width: 150px; margin-left: 8px">
 					<a-select-option value="mp4">mp4</a-select-option>
 					<a-select-option value="ppt">ppt</a-select-option>
 					<a-select-option value="word">word</a-select-option>
 					<a-select-option value="pdf">pdf</a-select-option>
-				</a-select>
+				</a-select> -->
 				<!-- <a-select
 					v-model:value="formState.suffix"
 					style="width: 200px; margin-left: 8px"
@@ -86,7 +94,7 @@
 				<!-- 状态列 -->
 				<template v-if="column.key === 'verifyStatus'">
 					<span v-if="record.verifyStatus === '0'">
-						<a-badge status="processing" text="处理中" />
+						<a-badge status="processing" text="未发布" />
 					</span>
 					<span v-else-if="record.verifyStatus === 'uploaded'">
 						<a-badge status="success" text="已上传" />
@@ -145,7 +153,7 @@
 										<a href="javascript:;" @click="edit(record)">编辑</a>
 									</a-menu-item>
 									<a-menu-item>
-										<a-popconfirm title="确认删除吗?" @confirm="resourcesDelete(record, 1)">
+										<a-popconfirm title="确认删除吗?" @confirm="resourcesDelete(record)">
 											<a href="javascript:;">删除</a>
 										</a-popconfirm>
 									</a-menu-item>
@@ -431,7 +439,9 @@
 
 	const columnsPublished = [...columnsPending]
 	const columnsRecycle = [...columnsPending]
-	const collegeMajorOptions = ref([])
+	const collegeMajorOptions = ref([]) //院校下拉数据
+	const resourceTypeOptions = ref([]) //资源类型下拉数据
+	const fileformatOptions = ref([]) //资源格式下拉数据
 	const currentColumns = computed(() => {
 		switch (formState.verifyStatus) {
 			case '0':
@@ -516,6 +526,30 @@
 				console.log(err)
 			})
 	}
+	//资源类型下拉查询
+	const getResourceTypeTree = () => {
+		resourceAuditApi
+			.resourceTypeTree()
+			.then((res) => {
+				console.log(res.data, '资源类型下拉')
+				// resourceTypeOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	//资源格式下拉查询
+	const getFileformat = () => {
+		resourceAuditApi
+			.fileformat()
+			.then((res) => {
+				console.log(res.data, '资源类型下拉')
+				fileformatOptions.value = res.data.records
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
 	const getCollegeMajor = (id) => {
 		resourceAuditApi
 			.zyselect({ collegeId: id })
@@ -706,11 +740,12 @@
 	}
 	//资源删除
 	const resourcesDelete = (record) => {
-		const params = {
-			ids: record.id,
-			verifyStatus: 4
-		}
 		if (formState.verifyStatus == 4) {
+			const params = [
+				{
+					id: record.id
+				}
+			]
 			resourceAuditApi
 				.deletefile(params)
 				.then((res) => {
@@ -720,6 +755,10 @@
 					console.error(err)
 				})
 		} else {
+			const params = {
+				ids: record.id,
+				verifyStatus: 4
+			}
 			resourceAuditApi
 				.updateStatus(params)
 				.then((res) => {
@@ -756,6 +795,8 @@
 		// 	formState.verifyStatus = '1'
 		// }
 		getOrgTreeSelector()
+		getFileformat()
+		getResourceTypeTree()
 		getListData()
 	})
 </script>

+ 1 - 1
src/views/myResources/releaseModal.vue

@@ -24,7 +24,7 @@
 			</a-form-item> -->
 
 			<a-form-item label="资源描述" name="courseDesc">
-				<a-textarea v-model:value="uploadForm.resourceDesc" placeholder="请输入课程介绍" :rows="4" />
+				<a-textarea v-model:value="uploadForm.resourceDesc" placeholder="请输入资源描述" :rows="4" />
 			</a-form-item>
 
 			<a-form-item label="上传封面" name="coverImageId">