于添 hai 3 meses
pai
achega
71241a13fc

+ 14 - 0
.env.production22

@@ -0,0 +1,14 @@
+# 生产环境
+NODE_ENV = production
+
+# 标题
+VITE_TITLE = 飞行学院
+
+# 接口地址
+VITE_API_BASEURL = http://22.120.128.109:19003
+VITE_FILEURL = http://22.120.128.108:10005/education/
+# 本地端口
+VITE_PORT = 9000
+
+# 开启设置抽屉
+VITE_SET_DRAWER = false

+ 1 - 0
package.json

@@ -14,6 +14,7 @@
 		"build": "shx rm -rf dist && vite build --mode production && node scripts/build-zip.cjs",
 		"build245": "shx rm -rf dist && vite build --mode production245 && node scripts/build-zip.cjs",
 		"build49": "shx rm -rf dist && vite build --mode production49 && node scripts/build-zip.cjs",
+		"build22": "shx rm -rf dist && vite build --mode production22 && node scripts/build-zip.cjs",
 		"prod": "vite  --mode production && node scripts/build-zip.cjs"
 	},
 	"dependencies": {

+ 8 - 0
src/api/myResource/user.js

@@ -12,3 +12,11 @@ export const checkUserLoginInfo = (p) => {
 export const getWeChatAuthState = (p) => {
 	return request('user/checkWxAuth', p, 'get')
 }
+export const getUserStorage = (p) => {
+
+	return request('storagere/getUserStorage', p, 'get')
+}
+export const getstorage = (p) => {
+
+	return request('resourceFile/getstorage', p, 'get')
+}

+ 36 - 3
src/api/resourceAudit.js

@@ -7,10 +7,16 @@ export default {
 	page(data) {
 		return request('disk/courseauditrecord/page', data, 'get')
 	},
+	stuLinkResourceRecordPage(data) {
+		return request('disk/courseauditrecord/stuLinkResourceRecordPage', data, 'get')
+	},
 	//资源管理添加
 	add(data = {}) {
 		return request('disk/courseauditrecord/add', data, 'post')
 	},
+	stuLinkResourceRecordAdd(data = {}) {
+		return request('disk/courseauditrecord/stuLinkResourceRecordAdd', data, 'post')
+	},
 	//发布
 	release(data = {}) {
 		return request('disk/courseinfo/add', data, 'post')
@@ -23,6 +29,12 @@ export default {
 	edit(data = {}) {
 		return request('disk/courseauditrecord/edit', data, 'post')
 	},
+	stuLinkResourceRecordEdit(data = {}) {
+		return request('disk/courseauditrecord/stuLinkResourceRecordEdit', data, 'post')
+	},
+	editUserFileName(data = {}) {
+		return request('disk/courseauditrecord/editUserFileName', data, 'post')
+	},
 	//编辑
 	detail(data = {}) {
 		return request('disk/courseauditrecord/detail', data, 'get')
@@ -45,7 +57,15 @@ export default {
 	},
 	//获取课程下拉
 	courseAllList(data = {}) {
-		return request('disk/courseinfo/allList', data, 'get')
+		return request('disk/courseopen/queryCourseList', data, 'get')
+	},
+	//获取学期列表
+	semesterDownList(data = {}) {
+		return request('disk/semester/downList', data, 'get')
+	},
+	//获取专业列表
+	majordownList(data = {}) {
+		return request('disk/major/downList', data, 'get')
 	},
 	//查询资源表单最后一次提交信息
 	recentlyRecord(data = {}) {
@@ -77,9 +97,22 @@ export default {
 	},
 	//公开人员查询所有以及下级
 	orgUserTreeSelector(data = {}) {
-		return request('disk/college/orgUserTreeSelector', data, 'get')
+		return request('bus/org/orgUserTreeSelector', data, 'get')
 	},
+	// 学院接口
 	orgList(data = {}) {
-		return request('sys/org/queryList', data, 'get')
+		return request('bus/org/queryList', data, 'get')
+	},
+	//根据学院id和专业id查询班级
+	gradeByZyAndOrgId(data = {}) {
+		return request('disk/grades/queryList', data, 'get')
+	},
+	//资源转换日志
+	resourceConversionLog(data = {}) {
+		return request('disk/userfileconvert/page', data, 'get')
+	},
+	//资源重新转换
+	transcodingResource(data = {}) {
+		return request('transcodingResource/file', data, 'post')
 	}
 }

+ 96 - 0
src/components/MiniYun/index.vue

@@ -0,0 +1,96 @@
+<template>
+	<div style="width: 100px;  height: 100%; display: flex; justify-content: center;  align-items: center;  cursor: pointer ; " >
+		<div class="shake-animation"  v-if="fileForms && fileForms.length > 0" style="padding: 5px" @click="onOpenMiniForm">
+			<span  style="display: block"  >上传任务管理</span>
+		</div>
+
+		<ResourceUpload ref="resourceUploadRef"></ResourceUpload>
+		<MiniForm ref="miniFormRef"></MiniForm>
+	</div>
+
+</template>
+<script setup>
+import ResourceUpload from './resourceUpload.vue'
+import MiniForm from './miniForm.vue'
+import EventBus from "@/utils/EventBus";
+
+const resourceUploadRef = ref(null)
+const miniFormRef = ref(null)
+import { miniyunStore } from '@/store/miniyun'
+import {storeToRefs} from "pinia";
+const myMiniyunStore = miniyunStore()
+// 使用 storeToRefs 保持响应性
+const { getFileForms ,getPauseFlags} = storeToRefs(myMiniyunStore)
+
+// getFileModel 保持了与 store 的响应式连接
+const fileForms = computed({
+	get: () => getFileForms.value
+})
+const pauseFlags = computed({
+	get: () => getPauseFlags.value
+})
+
+const onOpenList = () => {
+	// console.log('打开上传资源')
+	// resourceUploadRef.value.open()
+
+}
+const onOpenMiniForm = () => {
+	miniFormRef.value.open()
+}
+const openMiniYun = (teacherData) => {
+	resourceUploadRef.value.open(teacherData)
+}
+onMounted(() => {
+
+})
+EventBus.off('openMiniYun',openMiniYun)
+EventBus.on('openMiniYun',openMiniYun)
+</script>
+
+
+<style scoped>
+.shake-animation {
+	animation: scaleAndShake 3s infinite;
+}
+
+.shake-animation span {
+	animation: textColorChange 3s infinite;
+}
+
+@keyframes scaleAndShake {
+	0% {
+		transform: scale(1);
+	}
+	30% {
+		transform: scale(0.8);
+	}
+	40%, 50%, 60%, 70%, 80% {
+		transform: scale(0.8) translateX(3px);
+	}
+	45%, 55%, 65%, 75% {
+		transform: scale(0.8) translateX(-3px);
+	}
+	85% {
+		transform: scale(0.8);
+	}
+	100% {
+		transform: scale(1);
+	}
+}
+
+@keyframes textColorChange {
+	0% {
+		color: #333;
+	}
+	30% {
+		color: #409eff;
+	}
+	85% {
+		color: #409eff;
+	}
+	100% {
+		color: #333;
+	}
+}
+</style>

+ 720 - 0
src/components/MiniYun/miniForm.vue

@@ -0,0 +1,720 @@
+<template>
+	<a-modal
+		v-model:visible="uploadModalVisible"
+		width="600px"
+		:footer="null"
+	>
+		<div v-for="(itemi, indexi) in fileForms" :key="itemi.key" >
+			<a-form :model="itemi" :rules="rules" ref="formRef">
+				<div>
+					<span style="color: blue">关键词 :</span>
+					<div v-if="itemi.keywordValue.split(',').length" style="display: inline">
+						<a-tag
+							v-for="(keyword, index) in itemi.keywordValue.split(',')"
+							:key="index"
+
+						>
+							{{ keyword }}
+						</a-tag>
+					</div>
+				</div>
+<!--			<a-form-item-->
+<!--				label="关键词(需添加2-5个关键词)"-->
+<!--				name="keywordValue"-->
+<!--				:label-col="{ span: 10 }"-->
+<!--				:wrapper-col="{ span: 18 }"-->
+<!--			>-->
+<!--			-->
+<!--			</a-form-item>-->
+
+			<div>
+				<span style="color: blue">资源是否公开 :</span>
+				<span style="color: red" v-if="itemi.authType === '0'">公开</span>
+				<span style="color: red" v-if="itemi.authType === '1'">部分人可见</span>
+			</div>
+<!--			<a-form-item label="资源是否公开" style="margin-top: 10px" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">-->
+
+<!--				<div class="public-status-buttons">-->
+<!--					<button v-if="itemi.authType === '0'" :class="['status-button', { active: itemi.authType === '0' }]" @click="setPublicStatus('0')">-->
+<!--						公开-->
+<!--					</button>-->
+<!--					<button v-if="itemi.authType === '1'" :class="['status-button', { active: itemi.authType === '1' }]" @click="setPublicStatus('1')">-->
+<!--						部分人可见-->
+<!--					</button>-->
+<!--				</div>-->
+<!--			</a-form-item>-->
+				<div>
+					<span style="color: blue">是否热门 :</span>
+					<span style="color: red" v-if="itemi.isHot == '0'">否</span>
+					<span style="color: red" v-if="itemi.isHot == '1'">是</span>
+				</div>
+				<div>
+					<span style="color: blue">是否推荐 :</span>
+					<span style="color: red" v-if="itemi.isRecommend == '0'">否</span>
+					<span style="color: red" v-if="itemi.isRecommend == '1'">是</span>
+				</div>
+
+
+				<div v-for="(itemii, indexii) in itemi.uploadFileList" :key="indexii">
+					<div style="padding: 2px">
+						<div style="display: flex; width: 100%; align-items: center; justify-content: space-between">
+							<div>
+								<span>{{ itemii.name.length > 20 ? itemii.name.slice(0, 20) + '...' + itemii.fileSuffix : itemii.name }}</span>
+							</div>
+							<div>
+								<span v-if="itemii.time != ''" style="display: block; color: blue">{{ itemii.time }}</span>
+							</div>
+							<div>
+								<div>
+									<!--							<span-->
+									<!--								v-if="item.percents == 0"-->
+									<!--								style="color: red; cursor: pointer; margin-left: 10px"-->
+									<!--							>读取中</span-->
+									<!--							>-->
+									<span
+										v-if="itemii.percents == 0"
+										style="color: red; cursor: pointer; margin-left: 10px"
+										@click="handlerRemoveItem(indexi,indexii)"
+									>删除</span
+									>
+																<span
+																	v-if="
+																		itemii.percents >= 0 &&
+																		itemii.percents < 100 &&
+																		(pauseFlags[itemii.md5] == false || pauseFlags[itemii.md5] == undefined)
+																	"
+																	style="color: blue; cursor: pointer; margin-left: 10px"
+																	@click="pauseUpload(itemii.md5)"
+																	>暂停</span>
+																<span
+																	v-if="itemii.percents >= 0 && itemii.percents < 100 && pauseFlags[itemii.md5] == true"
+																	style="color: green; cursor: pointer; margin-left: 10px"
+																	@click="resumeUpload(itemii.md5)"
+																	>恢复</span
+																>
+								</div>
+							</div>
+						</div>
+						<a-progress :percent="itemii.percents" />
+					</div>
+				</div>
+
+
+<!--			<a-form-item label="是否热门" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">-->
+<!--				<a-radio-group v-model:value="formState.isHot">-->
+<!--					<a-radio  v-if='itemi.isHot==0' :value="0">否</a-radio>-->
+<!--					<a-radio  v-if='itemi.isHot==1' :value="1">是</a-radio>-->
+<!--				</a-radio-group>-->
+<!--			</a-form-item>-->
+<!--			<a-form-item label="是否推荐" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">-->
+<!--				<a-radio-group v-model:value="formState.isRecommend">-->
+<!--					<a-radio  v-if='formState.isRecommend==0' :value="0">否</a-radio>-->
+<!--					<a-radio  v-if='formState.isRecommend==1' :value="1">是</a-radio>-->
+<!--				</a-radio-group>-->
+<!--			</a-form-item>-->
+
+
+			<!-- 选择成员 -->
+<!--			<userSelection-->
+<!--				:visible="userReleaseVisible"-->
+<!--				:userRelateIds="userRelateIdss"-->
+<!--				@close="userReleaseVisible = false"-->
+<!--				@confirm="confirmUser"-->
+<!--			></userSelection>-->
+		</a-form>
+		</div>
+	</a-modal>
+</template>
+
+<script setup>
+	import { ref, reactive, onMounted } from 'vue'
+	import { Modal, Upload, Form } from 'ant-design-vue'
+	import resourceAuditApi from '@/api/resourceAudit.js'
+	import userSelection from './userSelection.vue'
+	import UpLoadBreakPoint from './upLoadBreakPoint.vue'
+	import tool from '@/utils/tool'
+	import sysConfig from "@/config";
+	const { proxy } = getCurrentInstance()
+
+	const props = defineProps({
+		isState: {
+			type: Number,
+			required: true,
+			default: 0
+		},
+		isVerifyStatus: {
+			type: Number,
+			required: true,
+			default: 0
+		},
+		resourcesId: {
+			type: Number,
+			required: true,
+			default: null
+		}
+	})
+	const emit = defineEmits(['close', 'getList'])
+
+	const headers = ref({
+		token: tool.data.get('TOKEN')
+	})
+	//课程类型
+	const courseTypeOptions = tool.dictList('COURSE_TYPE')
+	const action = ref(sysConfig.API_URL+'/api/webapp/dev/file/uploadMinioReturnId')
+	const formState = reactive({
+		userfileIds: null, //资源文件id
+		coverImage: undefined, //封面id
+		// courseId: null, //课程
+		// majorType: null, //专业
+		resourceType: null, //资源类型一级
+		resourceTwoType: null, //资源类型二级
+		resourceThreeType: null, //资源类型二级
+		collegeId: null, //院校一级id
+		collegeTwoId: null, //院校二级id
+		collegeThreeId: null, //院校三级id
+		resourceDesc: null, //资源介绍
+		// majorId: null, //专业id
+		// courseTypeName: [], // 资源类型
+		keywordValue: [], // 添加关键词
+		keyword: [], // 热门关键词
+		authType: '0', // 资源是否公开
+		userRelateIds: null, //资源公开人员id
+		isRecommend: 0, // 资源是否推荐
+		isHot: 0 // 资源是否热门
+	})
+	import { miniyunStore } from '@/store/miniyun'
+	import {storeToRefs} from "pinia";
+	const myMiniyunStore = miniyunStore()
+	// 使用 storeToRefs 保持响应性
+	const { getFileForms ,getPauseFlags} = storeToRefs(myMiniyunStore)
+
+	// getFileModel 保持了与 store 的响应式连接
+	const fileForms = computed({
+		get: () => getFileForms.value
+	})
+	const pauseFlags = computed({
+		get: () => getPauseFlags.value
+	})
+	const upLoadBreakPointRef = ref(null) // 预览回显
+	const coverImagePath = ref() // 预览回显
+	const formRef = ref() // 添加表单引用
+	const collegeMajorOptions = ref([]) //院系
+	const resourceTypeOptions = ref([]) //资源类型下拉数据
+	const HotKeywordsOptions = ref([]) //热门关键词列表
+	const majorIdName = ref([]) //院系回显
+	const resourceName = ref([]) //资源回显
+	const majorOptions = ref([]) //专业
+	const courseOptions = ref([]) //课程
+	const userRelateIdss = ref([])
+	const myHot = ref({})
+	// 上传资源模态框
+	const uploadModalVisible = ref(false)
+	// 用户选择模态框
+	const userReleaseVisible = ref(false)
+
+
+
+	const pauseUpload = (md5) => {
+		myMiniyunStore.pauseUpload(md5)
+		// const item = uploadFileList.value[index]
+		// if (item && item.md5) {
+		// 	pauseFlags.value[item.md5] = true
+		// 	uploadingTasks.value[item.md5] = false
+		// }
+	}
+
+	const resumeUpload = (md5) => {
+		myMiniyunStore.resumeUpload(md5)
+		// const item = uploadFileList.value[index]
+		// if (!item || !item.md5) return
+		//
+		// pauseFlags.value[item.md5] = false
+		//
+		// // 如果当前上传任务小于 2,则开始上传
+		// const activeTasks = Object.keys(uploadingTasks.value).filter((key) => uploadingTasks.value[key])
+		// if (activeTasks.length < 2) {
+		// 	uploadingTasks.value[item.md5] = true
+		// 	// uploadSingleFile(item)
+		// } else {
+		// 	pauseFlags.value[item.md5] = true
+		// }
+	}
+
+	const handlerRemoveItem = (indexi,indexii) => {
+		// const item = uploadFileList.value[index]
+		// if (item && item.md5) {
+		// 	delete pauseFlags.value[item.md5] // 清理暂停标志
+		// }
+
+		myMiniyunStore.handlerRemoveItem(indexi,indexii)
+
+
+		// emit('onSuccess', uploadFileList.value)
+	}
+	// 关闭模态框
+	const handleUploadCancel = () => {
+		emit('close')
+		fileList.value = []
+	}
+	// 新增Set记录已处理ID
+	// const processedIds = ref(new Set())
+	//资源文件上传成功返回
+	const uploadSuccess = async (idsArr) => {
+		formState.userfileIds = idsArr.join(',')
+		// const newIds = idsArr.filter((id) => !processedIds.value.has(id))
+		// if (newIds.length === 0) return
+		// //文件转换
+		// try {
+		// 	const requests = newIds.map((id) => resourceAuditApi.fileFormatConversion({ userFileId: id }))
+		// 	await Promise.all(requests)
+		// 	newIds.forEach((id) => processedIds.value.add(id))
+		// 	newKeyword.value = ''
+		// } catch (err) {
+		// 	console.error('部分请求失败:', err)
+		// }
+	}
+	const onSuccess = (uploadFileList) => {
+		let list = []
+		console.log('formState.userfileIds是数组:', uploadFileList)
+		for (let i = 0; i < uploadFileList.length; i++) {
+			if (uploadFileList[i].userFileId) {
+				list.push(uploadFileList[i].userFileId)
+			}
+		}
+		console.log('formState.userfileIds是:', list)
+		formState.userfileIds = list.join(',')
+	}
+
+	// 自定义校验函数示例
+	const validateKeywords = (rule, value, callback) => {
+		if (value.length < 2) {
+			callback(new Error('至少需要添加2个关键词'))
+		} else if (value.length > 5) {
+			callback(new Error('最多只能添加5个关键词'))
+		} else {
+			callback()
+		}
+	}
+	// 封面文件id
+	const handleChangeCover = (fileId) => {
+		formState.coverImage = fileId
+	}
+
+	// 移除封面文件
+	const handleRemoveCover = () => {
+		formState.coverImage = undefined
+	}
+	const rules = {
+		// courseTypeName: [{ required: true, message: '请选择资源类型', trigger: 'change' }],
+		collegeId: [{ required: true, message: '请选择单位', trigger: 'blur' }],
+		keywordValue: [
+			{ required: true, message: '请选择关键词', trigger: 'blur' },
+			{ validator: validateKeywords, trigger: 'blur' }
+		],
+		// coverImage: [{ required: true, message: '请上传封面', trigger: 'blur' }],
+		resourceType: [{ required: true, message: '请选择资源类型', trigger: 'blur' }],
+		resourceDesc: [
+			{ required: true, message: '请输入资源描述', trigger: 'blur' },
+			{ pattern: /^[\s\S]{10,500}$/, message: '描述长度应在10-500字符之间', trigger: 'blur' }
+		],
+		coverImage: [{ required: true, message: '请上传封面', trigger: 'blur' }],
+	}
+	const newKeyword = ref('') //关键词
+	const handleAddKeyword = (e) => {
+		const newKeywords = newKeyword.value.trim()
+		resourceAuditApi
+			.addHotKeywords({
+				wordName: newKeywords,
+				popular: 0
+			})
+			.then((res) => {
+				console.log(res.data, '添加热门关键词')
+				getHotKeywords()
+				newKeyword.value = ''
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+		// if (newKeywords && !formState.keywordValue.includes(newKeywords)) {
+		// 	formState.keywordValue.push(newKeywords)
+		// }
+	}
+
+	const handleRemoveKeyword = (keywordName, index) => {
+
+		console.log('删除的东西 01 ', ' keywordName ',keywordName,' index ',index)
+		console.log('删除的东西 02 ', ' formState.keyword ',formState.keyword)
+
+
+		console.log(keywordName, index,myHot.value)
+		// // 1. 从显示列表中删除
+		// formState.keywordValue = formState.keywordValue.filter((name) => name !== keywordName)
+		// // console.log('看看点击取消了什么',' formState.keywordValue ',formState.keywordValue)
+		// // formState.keyword
+		// // 2. 更新checkbox的绑定值
+		// formState.keyword = HotKeywordsOptions.value
+			// .filter((option) => formState.keywordValue.includes(option.label))
+			// .map((item) => item.value)
+		// // formState.keyword = formState.keyword.filter((item, i) => i !== index)
+		// // console.log('看看点123123',' formState.keyword ',formState.keyword,' index ',index)
+
+		let myId = myHot.value[keywordName]
+		// console.log('换算出来',formState.keyword,index)
+
+		// for (let i = formState.keyword.length-1; i > 0 ; i--) {
+		// 	// console.log('看看',formState.keyword[i] , i , index)
+		// 	console.log('要删除第几个' ,i, index ,  formState.keyword[i])
+		// 	if( i == index){
+		//
+		// 		// formState.keyword.splice(i,1)
+		// 	}
+		// }
+		// console.log('换算出来  结果',formState.keyword)
+
+		// formState.keyword.filter((item, i) => item != keywordName)
+		// formState.keyword = [{'专业' : 1}]
+		// formState.keyword.filter((item, i) => item != index)
+		// formState.keyword.filter((option) => formState.keywordValue.includes(option.value))
+		// 	.map((item) => item.label)
+		// console.log('换算出来 剩下',formState.keyword)
+		// console.log('还有吗',formState.keywordValue)
+
+	}
+	const getHotKeywords = () => {
+		myHot.value = {}
+		resourceAuditApi
+			.HotKeywords()
+			.then((res) => {
+				console.log(res.data, '获取热门关键词')
+				HotKeywordsOptions.value = res.data.map((it) => {
+					myHot.value[it.wordName] = it.id
+					return {
+						value: it.id,
+						label: it.wordName
+					}
+				})
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	//资源类型下拉查询
+	const getResourceTypeTree = () => {
+		resourceAuditApi
+			.resourceTypeTree()
+			.then((res) => {
+				console.log(res.data, '资源类型下拉')
+				resourceTypeOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	const handleChangeKeyword = (checkedValues) => {
+		formState.keyword = checkedValues
+		console.log('点击checkbox',checkedValues )
+		formState.keywordValue = HotKeywordsOptions.value
+			.filter((option) => checkedValues.includes(option.value))
+			.map((item) => item.label)
+		console.log('点击checkbox  123 ',formState.keywordValue )
+	}
+	const setPublicStatus = (status) => {
+		formState.authType = status
+		if (status === '1') {
+			userReleaseVisible.value = true
+		}
+	}
+	//院系组织查询
+	const getOrgTreeSelector = () => {
+		resourceAuditApi
+			.orgTreeSelector()
+			.then((res) => {
+				console.log(res.data, '获取组织树选择器')
+				collegeMajorOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	//院系组织查询
+	const changeCollegeMajor = (value, selectedOptions) => {
+		console.log('Selected:', value, selectedOptions)
+		if (!value) {
+			majorIdName.value = ''
+			return false
+		}
+		majorIdName.value = selectedOptions.map((it) => it.name).join('/')
+		formState.collegeId = value[0] || null
+		formState.collegeTwoId = value[1] || null
+		formState.collegeThreeId = value[2] || null
+		if (selectedOptions.length) {
+			// 获取选中的最后一级
+			const lastSelected = selectedOptions[selectedOptions.length - 1]
+			// formState.selectedCollegeMajor = {
+			// 	id: lastSelected.id,
+			// 	name: lastSelected.name,
+			// 	fullPath: selectedOptions.map((opt) => opt.name).join(' / ')
+			// }
+			console.log(lastSelected, '最后一级id')
+			getCollegeMajor(lastSelected.id)
+		}
+	}
+	const changeCollegeResource = (value, selectedOptions) => {
+		console.log('Selected:', value, selectedOptions)
+		if (!value) {
+			resourceName.value = ''
+			return false
+		}
+		resourceName.value = selectedOptions.map((it) => it.name).join('/')
+		formState.resourceType = value[0] || undefined
+		formState.resourceTwoType = value[1] || undefined
+		formState.resourceThreeType = value[2] || undefined
+	}
+	const getCollegeMajor = (id) => {
+		resourceAuditApi
+			.zyselect({ collegeId: id })
+			.then((res) => {
+				console.log(res.data, '专业下拉数据')
+				majorOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	const confirmUser = (userIds) => {
+		console.log(userIds, '用户id')
+		userReleaseVisible.value = false
+		formState.userRelateIds = userIds.join(',')
+	}
+	// 确认上传
+	const handleUploadOk = async () => {
+		try {
+			await formRef.value.validate()
+			// console.log('formState.userfileIds是:提交了', formState.userfileIds)
+			// if (!formState.userfileIds) {
+			// 	Modal.error({ content: '请先上传文件!!' })
+			// 	return
+			// }
+			if (upLoadBreakPointRef.value.getUploadFileList().length == 0) {
+				Modal.error({ content: '请先上传文件!!' })
+				return
+			}
+			const formData = {
+				// userfileIds: formState.userfileIds,
+				resourceType: formState.resourceType,
+				resourceTwoType: formState.resourceTwoType,
+				resourceThreeType: formState.resourceThreeType,
+				collegeId: formState.collegeId,
+				collegeTwoId: formState.collegeTwoId,
+				collegeThreeId: formState.collegeThreeId,
+				keywordValue: formState.keywordValue.join(','),
+				keyword: formState.keyword.join(','),
+				resourceDesc: formState.resourceDesc,
+				isHot: formState.isHot,
+				isRecommend: formState.isRecommend,
+				coverImage: formState.coverImage,
+				publicStatus: formState.publicStatus,
+				authType: formState.authType,
+				userRelateIds: formState.authType == 1 ? formState.userRelateIds : null,
+				uploadFileList : upLoadBreakPointRef.value.getUploadFileList()
+			}
+			console.log('上传数据',formData )
+			myMiniyunStore.addFileForms(formData)
+				// resourceAuditApi
+				// 	.add(formData)
+				// 	.then((res) => {
+				// 		emit('getList')
+				// 		Modal.success({ content: '资源上传成功' })
+				// 	})
+				// 	.catch((err) => {
+				// 		Modal.success({ content: '资源上传失败' })
+				// 		console.log(err)
+				// 	})
+		} catch (error) {
+			console.error('错误信息',error)
+			if (error.errorFields) {
+				// 表单验证错误
+				Modal.error({ content: '请检查表单填写是否正确' })
+			} else {
+				// API错误
+				Modal.error({ content: '资源上传失败' })
+				console.error(error)
+			}
+		}
+	}
+	// 获取资源详情
+	const getDetail = () => {
+		resourceAuditApi.detail({ id: props.resourcesId }).then((res) => {
+			console.log(res.data, '资源详情')
+			formState.userfileIds = res.data.fileId
+			formState.userRelateIds = res.data.userRelateIdList?.join(',')
+			userRelateIdss.value = res.data.userRelateIdList
+			formState.resourceType = res.data.resourceType
+			formState.resourceTwoType = res.data.resourceTwoType
+			formState.resourceThreeType = res.data.resourceThreeType
+			formState.collegeId = res.data.collegeId
+			formState.coverImage = res.data.coverImage
+			coverImagePath.value = res.data.coverImagePath
+			formState.collegeTwoId = res.data.collegeTwoId
+			formState.collegeThreeId = res.data.collegeThreeId
+			majorIdName.value = res.data.collegeAllId?.split(',')
+			resourceName.value = res.data.resourceALLTypeName?.split(',')
+			getCollegeMajor(majorIdName.value[majorIdName.value.length - 1])
+			formState.resourceDesc = res.data.resourceDesc
+			// formState.majorId = res.data.majorId
+			formState.keywordValue = res.data.keywordValue?.split(',')
+			formState.keyword = res.data.keyword.split(',').filter(Boolean).map(Number)
+
+			formState.isRecommend = Number(res.data.isRecommend) || 0
+			formState.isHot = Number(res.data.isHot) || 0
+			formState.authType = res.data.authType
+		})
+	}
+	// 获取历史添加表单
+	const getformState = () => {
+		resourceAuditApi.recentlyRecord().then((res) => {
+			console.log(res.data, '历史表单数据')
+			formState.resourceType = res.data.resourceType
+			formState.userRelateIds = res.data.userRelateIdList?.join(',')
+			userRelateIdss.value = res.data.userRelateIdList
+			formState.resourceTwoType = res.data.resourceTwoType
+			formState.resourceThreeType = res.data.resourceThreeType
+			formState.collegeId = res.data.collegeId
+			formState.collegeTwoId = res.data.collegeTwoId
+			formState.collegeThreeId = res.data.collegeThreeId
+			majorIdName.value = res.data.collegeAllId?.split(',')
+			resourceName.value = res.data.resourceAllType?.split(',')
+			getCollegeMajor(majorIdName.value[majorIdName.value.length - 1])
+			formState.resourceDesc = res.data.resourceDesc
+			// formState.majorId = res.data.majorId
+			formState.keywordValue = res.data.keywordValue?.split(',')
+			formState.keyword = res.data.keyword?.split(',').filter(Boolean).map(Number)
+		})
+	}
+	// 上传前的钩子函数
+	const beforeUpload = (file) => {
+		const isLt2G = file.size / 1024 / 1024 / 1024 < 2
+		if (!isLt2G) {
+			Modal.error({ content: '文件大小不能超过 2GB!' })
+		}
+		return isLt2G
+	}
+
+	// 移除文件
+	const handleRemove = (file) => {
+		const index = fileList.value.indexOf(file)
+		const newFileList = fileList.value.slice()
+		newFileList.splice(index, 1)
+		fileList.value = newFileList
+		// 如果移除的是当前封面文件,则清空coverImageId
+		if (formState.userfileIds === file.id) {
+			formState.userfileIds = null
+		}
+	}
+
+	// 文件状态改变时的处理函数
+	const handleChange = ({ file, fileList: newFileList }) => {
+		if (newFileList.length > 1) {
+			fileList.value = [newFileList[0]] // 只保留最新上传的文件
+			Modal.error({ content: '只能上传一个文件!' })
+			return
+		}
+
+		if (file.response?.code == 200) {
+			// 上传成功,获取文件ID
+			const fileId = file.response?.data || file.id
+			console.log('上传成功,获取文件ID', fileId)
+			if (fileId) {
+				formState.userfileIds = fileId
+			}
+		}
+		fileList.value = newFileList
+		if (file.status === 'uploading') {
+			file.percent = Math.floor(file.percent)
+		}
+		if (file.response?.code == 200) {
+			file.percent = 100
+			Modal.success({ content: '文件上传成功' })
+		} else if (file.response?.code == 500) {
+			Modal.error({ content: '文件上传失败' })
+			file.percent = 0
+		}
+	}
+	const open = () =>{
+		uploadModalVisible.value = true
+	console.log('打开看到了什么 1 ',myMiniyunStore)
+	console.log('打开看到了什么 2 ',fileForms.value)
+
+	}
+
+	onMounted(() => {
+		// getOrgTreeSelector()
+		// getHotKeywords()
+		// getResourceTypeTree()
+		// if (props.isState == 1) {
+		// 	getDetail()
+		// } else {
+		// 	getformState()
+		// }
+	})
+
+	defineExpose({
+		open
+	})
+</script>
+<style scoped>
+	.upload-area {
+		border: 2px dashed #3ca9f5;
+		padding: 40px;
+		text-align: center;
+	}
+
+	.upload-area p {
+		margin: 10px 0;
+	}
+
+	.file-item {
+		display: flex;
+		align-items: center;
+		margin: 10px 0;
+	}
+
+	.file-item .ant-progress {
+		flex: 1;
+		margin: 0 10px;
+	}
+
+	/* 新增表单样式 */
+	.ant-form-item {
+		margin-bottom: 16px;
+	}
+	.public-status-buttons {
+		display: flex;
+	}
+
+	.status-button {
+		padding: 5px 10px;
+		/* margin-right: 10px; */
+		border: 1px solid #ccc;
+		/* border-radius: 3px; */
+		cursor: pointer;
+		background-color: #fff;
+	}
+
+	.status-button.active {
+		background-color: #40a9ff;
+		color: #fff;
+		border-color: #40a9ff;
+	}
+	.upload-area {
+		border: 2px dashed #3ca9f5;
+		padding: 40px;
+		text-align: center;
+		transition: border-color 0.3s; /* 平滑过渡效果 */
+	}
+
+	.upload-area.drag-over {
+		border-color: #1890ff;
+		background-color: rgba(24, 144, 255, 0.05);
+	}
+</style>

+ 685 - 0
src/components/MiniYun/resourceUpload.vue

@@ -0,0 +1,685 @@
+<template>
+	<!-- 上传资源模态框 -->
+	<a-modal
+		v-model:visible="uploadModalVisible"
+		:title="isState == 0 ? '上传资源' : '编辑'"
+		@ok="handleUploadOk"
+		@cancel="handleUploadCancel"
+		width="600px"
+	>
+		<a-form :model="formState" :rules="rules" ref="formRef">
+			<a-form-item label="单位" name="collegeId" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-cascader
+					v-model:value="majorIdName"
+					:options="collegeMajorOptions"
+					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
+					placeholder="请选择单位"
+					style="width: 300px"
+					changeOnSelect
+					@change="changeCollegeMajor"
+				/>
+			</a-form-item>
+			<!-- <a-form-item label="专业" name="majorId" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-select
+					v-model:value="formState.majorId"
+					style="width: 200px"
+					:fieldNames="{ label: 'majorName', value: 'majorCode' }"
+					:options="majorOptions"
+					placeholder="请选择专业"
+				/>
+			</a-form-item> -->
+			<!-- <a-form-item label="课程" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-select
+					v-model:value="formState.courseId"
+					style="width: 200px"
+					:fieldNames="{ label: 'courseName', value: 'courseId' }"
+					:options="courseOptions"
+					placeholder="请选择课程"
+				/>
+			</a-form-item> -->
+			<a-form-item label="资源类型" name="resourceType" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-cascader
+					style="width: 240px; margin-left: 8px"
+					v-model:value="resourceName"
+					:options="resourceTypeOptions"
+					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
+					placeholder="请选择资源类型"
+					changeOnSelect
+					@change="changeCollegeResource"
+				/>
+			</a-form-item>
+			<a-form-item
+				label="关键词(需添加2-5个关键词)"
+				name="keywordValue"
+				:label-col="{ span: 10 }"
+				:wrapper-col="{ span: 18 }"
+			>
+				<div v-if="!formState.keywordValue.length" style="color: #ccc">请选择下方热门关键词</div>
+				<div v-if="formState.keywordValue.length">
+					<a-tag
+						v-for="(keyword, index) in formState.keywordValue"
+						:key="index"
+
+						@close="handleRemoveKeyword(keyword, index)"
+					>
+						{{ keyword }}
+					</a-tag>
+				</div>
+			</a-form-item>
+			<a-form-item label="热门关键词" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-checkbox-group
+					v-model:value="formState.keyword"
+					:options="HotKeywordsOptions"
+					@change="handleChangeKeyword"
+				/>
+			</a-form-item>
+			<a-row>
+				<a-col :span="5"></a-col>
+				<a-col :span="8">
+					<a-input v-model:value="newKeyword" placeholder="请输入关键词" @pressEnter="handleAddKeyword" />
+				</a-col>
+				<a-col :span="9" style="line-height: 30px"> 按回车Enter创建新关键词 </a-col>
+			</a-row>
+			<a-form-item label="资源是否公开" style="margin-top: 10px" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<div class="public-status-buttons">
+					<button :class="['status-button', { active: formState.authType === '0' }]" @click="setPublicStatus('0')">
+						公开
+					</button>
+					<button :class="['status-button', { active: formState.authType === '1' }]" @click="setPublicStatus('1')">
+						部分人可见
+					</button>
+				</div>
+			</a-form-item>
+			<a-form-item label="是否热门" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-radio-group v-model:value="formState.isHot">
+					<a-radio :value="0">否</a-radio>
+					<a-radio :value="1">是</a-radio>
+				</a-radio-group>
+			</a-form-item>
+			<a-form-item label="是否推荐" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-radio-group v-model:value="formState.isRecommend">
+					<a-radio :value="0">否</a-radio>
+					<a-radio :value="1">是</a-radio>
+				</a-radio-group>
+			</a-form-item>
+
+
+			<!-- 选择成员 -->
+			<userSelection
+				:visible="userReleaseVisible"
+				:userRelateIds="userRelateIdss"
+				@close="userReleaseVisible = false"
+				@confirm="confirmUser"
+			></userSelection>
+		</a-form>
+		<template v-if="isState == 0">
+			<!-- 资源上传 -->
+			<!-- <UploadModal @success="uploadSuccess"></UploadModal> -->
+			<UpLoadBreakPoint ref="upLoadBreakPointRef" @onSuccess="onSuccess"></UpLoadBreakPoint>
+		</template>
+	</a-modal>
+</template>
+
+<script setup>
+	import { ref, reactive, onMounted } from 'vue'
+	import { Modal, Upload, Form } from 'ant-design-vue'
+	import resourceAuditApi from '@/api/resourceAudit.js'
+	import userSelection from './userSelection.vue'
+	import UpLoadBreakPoint from './upLoadBreakPoint.vue'
+	import tool from '@/utils/tool'
+	import sysConfig from "@/config";
+
+	const { proxy } = getCurrentInstance()
+	const teacherData = ref(null)
+	const props = defineProps({
+		isState: {
+			type: Number,
+			required: true,
+			default: 0
+		},
+		isVerifyStatus: {
+			type: Number,
+			required: true,
+			default: 0
+		},
+		resourcesId: {
+			type: Number,
+			required: true,
+			default: null
+		}
+	})
+	const emit = defineEmits(['close', 'getList'])
+
+	const headers = ref({
+		token: tool.data.get('TOKEN')
+	})
+	//课程类型
+	const courseTypeOptions = tool.dictList('COURSE_TYPE')
+	const action = ref(sysConfig.API_URL+'/api/webapp/dev/file/uploadMinioReturnId')
+	const formState = reactive({
+		userfileIds: null, //资源文件id
+		coverImage: undefined, //封面id
+		// courseId: null, //课程
+		// majorType: null, //专业
+		resourceType: null, //资源类型一级
+		resourceTwoType: null, //资源类型二级
+		resourceThreeType: null, //资源类型二级
+		collegeId: null, //院校一级id
+		collegeTwoId: null, //院校二级id
+		collegeThreeId: null, //院校三级id
+		resourceDesc: null, //资源介绍
+		// majorId: null, //专业id
+		// courseTypeName: [], // 资源类型
+		keywordValue: [], // 添加关键词
+		keyword: [], // 热门关键词
+		authType: '0', // 资源是否公开
+		userRelateIds: null, //资源公开人员id
+		isRecommend: 0, // 资源是否推荐
+		isHot: 0 // 资源是否热门
+	})
+	import { miniyunStore } from '@/store/miniyun'
+	const myMiniyunStore = miniyunStore()
+	const upLoadBreakPointRef = ref(null) // 预览回显
+	const coverImagePath = ref() // 预览回显
+	const formRef = ref() // 添加表单引用
+	const collegeMajorOptions = ref([]) //院系
+	const resourceTypeOptions = ref([]) //资源类型下拉数据
+	const HotKeywordsOptions = ref([]) //热门关键词列表
+	const majorIdName = ref([]) //院系回显
+	const resourceName = ref([]) //资源回显
+	const majorOptions = ref([]) //专业
+	const courseOptions = ref([]) //课程
+	const userRelateIdss = ref([])
+	const myHot = ref({})
+	// 上传资源模态框
+	const uploadModalVisible = ref(false)
+	// 用户选择模态框
+	const userReleaseVisible = ref(false)
+
+	// 文件列表
+	const fileList = ref([])
+
+	const open = (data) =>{
+		uploadModalVisible.value = true
+		teacherData.value = data
+		console.log('老师参数是',teacherData.value)
+		getOrgTreeSelector()
+		getHotKeywords()
+		getResourceTypeTree()
+		getformState()
+		// if (props.isState == 1) {
+		// 	getDetail()
+		// } else {
+		// 	getformState()
+		// }
+
+		nextTick(()=>{
+			upLoadBreakPointRef.value.open()
+		})
+
+	}
+
+	// 关闭模态框
+	const handleUploadCancel = () => {
+		emit('close')
+		fileList.value = []
+	}
+	// 新增Set记录已处理ID
+	// const processedIds = ref(new Set())
+	//资源文件上传成功返回
+	const uploadSuccess = async (idsArr) => {
+		formState.userfileIds = idsArr.join(',')
+		// const newIds = idsArr.filter((id) => !processedIds.value.has(id))
+		// if (newIds.length === 0) return
+		// //文件转换
+		// try {
+		// 	const requests = newIds.map((id) => resourceAuditApi.fileFormatConversion({ userFileId: id }))
+		// 	await Promise.all(requests)
+		// 	newIds.forEach((id) => processedIds.value.add(id))
+		// 	newKeyword.value = ''
+		// } catch (err) {
+		// 	console.error('部分请求失败:', err)
+		// }
+	}
+	const onSuccess = (uploadFileList) => {
+		let list = []
+		console.log('formState.userfileIds是数组:', uploadFileList)
+		for (let i = 0; i < uploadFileList.length; i++) {
+			if (uploadFileList[i].userFileId) {
+				list.push(uploadFileList[i].userFileId)
+			}
+		}
+		console.log('formState.userfileIds是:', list)
+		formState.userfileIds = list.join(',')
+	}
+
+	// 自定义校验函数示例
+	const validateKeywords = (rule, value, callback) => {
+		if (value.length < 2) {
+			callback(new Error('至少需要添加2个关键词'))
+		} else if (value.length > 5) {
+			callback(new Error('最多只能添加5个关键词'))
+		} else {
+			callback()
+		}
+	}
+	// 封面文件id
+	const handleChangeCover = (fileId) => {
+		formState.coverImage = fileId
+	}
+
+	// 移除封面文件
+	const handleRemoveCover = () => {
+		formState.coverImage = undefined
+	}
+	const rules = {
+		// courseTypeName: [{ required: true, message: '请选择资源类型', trigger: 'change' }],
+		collegeId: [{ required: true, message: '请选择单位', trigger: 'blur' }],
+		keywordValue: [
+			{ required: true, message: '请选择关键词', trigger: 'blur' },
+			{ validator: validateKeywords, trigger: 'blur' }
+		],
+		// coverImage: [{ required: true, message: '请上传封面', trigger: 'blur' }],
+		resourceType: [{ required: true, message: '请选择资源类型', trigger: 'blur' }],
+		resourceDesc: [
+			{ required: true, message: '请输入资源描述', trigger: 'blur' },
+			{ pattern: /^[\s\S]{10,500}$/, message: '描述长度应在10-500字符之间', trigger: 'blur' }
+		],
+		coverImage: [{ required: true, message: '请上传封面', trigger: 'blur' }],
+	}
+	const newKeyword = ref('') //关键词
+	const handleAddKeyword = (e) => {
+		const newKeywords = newKeyword.value.trim()
+		resourceAuditApi
+			.addHotKeywords({
+				wordName: newKeywords,
+				popular: 0
+			})
+			.then((res) => {
+				console.log(res.data, '添加热门关键词')
+				getHotKeywords()
+				newKeyword.value = ''
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+		// if (newKeywords && !formState.keywordValue.includes(newKeywords)) {
+		// 	formState.keywordValue.push(newKeywords)
+		// }
+	}
+
+	const handleRemoveKeyword = (keywordName, index) => {
+
+		console.log('删除的东西 01 ', ' keywordName ',keywordName,' index ',index)
+		console.log('删除的东西 02 ', ' formState.keyword ',formState.keyword)
+
+
+		console.log(keywordName, index,myHot.value)
+		// // 1. 从显示列表中删除
+		// formState.keywordValue = formState.keywordValue.filter((name) => name !== keywordName)
+		// // console.log('看看点击取消了什么',' formState.keywordValue ',formState.keywordValue)
+		// // formState.keyword
+		// // 2. 更新checkbox的绑定值
+		// formState.keyword = HotKeywordsOptions.value
+			// .filter((option) => formState.keywordValue.includes(option.label))
+			// .map((item) => item.value)
+		// // formState.keyword = formState.keyword.filter((item, i) => i !== index)
+		// // console.log('看看点123123',' formState.keyword ',formState.keyword,' index ',index)
+
+		let myId = myHot.value[keywordName]
+		// console.log('换算出来',formState.keyword,index)
+
+		// for (let i = formState.keyword.length-1; i > 0 ; i--) {
+		// 	// console.log('看看',formState.keyword[i] , i , index)
+		// 	console.log('要删除第几个' ,i, index ,  formState.keyword[i])
+		// 	if( i == index){
+		//
+		// 		// formState.keyword.splice(i,1)
+		// 	}
+		// }
+		// console.log('换算出来  结果',formState.keyword)
+
+		// formState.keyword.filter((item, i) => item != keywordName)
+		// formState.keyword = [{'专业' : 1}]
+		// formState.keyword.filter((item, i) => item != index)
+		// formState.keyword.filter((option) => formState.keywordValue.includes(option.value))
+		// 	.map((item) => item.label)
+		// console.log('换算出来 剩下',formState.keyword)
+		// console.log('还有吗',formState.keywordValue)
+
+	}
+	const getHotKeywords = () => {
+		myHot.value = {}
+		resourceAuditApi
+			.HotKeywords()
+			.then((res) => {
+				console.log(res.data, '获取热门关键词')
+				HotKeywordsOptions.value = res.data.map((it) => {
+					myHot.value[it.wordName] = it.id
+					return {
+						value: it.id,
+						label: it.wordName
+					}
+				})
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	//资源类型下拉查询
+	const getResourceTypeTree = () => {
+		resourceAuditApi
+			.resourceTypeTree()
+			.then((res) => {
+				console.log(res.data, '资源类型下拉')
+				resourceTypeOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	const handleChangeKeyword = (checkedValues) => {
+		formState.keyword = checkedValues
+		console.log('点击checkbox',checkedValues )
+		formState.keywordValue = HotKeywordsOptions.value
+			.filter((option) => checkedValues.includes(option.value))
+			.map((item) => item.label)
+		console.log('点击checkbox  123 ',formState.keywordValue )
+	}
+	const setPublicStatus = (status) => {
+		formState.authType = status
+		if (status === '1') {
+			userReleaseVisible.value = true
+		}
+	}
+	//院系组织查询
+	const getOrgTreeSelector = () => {
+		resourceAuditApi
+			.orgTreeSelector()
+			.then((res) => {
+				console.log(res.data, '获取组织树选择器')
+				collegeMajorOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	//院系组织查询
+	const changeCollegeMajor = (value, selectedOptions) => {
+		console.log('Selected:', value, selectedOptions)
+		if (!value) {
+			majorIdName.value = ''
+			return false
+		}
+		majorIdName.value = selectedOptions.map((it) => it.name).join('/')
+		formState.collegeId = value[0] || null
+		formState.collegeTwoId = value[1] || null
+		formState.collegeThreeId = value[2] || null
+		if (selectedOptions.length) {
+			// 获取选中的最后一级
+			const lastSelected = selectedOptions[selectedOptions.length - 1]
+			// formState.selectedCollegeMajor = {
+			// 	id: lastSelected.id,
+			// 	name: lastSelected.name,
+			// 	fullPath: selectedOptions.map((opt) => opt.name).join(' / ')
+			// }
+			console.log(lastSelected, '最后一级id')
+			getCollegeMajor(lastSelected.id)
+		}
+	}
+	const changeCollegeResource = (value, selectedOptions) => {
+		console.log('Selected:', value, selectedOptions)
+		if (!value) {
+			resourceName.value = ''
+			return false
+		}
+		resourceName.value = selectedOptions.map((it) => it.name).join('/')
+		formState.resourceType = value[0] || undefined
+		formState.resourceTwoType = value[1] || undefined
+		formState.resourceThreeType = value[2] || undefined
+	}
+	const getCollegeMajor = (id) => {
+		resourceAuditApi
+			.zyselect({ collegeId: id })
+			.then((res) => {
+				console.log(res.data, '专业下拉数据')
+				majorOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	const confirmUser = (userIds) => {
+		console.log(userIds, '用户id')
+		userReleaseVisible.value = false
+		formState.userRelateIds = userIds.join(',')
+	}
+	// 确认上传
+	const handleUploadOk = async () => {
+		try {
+			await formRef.value.validate()
+			// console.log('formState.userfileIds是:提交了', formState.userfileIds)
+			// if (!formState.userfileIds) {
+			// 	Modal.error({ content: '请先上传文件!!' })
+			// 	return
+			// }
+			if (upLoadBreakPointRef.value.getUploadFileList().length == 0) {
+				Modal.error({ content: '请先上传文件!!' })
+				return
+			}
+			const formData = {
+				// userfileIds: formState.userfileIds,
+				key : new Date().getMilliseconds(),
+				teacherId : teacherData.value.teacherId,
+				resourceType: formState.resourceType,
+				resourceTwoType: formState.resourceTwoType,
+				resourceThreeType: formState.resourceThreeType,
+				collegeId: formState.collegeId,
+				collegeTwoId: formState.collegeTwoId,
+				collegeThreeId: formState.collegeThreeId,
+				keywordValue: formState.keywordValue.join(','),
+				keyword: formState.keyword.join(','),
+				resourceDesc: formState.resourceDesc,
+				isHot: formState.isHot,
+				isRecommend: formState.isRecommend,
+				coverImage: formState.coverImage,
+				publicStatus: formState.publicStatus,
+				authType: formState.authType,
+				userRelateIds: formState.authType == 1 ? formState.userRelateIds : null,
+				uploadFileList : upLoadBreakPointRef.value.getUploadFileList()
+			}
+			console.log('上传数据',formData )
+			myMiniyunStore.addFileForms(formData)
+
+			uploadModalVisible.value = false
+				// resourceAuditApi
+				// 	.add(formData)
+				// 	.then((res) => {
+				// 		emit('getList')
+				// 		Modal.success({ content: '资源上传成功' })
+				// 	})
+				// 	.catch((err) => {
+				// 		Modal.success({ content: '资源上传失败' })
+				// 		console.log(err)
+				// 	})
+		} catch (error) {
+			console.error('错误信息',error)
+			if (error.errorFields) {
+				// 表单验证错误
+				Modal.error({ content: '请检查表单填写是否正确' })
+			} else {
+				// API错误
+				Modal.error({ content: '资源上传失败' })
+				console.error(error)
+			}
+		}
+	}
+	// 获取资源详情
+	const getDetail = () => {
+		resourceAuditApi.detail({ id: props.resourcesId }).then((res) => {
+			console.log(res.data, '资源详情')
+			formState.userfileIds = res.data.fileId
+			formState.userRelateIds = res.data.userRelateIdList?.join(',')
+			userRelateIdss.value = res.data.userRelateIdList
+			formState.resourceType = res.data.resourceType
+			formState.resourceTwoType = res.data.resourceTwoType
+			formState.resourceThreeType = res.data.resourceThreeType
+			formState.collegeId = res.data.collegeId
+			formState.coverImage = res.data.coverImage
+			coverImagePath.value = res.data.coverImagePath
+			formState.collegeTwoId = res.data.collegeTwoId
+			formState.collegeThreeId = res.data.collegeThreeId
+			majorIdName.value = res.data.collegeAllId?.split(',')
+			resourceName.value = res.data.resourceALLTypeName?.split(',')
+			getCollegeMajor(majorIdName.value[majorIdName.value.length - 1])
+			formState.resourceDesc = res.data.resourceDesc
+			// formState.majorId = res.data.majorId
+			formState.keywordValue = res.data.keywordValue?.split(',')
+			formState.keyword = res.data.keyword.split(',').filter(Boolean).map(Number)
+
+			formState.isRecommend = Number(res.data.isRecommend) || 0
+			formState.isHot = Number(res.data.isHot) || 0
+			formState.authType = res.data.authType
+		})
+	}
+	// 获取历史添加表单
+	const getformState = () => {
+		resourceAuditApi.recentlyRecord().then((res) => {
+			console.log(res.data, '历史表单数据')
+			formState.resourceType = res.data.resourceType
+			formState.userRelateIds = res.data.userRelateIdList?.join(',')
+			userRelateIdss.value = res.data.userRelateIdList
+			formState.resourceTwoType = res.data.resourceTwoType
+			formState.resourceThreeType = res.data.resourceThreeType
+			formState.collegeId = res.data.collegeId
+			formState.collegeTwoId = res.data.collegeTwoId
+			formState.collegeThreeId = res.data.collegeThreeId
+			majorIdName.value = res.data.collegeAllId?.split(',')
+			resourceName.value = res.data.resourceAllType?.split(',')
+			getCollegeMajor(majorIdName.value[majorIdName.value.length - 1])
+			formState.resourceDesc = res.data.resourceDesc
+			// formState.majorId = res.data.majorId
+			formState.keywordValue = res.data.keywordValue?.split(',')
+			formState.keyword = res.data.keyword?.split(',').filter(Boolean).map(Number)
+		})
+	}
+	// 上传前的钩子函数
+	const beforeUpload = (file) => {
+		const isLt2G = file.size / 1024 / 1024 / 1024 < 2
+		if (!isLt2G) {
+			Modal.error({ content: '文件大小不能超过 2GB!' })
+		}
+		return isLt2G
+	}
+
+	// 移除文件
+	const handleRemove = (file) => {
+		const index = fileList.value.indexOf(file)
+		const newFileList = fileList.value.slice()
+		newFileList.splice(index, 1)
+		fileList.value = newFileList
+		// 如果移除的是当前封面文件,则清空coverImageId
+		if (formState.userfileIds === file.id) {
+			formState.userfileIds = null
+		}
+	}
+
+	// 文件状态改变时的处理函数
+	const handleChange = ({ file, fileList: newFileList }) => {
+		if (newFileList.length > 1) {
+			fileList.value = [newFileList[0]] // 只保留最新上传的文件
+			Modal.error({ content: '只能上传一个文件!' })
+			return
+		}
+
+		if (file.response?.code == 200) {
+			// 上传成功,获取文件ID
+			const fileId = file.response?.data || file.id
+			console.log('上传成功,获取文件ID', fileId)
+			if (fileId) {
+				formState.userfileIds = fileId
+			}
+		}
+		fileList.value = newFileList
+		if (file.status === 'uploading') {
+			file.percent = Math.floor(file.percent)
+		}
+		if (file.response?.code == 200) {
+			file.percent = 100
+			Modal.success({ content: '文件上传成功' })
+		} else if (file.response?.code == 500) {
+			Modal.error({ content: '文件上传失败' })
+			file.percent = 0
+		}
+	}
+	onMounted(() => {
+		// getOrgTreeSelector()
+		// getHotKeywords()
+		// getResourceTypeTree()
+		// if (props.isState == 1) {
+		// 	getDetail()
+		// } else {
+		// 	getformState()
+		// }
+	})
+
+	defineExpose({
+		open
+	})
+</script>
+<style scoped>
+	.upload-area {
+		border: 2px dashed #3ca9f5;
+		padding: 40px;
+		text-align: center;
+	}
+
+	.upload-area p {
+		margin: 10px 0;
+	}
+
+	.file-item {
+		display: flex;
+		align-items: center;
+		margin: 10px 0;
+	}
+
+	.file-item .ant-progress {
+		flex: 1;
+		margin: 0 10px;
+	}
+
+	/* 新增表单样式 */
+	.ant-form-item {
+		margin-bottom: 16px;
+	}
+	.public-status-buttons {
+		display: flex;
+	}
+
+	.status-button {
+		padding: 5px 10px;
+		/* margin-right: 10px; */
+		border: 1px solid #ccc;
+		/* border-radius: 3px; */
+		cursor: pointer;
+		background-color: #fff;
+	}
+
+	.status-button.active {
+		background-color: #40a9ff;
+		color: #fff;
+		border-color: #40a9ff;
+	}
+	.upload-area {
+		border: 2px dashed #3ca9f5;
+		padding: 40px;
+		text-align: center;
+		transition: border-color 0.3s; /* 平滑过渡效果 */
+	}
+
+	.upload-area.drag-over {
+		border-color: #1890ff;
+		background-color: rgba(24, 144, 255, 0.05);
+	}
+</style>

+ 848 - 0
src/components/MiniYun/upLoadBreakPoint.vue

@@ -0,0 +1,848 @@
+<template>
+	<div class="mydiv">
+		<a-spin :spinning="spinning" tip="读取中...">
+			<a-upload-dragger
+				ref="fileUpload"
+				:file-list="fileList"
+				:before-upload="beforeUpload"
+				@change="handleChange"
+				:show-upload-list="false"
+				:customRequest="customRequest"
+				:multiple="false"
+				:drag="true"
+				:progress="progress"
+			>
+				<div >
+									<div><p class="ant-upload-text" style="display: inline-block;">点击上传或将文件拖拽至此区域上传</p></div>
+					<div>	<p class="ant-upload-hint"  style="display: inline-block;">
+										按住Ctrl可同时多选,支持上传
+									</p></div>
+					<div>	<p class="ant-upload-hint" style="width: 50%;
+    display: inline-block;">
+										{{props.upLoadfileLists.join('/')}}
+									</p></div>
+					<div><p class="ant-upload-hint"  style="display: inline-block;">
+						等单个文件不能超过2G
+					</p></div>
+				</div>
+
+			</a-upload-dragger>
+	</a-spin>
+
+
+		<!-- <div style="margin-bottom: 20px">
+			<a-button v-if="uploadFileList.length > 0" type="primary" @click="uploadFilesList">上传</a-button>
+		</div> -->
+
+		<div v-for="(item, index) in uploadFileList" :key="index">
+			<div style="padding: 10px">
+				<div style="display: flex; width: 100%; align-items: center; justify-content: space-between">
+					<div>
+						<span>{{ item.name.length > 20 ? item.name.slice(0, 20) + '...' + item.fileSuffix : item.name }}</span>
+					</div>
+					<div>
+						<span v-if="item.time != ''" style="display: block; color: blue">{{ item.time }}</span>
+					</div>
+					<div>
+						<div>
+<!--							<span-->
+<!--								v-if="item.percents == 0"-->
+<!--								style="color: red; cursor: pointer; margin-left: 10px"-->
+<!--							>读取中</span-->
+<!--							>-->
+							<span
+								v-if="item.percents == 0 || item.percents == 100"
+								style="color: red; cursor: pointer; margin-left: 10px"
+								@click="handlerRemoveItem(index)"
+								>删除</span
+							>
+<!--							<span-->
+<!--								v-if="-->
+<!--									item.percents >= 0 &&-->
+<!--									item.percents < 100 &&-->
+<!--									(pauseFlags[item.md5] == false || pauseFlags[item.md5] == undefined)-->
+<!--								"-->
+<!--								style="color: blue; cursor: pointer; margin-left: 10px"-->
+<!--								@click="pauseUpload(index)"-->
+<!--								>暂停</span-->
+<!--							>-->
+<!--							<span-->
+<!--								v-if="item.percents >= 0 && item.percents < 100 && pauseFlags[item.md5] == true"-->
+<!--								style="color: green; cursor: pointer; margin-left: 10px"-->
+<!--								@click="resumeUpload(index)"-->
+<!--								>恢复</span-->
+<!--							>-->
+						</div>
+					</div>
+				</div>
+
+<!--				<a-progress :percent="item.percents" />-->
+			</div>
+		</div>
+
+		<!-- <div> -->
+		<!-- <el-progress :text-inside="true" :stroke-width="20" :percentage="successfulChunkPercents" status="success" /> -->
+		<!-- <a-progress :percent="successfulChunkPercents" /> -->
+		<!-- </div> -->
+
+		<!--    已上传列表-->
+		<!-- <el-table :data="uploadList" border style="width: 100%">
+			<el-table-column fixed prop="id.date" label="日期" width="150"> </el-table-column>
+			<el-table-column prop="url" label="下载地址"> </el-table-column>
+
+			<el-table-column label="操作">
+				<template #default="scope">
+					<el-button link type="primary" size="small" @click.prevent="deleteFile(scope.row.url)"> 删除 </el-button>
+					<el-button link type="primary" size="small" @click.prevent="downloadFile(scope.row.url)"> 下载 </el-button>
+				</template>
+			</el-table-column>
+		</el-table> -->
+	</div>
+</template>
+<script setup>
+import { ref, onMounted } from 'vue'
+import axios from 'axios'
+import sysConfig from '@/config/index'
+	import { miniyunStore } from '@/store/miniyun'
+	const myMiniyunStore = miniyunStore()
+const fileUpload = ref(null)
+	import { message } from 'ant-design-vue'
+	import SparkMD5 from 'spark-md5'
+	import tool from '@/utils/tool'
+	 const uploadFileList = ref([])
+	 const spinning = ref(false)
+const chunkSize = ref(5 * 1024 * 1024)
+	const props = defineProps({
+		uploadCount: {
+			type: Number,
+			default: () => 10
+		},
+		upLoadfileLists: {
+			type: Array,
+			// 视频"wmv","avi","flv","mpeg","mpg","rmvb","mov","mkv","mp4"以及文档"doc","docx","ppt","pptx","xls","xlsx","pdf"应该都允许
+			default: () => ['jpg','png','pdf','mp4','wmv','avi','flv','mpeg','mpg','rmvb','mov','mkv','docx','doc','xlsx','xls','pptx','ppt','txt','cad','zip','rar','dwg','dxf','dwt']
+		},
+	})
+
+	const handlerRemoveItem = (index) => {
+		const item = uploadFileList.value[index]
+		// if (item && item.md5) {
+		// 	delete pauseFlags.value[item.md5] // 清理暂停标志
+		// }
+		uploadFileList.value.splice(index, 1)
+		// emit('onSuccess', uploadFileList.value)
+	}
+	const pauseUpload = (index) => {
+		const item = uploadFileList.value[index]
+		if (item && item.md5) {
+			pauseFlags.value[item.md5] = true
+			uploadingTasks.value[item.md5] = false
+		}
+	}
+
+	const resumeUpload = (index) => {
+		const item = uploadFileList.value[index]
+		if (!item || !item.md5) return
+
+		pauseFlags.value[item.md5] = false
+
+		// 如果当前上传任务小于 2,则开始上传
+		const activeTasks = Object.keys(uploadingTasks.value).filter((key) => uploadingTasks.value[key])
+		if (activeTasks.length < 2) {
+			uploadingTasks.value[item.md5] = true
+			// uploadSingleFile(item)
+		} else {
+			pauseFlags.value[item.md5] = true
+		}
+	}
+	const calculateFileMD5 = (file) => {
+		return new Promise((resolve, reject) => {
+			const reader = new FileReader()
+			const spark = new SparkMD5.ArrayBuffer()
+
+			reader.onload = (e) => {
+				spark.append(e.target.result)
+				const md5 = spark.end()
+				resolve(md5)
+			}
+
+			reader.onerror = () => reject(new Error('文件读取失败'))
+
+			reader.readAsArrayBuffer(file)
+		})
+	}
+
+	const updateProgress = (chunkSize) => {
+		uploadedSize.value += chunkSize
+		const percent = Math.round((uploadedSize.value / totalSize.value) * 100)
+		console.log(`上传进度: ${percent}%`)
+	}
+	const calculateSpeed = (startTime, uploadedSize) => {
+		const currentTime = new Date().getTime()
+		const timeElapsed = (currentTime - startTime) / 1000 // 单位:秒
+		if (timeElapsed > 0) {
+			const speed = uploadedSize / timeElapsed // 单位:字节/秒
+			return speed
+		}
+		return 0
+	}
+	const estimateRemainingTime = (startTime, uploadedSize, totalSize) => {
+		console.log('疑问', ' 总的 ', totalSize, ' 变化的 ', uploadedSize)
+		const remainingSize = totalSize - uploadedSize // 剩余文件大小
+		const speed = calculateSpeed(startTime, uploadedSize) // 平均上传速度(字节/秒)
+
+		if (speed > 0) {
+			const remainingTimeSeconds = remainingSize / speed // 剩余时间(秒)
+			return remainingTimeSeconds
+		}
+		return Infinity // 如果上传速度为 0,则无法估算
+	}
+	const formatTime = (seconds) => {
+		const minutes = Math.floor(seconds / 60)
+		const secs = Math.floor(seconds % 60)
+		if (minutes == 0 && secs == 0) {
+			return ''
+		}
+		return `${minutes} 分 ${secs} 秒`
+	}
+	// 异步方法:选择文件时触发 ============
+	const handleFileChange = async (file) => {
+		// const reader = new FileReader()
+		//   const spark = new SparkMD5.ArrayBuffer()
+		// console.log("读取文件",file.raw)
+		// reader.readAsArrayBuffer(file.raw) // 异步读取文件内容
+
+		// const fileContent = await new Promise((resolve, reject) => {
+		//     reader.onload = (event) => {
+		//         console.log('开始读取文件了...', event, reader.result)
+		//          spark.append(event.target.result)
+		//         resolve(spark.end())
+		//     }
+		//     reader.onerror = (error) => {
+		//         console.log('读取文件时发生错误...', error)
+		//         reject(error)
+		//     }
+		//     reader.readAsArrayBuffer(file)
+		// })
+		// console.log('111文件的md5哈希值是 fileContent:', fileContent)
+		// 计算MD5哈希值
+		let fileMd5 = await calculateFileMD5(file.raw)
+		file.raw.md5 = fileMd5
+		console.log('====开始获取File对象了...', file)
+		let fileSuffix = '.' + file.raw.name.split('.').pop() // 得到.文件类型
+		let currentFile = file.raw
+		console.log('currentFile:', currentFile)
+		// for (let i = 0; i < chunkCount.value; i++) {
+		//     // 文件开始遍历切片存入数组
+		//     const start = i * chunkSize.value
+		//     console.log('循环中的currentFile:', currentFile.value)
+		//     const end = Math.min(
+		//         start + chunkSize.value - 1,
+		//         currentFile.value.size - 1
+		//     )
+		//     chunkList.value[i] = currentFile.value.slice(start, end)
+		// }
+		let chunkList = splitFileByChunkSize(currentFile, chunkSize.value)
+		console.log('file对象:', file)
+		return {
+			name: file.raw.name,
+			size: currentFile.size,
+			md5: fileMd5, // md5哈希值
+			chunks: chunkList, // 分块列表
+			fileSuffix: fileSuffix, // 后缀
+			percents: 0,
+			time: ''
+		}
+
+		// console.log('md5:', fileMd5.value)
+		// console.log('chunkList:', chunkList.value)
+		// console.log('fileSuffix:', fileSuffix.value)
+		// console.log('currentFile:', currentFile.value)
+		// uploadFileList.value.push({
+		// 	name: file.raw.name,
+		// 	size: currentFile.value.size,
+		// 	md5: fileMd5.value, // md5哈希值
+		// 	chunks: chunkList.value, // 分块列表
+		// 	fileSuffix: fileSuffix.value, // 后缀
+		// 	percents: 0,
+		// 	time: ''
+		// }) // 使用对象数组进行处理 ============
+		// chunkList.value = []
+		// console.log('uploadFileList是:', uploadFileList.value)
+		// console.log('结束handleFileChange了')
+	}
+
+	const splitFileByChunkSize = (file, chunkSizeValue) => {
+		const fileSize = file.size
+		const chunkCount = Math.ceil(fileSize / chunkSizeValue) // 计算总分块数
+		// console.log(
+		//     '统计:',
+		//     '块数',
+		//     chunkCount,
+		//     '文件大小',
+		//     fileSize,
+		//     ' 单块 ',
+		//     chunkSizeValue
+		// )
+		let chunkList = []
+		for (let i = 0; i < chunkCount; i++) {
+			const start = i * chunkSizeValue
+			const min = start + chunkSizeValue
+
+			console.log('奇怪:', ' start ', start, ' chunkSizeValue ', chunkSizeValue, ' min ', min)
+			const end = Math.min(min, fileSize)
+			// console.log(
+			//     '统计:',
+			//     ' start ',
+			//     start,
+			//     ' end ',
+			//     end,
+			//     ' 单块 ',
+			//     chunkSizeValue,
+			//     ' 比较谁大 ',
+			//     start + chunkSizeValue,
+			//     ' ssss ',
+			//     fileSize
+			// )
+			chunkList[i] = file.slice(start, end) // 注意:slice 是 [start, end) 前闭后开区间
+
+			console.log(
+				'准备开始:',
+				' 循环次数 ',
+				i,
+				' 块数 ',
+				chunkCount,
+				'开始的大小',
+				start,
+				'结束的大小',
+				end,
+				' file ',
+				file.slice(start, end)
+			)
+		}
+
+		console.log('分片完成:', chunkList)
+		return chunkList
+	}
+
+	const checkMd5List = async (uploadFile) => {
+		let md5List = []
+		let element = {
+			md5: uploadFile.md5,
+			size: uploadFile.size,
+			chunkSize: uploadFile.chunks.length,
+			fileName: uploadFile.name,
+			fileSuffix: uploadFile.fileSuffix
+		}
+		md5List.push(element)
+
+		await axios
+			.post(sysConfig.API_URL+'/api/webapp/minio/checkMd5List', md5List, { headers: { Token: tool.data.get('TOKEN') } })
+			.then((res) => {
+				console.log('文件上传返回结果:', res.data)
+				// return
+				var list = res.data
+				if (list.length !== 0) {
+					let upList = []
+					for (let item of list) {
+						console.log('item回来的', JSON.stringify(item))
+						if (uploadFile.md5 === item.md5) {
+							uploadFile.userFileId = item.userFileId
+							//重要的步骤
+							if (item.userFileId) {
+								uploadFile.percents = 100
+								pauseFlags.value[item.md5] = false
+								// emit('onSuccess', uploadFile)
+							}
+							// upList.push(item)
+						}
+					}
+					console.log('upList是:', upList)
+					// uploadFileList.value.push(uploadFile)
+					// emit('onSuccess', uploadFileList.value)
+				} else {
+					// clearFileList()
+				}
+
+				// 文件均存在minio中了,无需上传
+				// if (uploadFileList.value.length === 0) {
+				// 	successfulChunkPercents.value = 100
+				// 	alert('文件上传成功')
+				// }
+			})
+			.catch((error) => {
+				console.log('检查返回错误', error)
+			})
+	}
+
+	// 点击上传按钮触发多文件上传 ===============
+	const uploadFilesList = async () => {
+		if (upLoadTag.value == true) {
+			message.loading('正在上传')
+			return
+		}
+		upLoadTag.value = true
+
+		emit('onUpLoading', upLoadTag.value)
+		if (currentFile.value == null) {
+			// alert('请选择文件后再上传!')
+			message.error('请选择文件后再上传!')
+			successfulChunkPercents.value = 0 // 重置百分比
+			fileList.value = [] // 文件列表
+			return
+		}
+		// 检查所有文件中是否存在未上传的,未上传则需要上传对应的文件 =========
+		let md5List = []
+		console.log('准备上传', uploadFileList.value)
+		for (let i = 0; i < uploadFileList.value.length; i++) {
+			let element = {
+				// md5: uploadFileList.value[i].md5,
+				md5: uploadFileList.value[i].md5,
+				chunkSize: uploadFileList.value[i].chunks.length,
+				fileName: uploadFileList.value[i].name,
+				fileSuffix: uploadFileList.value[i].fileSuffix
+			}
+			md5List.push(element)
+		}
+		console.log('上传的md5_suffix_List是:', md5List)
+
+		await axios
+			.post(sysConfig.API_URL+'/api/webapp/minio/checkMd5List', md5List, { headers: { Token: tool.data.get('TOKEN') } })
+			.then((res) => {
+				console.log('文件上传返回结果:', res.data)
+				// return
+				var list = res.data
+				if (list.length !== 0) {
+					let upList = []
+					for (let item1 of uploadFileList.value) {
+						for (let item2 of list) {
+							console.log('item回来的', JSON.stringify(item2))
+							if (item1.md5 === item2.md5) {
+								//重要的步骤
+								item1.userFileId = item2.userFileId
+								if (item1.userFileId) {
+									item1.percents = 100
+								}
+								upList.push(item1)
+							}
+						}
+					}
+					console.log('upList是:', upList)
+					uploadFileList.value = upList
+				} else {
+					clearFileList()
+				}
+				console.log('最后必须上传的文件:', uploadFileList.value)
+				// 文件均存在minio中了,无需上传
+				// if (uploadFileList.value.length === 0) {
+				// 	successfulChunkPercents.value = 100
+				// 	alert('文件上传成功')
+				// }
+			})
+			.catch((error) => {
+				console.log('检查返回错误', error)
+			})
+		// return
+		console.log('开始上传', uploadFileList.value)
+		// 检查上传的多个文件是否均存在,如果部分存在,进行剔除,剩余部分仍旧进行上传。
+		// 分块的Promises化
+		const chunkPromises = []
+		// 上传分块的数组,校验是否完成上传
+		const chunksUploadedList = []
+		// 直接计算一共多少个分块
+		uploadFileList.value.forEach((item) => {
+			allChunks.value += item.chunks.length
+		})
+
+		console.log('所有文件加起来一共有多少个分块?', allChunks.value, uploadFileList.value)
+		for (const item of uploadFileList.value) {
+			const md5 = item.md5
+			uploadChunks.value = 0
+
+			if (item.userFileId == undefined || item.userFileId == null) {
+				startTime.value = new Date().getTime() // 开始时间戳(毫秒
+				totalSize.value = item.size // 文件总大小
+				uploadedSize.value = 0
+				for (let i = 0; i < item.chunks.length; i++) {
+					console.log('上传第', i + 1, '个分片')
+					let chunk = item.chunks[i]
+					console.log('去上传...', i + 1, chunk)
+
+					// 检查是否暂停
+					console.log('看看...', ' 列表 ', pauseFlags.value, ' md5 ', md5)
+
+					while (pauseFlags.value[md5] && true == pauseFlags.value[md5]) {
+						await new Promise((resolve) => setTimeout(resolve, 100)) // 等待 1 秒后再次检查
+					}
+					chunkPromises.push(
+						await uploadFilesChunk(
+							{
+								md5,
+								chunk,
+								chunkIndex: i + 1,
+								fileSuffix: item.fileSuffix,
+								chunkSize: item.chunks.length,
+								fileName: item.name
+							},
+							() => {
+								chunksUploaded.value++
+								uploadChunks.value++
+
+								// successfulChunkPercents.value = 100 * (uploadChunks.value / allChunks.value).toFixed(2)
+								uploadedSize.value += chunk.size // 更新已上传大小
+								const remainingTime = estimateRemainingTime()
+								console.log(`预计剩余时间: ${formatTime(remainingTime)}`)
+								item.percents = (100 * (uploadChunks.value / item.chunks.length)).toFixed(2)
+								item.time = formatTime(remainingTime)
+								console.log(
+									'执行了自增...',
+									'分片长度',
+									item.chunks.length,
+									'执行到多少了',
+									uploadChunks.value,
+									'百分比',
+									100 * (uploadChunks.value / item.chunks.length).toFixed(2)
+								)
+								console.log('this.uploadChunks:', uploadChunks.value)
+								console.log('this.allChunks:', allChunks.value)
+								console.log('进度:', (uploadChunks.value / allChunks.value).toFixed(2))
+							}
+						)
+					)
+				}
+			}
+
+			console.log('this.chunkUploaded是:', chunksUploaded.value)
+			chunksUploadedList.push(chunksUploaded.value) // 存储不同文件的上传分块数量
+			chunksUploaded.value = 0
+		}
+		await Promise.all(chunkPromises)
+		console.log('上传完成!')
+		console.log('chunksUploadList是:', chunksUploadedList)
+		let mergeResults = []
+		for (let i = 0; i < chunksUploadedList.length; i++) {
+			console.log('this.uploadFileList' + i + '是:' + uploadFileList.value[i].chunks.length)
+			console.log('chunksUploadedList' + i + '是:' + chunksUploadedList[i])
+			if (uploadFileList.value[i].chunks.length === chunksUploadedList[i]) {
+				const mergeResult = await axios.post(
+					// `/api/webapp/disk/minio/merge?md5=${uploadFileList.value[i].md5}&fileSuffix=${uploadFileList.value[i].fileSuffix}&chunkTotal=${chunksUploadedList[i]}`
+					sysConfig.API_URL+`/api/webapp/minio/merge?md5=${uploadFileList.value[i].md5}&fileSuffix=${uploadFileList.value[i].fileSuffix}&chunkTotal=${chunksUploadedList[i]}&fileName=${uploadFileList.value[i].name}&fileSize=${uploadFileList.value[i].size}`,
+					null,
+					{ headers: { Token: tool.data.get('TOKEN') } }
+				)
+				// if (mergeResult.data.startsWith('[miss_chunk]')) {
+				//     alert('文件缺失,请重新上传')
+				//     return
+				// }
+				console.log('合并结果1:', mergeResult)
+				uploadFileList.value[i].userFileId = mergeResult.data.userFileId
+				// mergeResults.push(mergeResult)
+			}
+		}
+		console.log('合并结果2:', uploadFileList.value)
+		upLoadTag.value = false
+		// 上传完成,清理任务状态
+		delete uploadingTasks.value[md5]
+		emit('onUpLoading', upLoadTag.value)
+		let finalRes = true
+		// emit('onSuccess', uploadFileList.value)
+		// for (const result of mergeResults) {
+		// 	if (result.data === '失败') {
+		// 		finalRes = false
+		// 		alert('上传失败!请重新上传')
+		// 		return
+		// 	} else {
+		// 		alert('上传成功!')
+		// 		successfulChunkPercents.value = 100
+		// 		clearFileList()
+		// 		// getList()
+		// 		return
+		// 	}
+		// }
+	}
+
+	// 多文件上传分片 ============
+	const uploadFilesChunk = async (data, onSuccess) => {
+		console.log('进入了uploadFileChunk方法...')
+		let retryTime = 5 //重试次数
+		const formData = new FormData()
+		formData.append('md5', data.md5)
+		// formData.append('md5', md5)
+		formData.append('chunkIndex', data.chunkIndex)
+		formData.append('chunk', data.chunk)
+		formData.append('chunkSize', data.chunkSize)
+		formData.append('fileSuffix', data.fileSuffix)
+		formData.append('fileName', data.fileName)
+		return axios
+			.post(sysConfig.API_URL+'/api/webapp/minio/upload', formData, {
+				headers: { 'Content-Type': 'multipart/form-data', Token: tool.data.get('TOKEN') }
+			})
+			.then((res) => onSuccess())
+			.catch((error) => {
+				console.log('上传分片失败了...', error)
+				if (retryTime > 0) {
+					retryTime--
+					return uploadChunk(data, onSuccess)
+				}
+			})
+	}
+
+	// 上传分片 旧
+	const uploadChunk = (data, onSuccess) => {
+		let retryTime = 5 //重试次数
+		const formData = new FormData()
+		// formData.append('identifier', fileMd5.value)
+		formData.append('md5', data.md5)
+		// formData.append('md5', md5)
+		formData.append('chunkIndex', data.chunkIndex)
+		formData.append('chunk', data.chunk)
+		formData.append('chunkSize', data.chunkSize)
+		formData.append('fileSuffix', data.fileSuffix)
+		formData.append('fileName', data.fileName)
+		return axios
+			.post(sysConfig.API_URL+'/api/webapp/minio/upload', formData, {
+				headers: { 'Content-Type': 'multipart/form-data', Token: tool.data.get('TOKEN') }
+			})
+			.then((res) => onSuccess())
+			.catch((error) => {
+				if (retryTime > 0) {
+					retryTime--
+					return uploadChunk(data, onSuccess)
+				}
+			})
+	}
+
+	// 删除文件
+	const deleteFile = (url) => {
+		axios
+			.get(sysConfig.API_URL+`/api/webapp/disk/delete?url=` + url)
+			.then((res) => {
+				console.log('删除文件:', res.data)
+				alert(res.data ? '删除成功!' : '删除失败!')
+				// getList()
+			})
+			.catch((error) => {
+				console.log('删除失败:', error)
+			})
+		console.log('url是:', url)
+	}
+
+	const downloadFile = (url) => {
+		window.location.href = url
+	}
+	/**
+	 * 判断文件名的后缀是否存在于指定数组中
+	 * @param {string} fileName - 文件名(如 "sss.mp4")
+	 * @param {Array<string>} suffixArray - 后缀数组(如 ['jpg','png','pdf','mp4',...])
+	 * @returns {boolean} - 是否存在
+	 */
+	const isFileSuffixInArray = (fileName, suffixArray) => {
+		// 获取文件后缀(包括点号)
+		const fileSuffix =  fileName.split('.').pop();
+
+		// 检查后缀是否在数组中(包含点号匹配)
+		return suffixArray.includes(fileSuffix);
+	}
+	const beforeUpload = async (file) => {
+		console.log('选择了文件', file)
+		spinning.value = true
+
+		// upLoadfileLists
+		if(!isFileSuffixInArray(	file.name,props.upLoadfileLists)){
+			const fileSuffix =  file.name.split('.').pop();
+			message.error('不允许上传后缀' +fileSuffix)
+			spinning.value = false
+			return false
+		}
+		if(uploadFileList.value.length >= props.uploadCount){
+			message.error('超过上传条目' + props.uploadCount + "条")
+			spinning.value = false
+			return false
+		}
+		let upFile = await handleFileChange({ raw: file })
+		console.log('可以上传的文件内容是', upFile)
+		// 检查本地 uploadFileList 是否已存在该 md5 文件
+		const exists = uploadFileList.value.some((item) => item.md5 === upFile.md5)
+		if (exists) {
+			message.warning('该文件已存在,不再重复添加')
+			spinning.value = false
+			return false
+		}
+		spinning.value = false
+		// await checkMd5List(upFile)
+		uploadFileList.value.push(upFile)
+		// emit('onSuccess', uploadFileList.value)
+		// const activeTasks = Object.keys(uploadingTasks.value).filter((key) => uploadingTasks.value[key])
+		//
+		// if (activeTasks.length < 2) {
+		// 	pauseFlags.value[upFile.md5] = false
+		// 	uploadingTasks.value[upFile.md5] = true
+		// } else {
+		// 	pauseFlags.value[upFile.md5] = true
+		// 	message.warning('上传队列已满,将进入暂停状态')
+		// }
+		// await uploadSingleFile(upFile)
+		// spinning.value = false
+		return false // 阻止默认上传
+	}
+	const handleChange = (info) => {
+		const { file } = info
+		if (file.status === 'removed') {
+			fileList.value = []
+			clearFileList()
+		}
+	}
+
+	const uploadSingleFile = async (fileObj) => {
+		const file = fileObj
+		const md5 = file.md5
+		const index = uploadFileList.value.findIndex((item) => item.md5 === md5)
+
+		if (index === -1) return
+
+		const item = uploadFileList.value[index]
+
+		if (!item || item.userFileId) return
+
+		// // 如果是暂停状态则不执行上传
+		// while (pauseFlags.value[md5]) {
+		// 	await new Promise((resolve) => setTimeout(resolve, 500))
+		// }
+		// 添加到正在上传任务中
+		uploadingTasks.value[md5] = true
+		file.startTime = new Date().getTime()
+		file.uploadedSize = 0
+
+		const chunkPromises = []
+
+		for (let i = 0; i < item.chunks.length; i++) {
+			let chunk = item.chunks[i]
+
+			while (pauseFlags.value[md5]) {
+				await new Promise((resolve) => setTimeout(resolve, 500))
+			}
+
+			chunkPromises.push(
+				await uploadFilesChunk(
+					{
+						md5,
+						chunk,
+						chunkIndex: i + 1,
+						fileSuffix: item.fileSuffix,
+						chunkSize: item.chunks.length,
+						fileName: item.name
+					},
+					() => {
+						// chunksUploaded.value++
+						// uploadChunks.value++
+
+						// successfulChunkPercents.value = 100 * (uploadChunks.value / allChunks.value).toFixed(2)
+						item.uploadedSize += chunk.size // 更新已上传大小
+						const remainingTime = estimateRemainingTime(item.startTime, item.uploadedSize, item.size)
+						console.log(`预计剩余时间: ${formatTime(remainingTime)}`)
+						// item.percents = (100 * (uploadChunks.value / item.chunks.length)).toFixed(2)
+						item.time = formatTime(remainingTime)
+
+						const percent = ((i + 1) / item.chunks.length) * 100
+						item.percents = percent.toFixed(2)
+						console.log(`我得名字: `, item.name, ' i ', i, ' item.chunks.length ', item.chunks.length)
+						// item.time = formatTime(estimateRemainingTime())
+					}
+				)
+			)
+		}
+
+		await Promise.all(chunkPromises)
+
+		// 合并分片
+		const mergeResult = await axios.post(
+			sysConfig.API_URL+`/api/webapp/minio/merge?md5=${md5}&fileSuffix=${item.fileSuffix}&chunkTotal=${item.chunks.length}&fileName=${item.name}&fileSize=${item.size}`,
+			null,
+			{ headers: { Token: tool.data.get('TOKEN') } }
+		)
+
+		uploadFileList.value[index].userFileId = mergeResult.data.userFileId
+		uploadFileList.value[index].percents = 100
+
+		uploadingTasks.value[item.md5] = false
+		// item.time = '上传完成'
+		// 尝试恢复一个被暂停的任务
+		autoResumePausedUpload()
+		// upLoadTag.value = false
+		emit('onSuccess', uploadFileList.value)
+	}
+
+	// watch(
+	// 	() => uploadFileList.value,
+	// 	(newValue) => {
+	// 		emit('onSuccess', newValue)
+	// 	},
+	// 	{ immediate: true }
+	// )
+	const autoResumePausedUpload = async () => {
+		const activeTasks = Object.keys(uploadingTasks.value).filter((key) => uploadingTasks.value[key])
+		if (activeTasks.length >= 2) return
+
+		// 查找第一个被暂停的文件
+		for (let i = 0; i < uploadFileList.value.length; i++) {
+			const item = uploadFileList.value[i]
+			if (item.percents > 0 && item.percents < 100 && pauseFlags.value[item.md5] === true) {
+				console.log(`自动恢复 ${item.name}`)
+				pauseFlags.value[item.md5] = false
+				uploadingTasks.value[item.md5] = true
+				await uploadSingleFile(item)
+				break
+			}
+		}
+	}
+	const customRequest = () => {}
+	const getList = () => {
+		axios
+			.get('http://127.0.0.1:9000/api/webapp/disk/minio/list')
+			.then((res) => {
+				this.uploadList = res.data
+				console.log('获取列表结果成功:', res.data)
+			})
+			.catch((error) => {
+				console.error('获取列表失败:', error)
+			})
+	}
+
+	const clearFileList = () => {
+		// successfulChunkPercents.value = 0
+		uploadFileList.value = []
+		// fileList.value = []
+	}
+	const open = () => {
+
+		clearFileList()
+	}
+
+onMounted(() => {
+	nextTick(() => {
+		// if ( fileUpload.value && fileUpload.value.$el.querySelector('input')) {
+		// 	fileUpload.value.$el.querySelector('input').webkitdirectory = true;
+		// }
+	});
+});
+onUpdated(() => {
+	console.log('组件已更新')
+})
+
+const getUploadFileList = () => {
+		return uploadFileList.value
+}
+
+	defineExpose({open, getUploadFileList})
+</script>
+
+<style scoped lang="less">
+/*大力水手*/
+.mydiv {
+	:deep(.ant-upload-btn) {
+		display: block !important;
+	}
+}
+
+
+</style>

+ 662 - 0
src/components/MiniYun/uploadIng.vue

@@ -0,0 +1,662 @@
+<template>
+	<!-- 上传资源模态框 -->
+	<a-modal
+		v-model:visible="uploadModalVisible"
+		:title="isState == 0 ? '上传资源' : '编辑'"
+
+		width="600px"
+	>
+		<a-form :model="formState" :rules="rules" ref="formRef">
+			<a-form-item label="单位" name="collegeId" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-cascader
+					v-model:value="majorIdName"
+					:options="collegeMajorOptions"
+					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
+					placeholder="请选择单位"
+					style="width: 300px"
+					changeOnSelect
+					@change="changeCollegeMajor"
+				/>
+			</a-form-item>
+
+			<a-form-item label="资源类型" name="resourceType" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-cascader
+					style="width: 240px; margin-left: 8px"
+					v-model:value="resourceName"
+					:options="resourceTypeOptions"
+					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
+					placeholder="请选择资源类型"
+					changeOnSelect
+					@change="changeCollegeResource"
+				/>
+			</a-form-item>
+			<a-form-item
+				label="关键词(需添加2-5个关键词)"
+				name="keywordValue"
+				:label-col="{ span: 10 }"
+				:wrapper-col="{ span: 18 }"
+			>
+				<div v-if="!formState.keywordValue.length" style="color: #ccc">请选择下方热门关键词</div>
+				<div v-if="formState.keywordValue.length">
+					<a-tag
+						v-for="(keyword, index) in formState.keywordValue"
+						:key="index"
+
+						@close="handleRemoveKeyword(keyword, index)"
+					>
+						{{ keyword }}
+					</a-tag>
+				</div>
+			</a-form-item>
+			<a-form-item label="热门关键词" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-checkbox-group
+					v-model:value="formState.keyword"
+					:options="HotKeywordsOptions"
+					@change="handleChangeKeyword"
+				/>
+			</a-form-item>
+			<a-row>
+				<a-col :span="5"></a-col>
+				<a-col :span="8">
+					<a-input v-model:value="newKeyword" placeholder="请输入关键词" @pressEnter="handleAddKeyword" />
+				</a-col>
+				<a-col :span="9" style="line-height: 30px"> 按回车Enter创建新关键词 </a-col>
+			</a-row>
+			<a-form-item label="资源是否公开" style="margin-top: 10px" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<div class="public-status-buttons">
+					<button :class="['status-button', { active: formState.authType === '0' }]" @click="setPublicStatus('0')">
+						公开
+					</button>
+					<button :class="['status-button', { active: formState.authType === '1' }]" @click="setPublicStatus('1')">
+						部分人可见
+					</button>
+				</div>
+			</a-form-item>
+			<a-form-item label="是否热门" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-radio-group v-model:value="formState.isHot">
+					<a-radio :value="0">否</a-radio>
+					<a-radio :value="1">是</a-radio>
+				</a-radio-group>
+			</a-form-item>
+			<a-form-item label="是否推荐" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+				<a-radio-group v-model:value="formState.isRecommend">
+					<a-radio :value="0">否</a-radio>
+					<a-radio :value="1">是</a-radio>
+				</a-radio-group>
+			</a-form-item>
+
+
+			<!-- 选择成员 -->
+			<userSelection
+				:visible="userReleaseVisible"
+				:userRelateIds="userRelateIdss"
+				@close="userReleaseVisible = false"
+				@confirm="confirmUser"
+			></userSelection>
+		</a-form>
+		<template v-if="isState == 0">
+			<!-- 资源上传 -->
+			<!-- <UploadModal @success="uploadSuccess"></UploadModal> -->
+<!--			<UpLoadBreakPoint ref="upLoadBreakPointRef" @onSuccess="onSuccess"></UpLoadBreakPoint>-->
+		</template>
+	</a-modal>
+</template>
+
+<script setup>
+	import { ref, reactive, onMounted } from 'vue'
+	import { Modal, Upload, Form } from 'ant-design-vue'
+	import resourceAuditApi from '@/api/resourceAudit.js'
+	import userSelection from './userSelection.vue'
+	import UpLoadBreakPoint from './upLoadBreakPoint.vue'
+	import tool from '@/utils/tool'
+	import sysConfig from "@/config";
+
+	const { proxy } = getCurrentInstance()
+
+	const props = defineProps({
+		isState: {
+			type: Number,
+			required: true,
+			default: 0
+		},
+		isVerifyStatus: {
+			type: Number,
+			required: true,
+			default: 0
+		},
+		resourcesId: {
+			type: Number,
+			required: true,
+			default: null
+		}
+	})
+	const emit = defineEmits(['close', 'getList'])
+
+	const headers = ref({
+		token: tool.data.get('TOKEN')
+	})
+	//课程类型
+	const courseTypeOptions = tool.dictList('COURSE_TYPE')
+	const action = ref(sysConfig.API_URL+'/api/webapp/dev/file/uploadMinioReturnId')
+	const formState = reactive({
+		userfileIds: null, //资源文件id
+		coverImage: undefined, //封面id
+		// courseId: null, //课程
+		// majorType: null, //专业
+		resourceType: null, //资源类型一级
+		resourceTwoType: null, //资源类型二级
+		resourceThreeType: null, //资源类型二级
+		collegeId: null, //院校一级id
+		collegeTwoId: null, //院校二级id
+		collegeThreeId: null, //院校三级id
+		resourceDesc: null, //资源介绍
+		// majorId: null, //专业id
+		// courseTypeName: [], // 资源类型
+		keywordValue: [], // 添加关键词
+		keyword: [], // 热门关键词
+		authType: '0', // 资源是否公开
+		userRelateIds: null, //资源公开人员id
+		isRecommend: 0, // 资源是否推荐
+		isHot: 0 // 资源是否热门
+	})
+	import { miniyunStore } from '@/store/miniyun'
+	const myMiniyunStore = miniyunStore()
+	const upLoadBreakPointRef = ref(null) // 预览回显
+	const coverImagePath = ref() // 预览回显
+	const formRef = ref() // 添加表单引用
+	const collegeMajorOptions = ref([]) //院系
+	const resourceTypeOptions = ref([]) //资源类型下拉数据
+	const HotKeywordsOptions = ref([]) //热门关键词列表
+	const majorIdName = ref([]) //院系回显
+	const resourceName = ref([]) //资源回显
+	const majorOptions = ref([]) //专业
+	const courseOptions = ref([]) //课程
+	const userRelateIdss = ref([])
+	const myHot = ref({})
+	// 上传资源模态框
+	const uploadModalVisible = ref(false)
+	// 用户选择模态框
+	const userReleaseVisible = ref(false)
+
+	// 文件列表
+	const fileList = ref([])
+
+	const open = () =>{
+		uploadModalVisible.value = true
+
+		getOrgTreeSelector()
+		getHotKeywords()
+		getResourceTypeTree()
+		getformState()
+		// if (props.isState == 1) {
+		// 	getDetail()
+		// } else {
+		// 	getformState()
+		// }
+
+		nextTick(()=>{
+			upLoadBreakPointRef.value.open()
+		})
+
+	}
+
+	// 关闭模态框
+	const handleUploadCancel = () => {
+		emit('close')
+		fileList.value = []
+	}
+	// 新增Set记录已处理ID
+	// const processedIds = ref(new Set())
+	//资源文件上传成功返回
+	const uploadSuccess = async (idsArr) => {
+		formState.userfileIds = idsArr.join(',')
+		// const newIds = idsArr.filter((id) => !processedIds.value.has(id))
+		// if (newIds.length === 0) return
+		// //文件转换
+		// try {
+		// 	const requests = newIds.map((id) => resourceAuditApi.fileFormatConversion({ userFileId: id }))
+		// 	await Promise.all(requests)
+		// 	newIds.forEach((id) => processedIds.value.add(id))
+		// 	newKeyword.value = ''
+		// } catch (err) {
+		// 	console.error('部分请求失败:', err)
+		// }
+	}
+	const onSuccess = (uploadFileList) => {
+		let list = []
+		console.log('formState.userfileIds是数组:', uploadFileList)
+		for (let i = 0; i < uploadFileList.length; i++) {
+			if (uploadFileList[i].userFileId) {
+				list.push(uploadFileList[i].userFileId)
+			}
+		}
+		console.log('formState.userfileIds是:', list)
+		formState.userfileIds = list.join(',')
+	}
+
+	// 自定义校验函数示例
+	const validateKeywords = (rule, value, callback) => {
+		if (value.length < 2) {
+			callback(new Error('至少需要添加2个关键词'))
+		} else if (value.length > 5) {
+			callback(new Error('最多只能添加5个关键词'))
+		} else {
+			callback()
+		}
+	}
+	// 封面文件id
+	const handleChangeCover = (fileId) => {
+		formState.coverImage = fileId
+	}
+
+	// 移除封面文件
+	const handleRemoveCover = () => {
+		formState.coverImage = undefined
+	}
+	const rules = {
+		// courseTypeName: [{ required: true, message: '请选择资源类型', trigger: 'change' }],
+		collegeId: [{ required: true, message: '请选择单位', trigger: 'blur' }],
+		keywordValue: [
+			{ required: true, message: '请选择关键词', trigger: 'blur' },
+			{ validator: validateKeywords, trigger: 'blur' }
+		],
+		// coverImage: [{ required: true, message: '请上传封面', trigger: 'blur' }],
+		resourceType: [{ required: true, message: '请选择资源类型', trigger: 'blur' }],
+		resourceDesc: [
+			{ required: true, message: '请输入资源描述', trigger: 'blur' },
+			{ pattern: /^[\s\S]{10,500}$/, message: '描述长度应在10-500字符之间', trigger: 'blur' }
+		],
+		coverImage: [{ required: true, message: '请上传封面', trigger: 'blur' }],
+	}
+	const newKeyword = ref('') //关键词
+	const handleAddKeyword = (e) => {
+		const newKeywords = newKeyword.value.trim()
+		resourceAuditApi
+			.addHotKeywords({
+				wordName: newKeywords,
+				popular: 0
+			})
+			.then((res) => {
+				console.log(res.data, '添加热门关键词')
+				getHotKeywords()
+				newKeyword.value = ''
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+		// if (newKeywords && !formState.keywordValue.includes(newKeywords)) {
+		// 	formState.keywordValue.push(newKeywords)
+		// }
+	}
+
+	const handleRemoveKeyword = (keywordName, index) => {
+
+		console.log('删除的东西 01 ', ' keywordName ',keywordName,' index ',index)
+		console.log('删除的东西 02 ', ' formState.keyword ',formState.keyword)
+
+
+		console.log(keywordName, index,myHot.value)
+		// // 1. 从显示列表中删除
+		// formState.keywordValue = formState.keywordValue.filter((name) => name !== keywordName)
+		// // console.log('看看点击取消了什么',' formState.keywordValue ',formState.keywordValue)
+		// // formState.keyword
+		// // 2. 更新checkbox的绑定值
+		// formState.keyword = HotKeywordsOptions.value
+			// .filter((option) => formState.keywordValue.includes(option.label))
+			// .map((item) => item.value)
+		// // formState.keyword = formState.keyword.filter((item, i) => i !== index)
+		// // console.log('看看点123123',' formState.keyword ',formState.keyword,' index ',index)
+
+		let myId = myHot.value[keywordName]
+		// console.log('换算出来',formState.keyword,index)
+
+		// for (let i = formState.keyword.length-1; i > 0 ; i--) {
+		// 	// console.log('看看',formState.keyword[i] , i , index)
+		// 	console.log('要删除第几个' ,i, index ,  formState.keyword[i])
+		// 	if( i == index){
+		//
+		// 		// formState.keyword.splice(i,1)
+		// 	}
+		// }
+		// console.log('换算出来  结果',formState.keyword)
+
+		// formState.keyword.filter((item, i) => item != keywordName)
+		// formState.keyword = [{'专业' : 1}]
+		// formState.keyword.filter((item, i) => item != index)
+		// formState.keyword.filter((option) => formState.keywordValue.includes(option.value))
+		// 	.map((item) => item.label)
+		// console.log('换算出来 剩下',formState.keyword)
+		// console.log('还有吗',formState.keywordValue)
+
+	}
+	const getHotKeywords = () => {
+		myHot.value = {}
+		resourceAuditApi
+			.HotKeywords()
+			.then((res) => {
+				console.log(res.data, '获取热门关键词')
+				HotKeywordsOptions.value = res.data.map((it) => {
+					myHot.value[it.wordName] = it.id
+					return {
+						value: it.id,
+						label: it.wordName
+					}
+				})
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	//资源类型下拉查询
+	const getResourceTypeTree = () => {
+		resourceAuditApi
+			.resourceTypeTree()
+			.then((res) => {
+				console.log(res.data, '资源类型下拉')
+				resourceTypeOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	const handleChangeKeyword = (checkedValues) => {
+		formState.keyword = checkedValues
+		console.log('点击checkbox',checkedValues )
+		formState.keywordValue = HotKeywordsOptions.value
+			.filter((option) => checkedValues.includes(option.value))
+			.map((item) => item.label)
+		console.log('点击checkbox  123 ',formState.keywordValue )
+	}
+	const setPublicStatus = (status) => {
+		formState.authType = status
+		if (status === '1') {
+			userReleaseVisible.value = true
+		}
+	}
+	//院系组织查询
+	const getOrgTreeSelector = () => {
+		resourceAuditApi
+			.orgTreeSelector()
+			.then((res) => {
+				console.log(res.data, '获取组织树选择器')
+				collegeMajorOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	//院系组织查询
+	const changeCollegeMajor = (value, selectedOptions) => {
+		console.log('Selected:', value, selectedOptions)
+		if (!value) {
+			majorIdName.value = ''
+			return false
+		}
+		majorIdName.value = selectedOptions.map((it) => it.name).join('/')
+		formState.collegeId = value[0] || null
+		formState.collegeTwoId = value[1] || null
+		formState.collegeThreeId = value[2] || null
+		if (selectedOptions.length) {
+			// 获取选中的最后一级
+			const lastSelected = selectedOptions[selectedOptions.length - 1]
+			// formState.selectedCollegeMajor = {
+			// 	id: lastSelected.id,
+			// 	name: lastSelected.name,
+			// 	fullPath: selectedOptions.map((opt) => opt.name).join(' / ')
+			// }
+			console.log(lastSelected, '最后一级id')
+			getCollegeMajor(lastSelected.id)
+		}
+	}
+	const changeCollegeResource = (value, selectedOptions) => {
+		console.log('Selected:', value, selectedOptions)
+		if (!value) {
+			resourceName.value = ''
+			return false
+		}
+		resourceName.value = selectedOptions.map((it) => it.name).join('/')
+		formState.resourceType = value[0] || undefined
+		formState.resourceTwoType = value[1] || undefined
+		formState.resourceThreeType = value[2] || undefined
+	}
+	const getCollegeMajor = (id) => {
+		resourceAuditApi
+			.zyselect({ collegeId: id })
+			.then((res) => {
+				console.log(res.data, '专业下拉数据')
+				majorOptions.value = res.data
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+	const confirmUser = (userIds) => {
+		console.log(userIds, '用户id')
+		userReleaseVisible.value = false
+		formState.userRelateIds = userIds.join(',')
+	}
+	// 确认上传
+	const handleUploadOk = async () => {
+		try {
+			await formRef.value.validate()
+			// console.log('formState.userfileIds是:提交了', formState.userfileIds)
+			// if (!formState.userfileIds) {
+			// 	Modal.error({ content: '请先上传文件!!' })
+			// 	return
+			// }
+			if (upLoadBreakPointRef.value.getUploadFileList().length == 0) {
+				Modal.error({ content: '请先上传文件!!' })
+				return
+			}
+			const formData = {
+				// userfileIds: formState.userfileIds,
+				resourceType: formState.resourceType,
+				resourceTwoType: formState.resourceTwoType,
+				resourceThreeType: formState.resourceThreeType,
+				collegeId: formState.collegeId,
+				collegeTwoId: formState.collegeTwoId,
+				collegeThreeId: formState.collegeThreeId,
+				keywordValue: formState.keywordValue.join(','),
+				keyword: formState.keyword.join(','),
+				resourceDesc: formState.resourceDesc,
+				isHot: formState.isHot,
+				isRecommend: formState.isRecommend,
+				coverImage: formState.coverImage,
+				publicStatus: formState.publicStatus,
+				authType: formState.authType,
+				userRelateIds: formState.authType == 1 ? formState.userRelateIds : null,
+				uploadFileList : upLoadBreakPointRef.value.getUploadFileList()
+			}
+			console.log('上传数据',formData )
+			myMiniyunStore.addFileForms(formData)
+				// resourceAuditApi
+				// 	.add(formData)
+				// 	.then((res) => {
+				// 		emit('getList')
+				// 		Modal.success({ content: '资源上传成功' })
+				// 	})
+				// 	.catch((err) => {
+				// 		Modal.success({ content: '资源上传失败' })
+				// 		console.log(err)
+				// 	})
+		} catch (error) {
+			console.error('错误信息',error)
+			if (error.errorFields) {
+				// 表单验证错误
+				Modal.error({ content: '请检查表单填写是否正确' })
+			} else {
+				// API错误
+				Modal.error({ content: '资源上传失败' })
+				console.error(error)
+			}
+		}
+	}
+	// 获取资源详情
+	const getDetail = () => {
+		resourceAuditApi.detail({ id: props.resourcesId }).then((res) => {
+			console.log(res.data, '资源详情')
+			formState.userfileIds = res.data.fileId
+			formState.userRelateIds = res.data.userRelateIdList?.join(',')
+			userRelateIdss.value = res.data.userRelateIdList
+			formState.resourceType = res.data.resourceType
+			formState.resourceTwoType = res.data.resourceTwoType
+			formState.resourceThreeType = res.data.resourceThreeType
+			formState.collegeId = res.data.collegeId
+			formState.coverImage = res.data.coverImage
+			coverImagePath.value = res.data.coverImagePath
+			formState.collegeTwoId = res.data.collegeTwoId
+			formState.collegeThreeId = res.data.collegeThreeId
+			majorIdName.value = res.data.collegeAllId?.split(',')
+			resourceName.value = res.data.resourceALLTypeName?.split(',')
+			getCollegeMajor(majorIdName.value[majorIdName.value.length - 1])
+			formState.resourceDesc = res.data.resourceDesc
+			// formState.majorId = res.data.majorId
+			formState.keywordValue = res.data.keywordValue?.split(',')
+			formState.keyword = res.data.keyword.split(',').filter(Boolean).map(Number)
+
+			formState.isRecommend = Number(res.data.isRecommend) || 0
+			formState.isHot = Number(res.data.isHot) || 0
+			formState.authType = res.data.authType
+		})
+	}
+	// 获取历史添加表单
+	const getformState = () => {
+		resourceAuditApi.recentlyRecord().then((res) => {
+			console.log(res.data, '历史表单数据')
+			formState.resourceType = res.data.resourceType
+			formState.userRelateIds = res.data.userRelateIdList?.join(',')
+			userRelateIdss.value = res.data.userRelateIdList
+			formState.resourceTwoType = res.data.resourceTwoType
+			formState.resourceThreeType = res.data.resourceThreeType
+			formState.collegeId = res.data.collegeId
+			formState.collegeTwoId = res.data.collegeTwoId
+			formState.collegeThreeId = res.data.collegeThreeId
+			majorIdName.value = res.data.collegeAllId?.split(',')
+			resourceName.value = res.data.resourceAllType?.split(',')
+			getCollegeMajor(majorIdName.value[majorIdName.value.length - 1])
+			formState.resourceDesc = res.data.resourceDesc
+			// formState.majorId = res.data.majorId
+			formState.keywordValue = res.data.keywordValue?.split(',')
+			formState.keyword = res.data.keyword?.split(',').filter(Boolean).map(Number)
+		})
+	}
+	// 上传前的钩子函数
+	const beforeUpload = (file) => {
+		const isLt2G = file.size / 1024 / 1024 / 1024 < 2
+		if (!isLt2G) {
+			Modal.error({ content: '文件大小不能超过 2GB!' })
+		}
+		return isLt2G
+	}
+
+	// 移除文件
+	const handleRemove = (file) => {
+		const index = fileList.value.indexOf(file)
+		const newFileList = fileList.value.slice()
+		newFileList.splice(index, 1)
+		fileList.value = newFileList
+		// 如果移除的是当前封面文件,则清空coverImageId
+		if (formState.userfileIds === file.id) {
+			formState.userfileIds = null
+		}
+	}
+
+	// 文件状态改变时的处理函数
+	const handleChange = ({ file, fileList: newFileList }) => {
+		if (newFileList.length > 1) {
+			fileList.value = [newFileList[0]] // 只保留最新上传的文件
+			Modal.error({ content: '只能上传一个文件!' })
+			return
+		}
+
+		if (file.response?.code == 200) {
+			// 上传成功,获取文件ID
+			const fileId = file.response?.data || file.id
+			console.log('上传成功,获取文件ID', fileId)
+			if (fileId) {
+				formState.userfileIds = fileId
+			}
+		}
+		fileList.value = newFileList
+		if (file.status === 'uploading') {
+			file.percent = Math.floor(file.percent)
+		}
+		if (file.response?.code == 200) {
+			file.percent = 100
+			Modal.success({ content: '文件上传成功' })
+		} else if (file.response?.code == 500) {
+			Modal.error({ content: '文件上传失败' })
+			file.percent = 0
+		}
+	}
+	onMounted(() => {
+		// getOrgTreeSelector()
+		// getHotKeywords()
+		// getResourceTypeTree()
+		// if (props.isState == 1) {
+		// 	getDetail()
+		// } else {
+		// 	getformState()
+		// }
+	})
+
+	defineExpose({
+		open
+	})
+</script>
+<style scoped>
+	.upload-area {
+		border: 2px dashed #3ca9f5;
+		padding: 40px;
+		text-align: center;
+	}
+
+	.upload-area p {
+		margin: 10px 0;
+	}
+
+	.file-item {
+		display: flex;
+		align-items: center;
+		margin: 10px 0;
+	}
+
+	.file-item .ant-progress {
+		flex: 1;
+		margin: 0 10px;
+	}
+
+	/* 新增表单样式 */
+	.ant-form-item {
+		margin-bottom: 16px;
+	}
+	.public-status-buttons {
+		display: flex;
+	}
+
+	.status-button {
+		padding: 5px 10px;
+		/* margin-right: 10px; */
+		border: 1px solid #ccc;
+		/* border-radius: 3px; */
+		cursor: pointer;
+		background-color: #fff;
+	}
+
+	.status-button.active {
+		background-color: #40a9ff;
+		color: #fff;
+		border-color: #40a9ff;
+	}
+	.upload-area {
+		border: 2px dashed #3ca9f5;
+		padding: 40px;
+		text-align: center;
+		transition: border-color 0.3s; /* 平滑过渡效果 */
+	}
+
+	.upload-area.drag-over {
+		border-color: #1890ff;
+		background-color: rgba(24, 144, 255, 0.05);
+	}
+</style>

+ 300 - 0
src/components/MiniYun/userSelection.vue

@@ -0,0 +1,300 @@
+<template>
+	<a-drawer
+		v-model:visible="props.visible"
+		title="选择可见成员"
+		placement="right"
+		width="50%"
+		:footer="null"
+		@close="handleCancel"
+	>
+		<!-- 左侧:树状结构成员列表 -->
+		<div class="left-panel">
+			<a-input-search
+				v-model:value="searchValue"
+				placeholder="输入部门或成员名称"
+				style="margin-bottom: 16px"
+				@search="onSearch"
+			/>
+			<a-tree
+				v-if="treeData.length > 0"
+				:tree-data="filteredTreeData"
+				:field-names="{ key: 'id', title: 'name', children: 'children' }"
+				:checked-keys="checkedKeys"
+				:expanded-keys="expandedKeys"
+				:auto-expand-parent="autoExpandParent"
+				checkable
+				show-icon
+				@check="onCheck"
+				@expand="onExpand"
+			>
+				<template #title="{ name, avatar }">
+					<span style="display: inline-flex; align-items: center">
+						<a-avatar :src="avatar" size="small" style="margin-right: 8px" />
+						{{ name }}
+					</span>
+				</template>
+				<template #switcherIcon="{ expanded }">
+					<caret-down-outlined v-if="expanded" />
+					<caret-right-outlined v-else />
+				</template>
+			</a-tree>
+		</div>
+
+		<!-- 右侧:已选择成员列表 -->
+		<div class="right-panel">
+			<div class="header">
+				<span>已选 {{ selectedUsers.length }} / 30</span>
+				<a-button type="link" @click="clearSelection">清空</a-button>
+			</div>
+			<a-list item-layout="horizontal" :data-source="selectedUsers">
+				<template #renderItem="{ item }">
+					<a-list-item>
+						<a-list-item-meta>
+							<template #avatar>
+								<a-avatar :src="item.avatar" />
+							</template>
+							<template #title>
+								<a>{{ item.name }}</a>
+							</template>
+							<template #description>
+								<span>{{ item.department }}</span>
+							</template>
+						</a-list-item-meta>
+						<template #actions>
+							<a @click="removeUser(item)">删除</a>
+						</template>
+					</a-list-item>
+				</template>
+			</a-list>
+		</div>
+
+		<!-- 底部按钮 -->
+		<template #footer>
+			<a-space>
+				<a-button @click="handleCancel">取消</a-button>
+				<a-button type="primary" @click="handleOk">确定</a-button>
+			</a-space>
+		</template>
+	</a-drawer>
+</template>
+
+<script setup>
+	import { ref, reactive, computed, onMounted } from 'vue'
+	import resourceAuditApi from '@/api/resourceAudit.js'
+	import { Modal, Input, Tree, List, Avatar, Button } from 'ant-design-vue'
+	const emit = defineEmits(['close', 'confirm'])
+	// const visible = ref(true)
+	const props = defineProps({
+		visible: {
+			type: Boolean,
+			default: false
+		},
+		userRelateIds: {
+			type: Array,
+			default: () => {}
+		}
+	})
+
+	const searchValue = ref('')
+	const treeData = ref([
+		{
+			id: '1',
+			name: '组织1',
+			children: [
+				{
+					id: '1-1',
+					name: '张小刚',
+					avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
+					isLeaf: true
+				},
+				{
+					id: '1-2',
+					name: '李小红',
+					avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
+					isLeaf: true
+				}
+			]
+		},
+		{
+			id: '2',
+			name: '组织2',
+			children: [
+				{
+					id: '2-1',
+					name: '研发部',
+					children: [
+						{
+							id: '2-1-1',
+							name: '王小明',
+							avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
+							isLeaf: true
+						}
+					]
+				}
+			]
+		}
+	])
+	watch(
+		() => props.userRelateIds,
+		(newVal) => {
+			if (newVal) {
+				console.log(newVal, 'props.userRelateIds')
+				checkedKeys.value = newVal
+				// selectedUsers.value = flatTree(treeData.value)
+				// 	.filter((node) => newVal.includes(node.id))
+				// 	.map((node) => ({ id: node.id, name: node.name }))
+			}
+		},
+		{ deep: true }
+	)
+	const selectedKeys = ref([])
+	const selectedUsers = ref([])
+	const checkedKeys = ref([])
+	const expandedKeys = ref([]) // 默认展开第一层
+	const autoExpandParent = ref(true)
+	const filteredTreeData = computed(() => {
+		const filterFn = (node) => {
+			// 保留匹配节点及其所有祖先节点
+			if (node.name.includes(searchValue.value)) return true
+			if (node.children) {
+				const hasMatchingChild = node.children.some(filterFn)
+				if (hasMatchingChild) return true
+			}
+			return false
+		}
+
+		return treeData.value.filter(filterFn)
+	})
+
+	// 替换原来的onSelect方法
+	const onCheck = (checkedKeysValue, { checked, node, checkedNodes }) => {
+		// 过滤掉非叶子节点
+		const leafNodes = checkedNodes.filter((node) => node.isLeaf&& node.infoType=="user")
+		checkedKeys.value = leafNodes.map((node) => node.id)
+		selectedUsers.value = leafNodes.map((node) => ({
+			id: node.id,
+			name: node.name
+			// 可选: 保留部门信息
+			// department: findDepartmentName(node.id, treeData.value)
+		}))
+	}
+	// 查找部门名称的辅助函数
+	const findDepartmentName = (id, nodes) => {
+		for (const node of nodes) {
+			if (node.children) {
+				const found = node.children.find((child) => child.id === id)
+				if (found) return node.name
+				const result = findDepartmentName(id, node.children)
+				if (result) return result
+			}
+		}
+		return ''
+	}
+	const onExpand = (keys) => {
+		// console.log(keys, 'onExpand')
+		expandedKeys.value = keys
+		autoExpandParent.value = false
+	}
+
+	const onSearch = (value) => {
+		searchValue.value = value
+	}
+	const augmentNode = (node) => {
+		if (node.children) {
+			node.isLeaf = false // 有children的节点标记为非叶子节点
+			node.children.forEach((child) => augmentNode(child))
+		} else {
+			node.isLeaf = true // 无children的节点标记为叶子节点
+		}
+	}
+	const getOrgUserTreeRespectively = () => {
+		resourceAuditApi
+			.orgUserTreeSelector()
+			.then((res) => {
+				if (res?.data) {
+					console.log(res.data, 'getOrgUserTreeRespectively')
+					res.data.forEach((root) => augmentNode(root))
+					// if (treeData.value.length > 0 && treeData.value[0]?.id) {
+					// 	expandedKeys.value = [treeData.value[0].id]
+					// }
+					treeData.value = res.data
+				}
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+
+	const clearSelection = () => {
+		selectedKeys.value = []
+		selectedUsers.value = []
+		checkedKeys.value = []
+	}
+
+	const removeUser = (user) => {
+		const index = selectedUsers.value.findIndex((u) => u.id === user.id)
+		if (index !== -1) {
+			selectedUsers.value.splice(index, 1)
+			selectedKeys.value = selectedUsers.value.map((u) => u.id)
+			checkedKeys.value = selectedUsers.value.map((u) => u.id)
+		}
+		// selectedUsers.value = selectedUsers.value.filter((u) => u.id !== user.id)
+		// checkedKeys.value = selectedUsers.value.map((u) => u.id)
+	}
+
+	const showModal = () => {
+		visible.value = true
+	}
+
+	const handleOk = () => {
+		console.log('Selected Users:', selectedUsers.value, checkedKeys.value)
+		emit('confirm', checkedKeys.value)
+	}
+
+	const handleCancel = () => {
+		emit('close')
+	}
+	onMounted(() => {
+		getOrgUserTreeRespectively()
+		// if (props.userRelateIds) {
+		// 	checkedKeys.value = props.userRelateIds // 直接赋值给树组件的checkedKeys
+		// }
+	})
+</script>
+
+<style scoped>
+	.left-panel,
+	.right-panel {
+		display: inline-block;
+		vertical-align: top;
+		width: 48%;
+		height: calc(100vh - 200px);
+		overflow-y: auto;
+		padding: 0 10px;
+	}
+
+	.right-panel {
+		border-left: 1px solid #f0f0f0;
+	}
+
+	.header {
+		display: flex;
+		justify-content: space-between;
+		margin-bottom: 16px;
+	}
+
+	.ant-tree-switcher {
+		width: 24px;
+		height: 24px;
+		line-height: 24px;
+	}
+
+	.ant-tree-switcher-icon {
+		font-size: 12px;
+		transition: transform 0.3s;
+	}
+
+	.ant-tree-switcher_close .ant-tree-switcher-icon {
+		transform: rotate(-90deg);
+	}
+</style>

+ 1 - 1
src/router/student.js

@@ -105,7 +105,7 @@ const routes = [
 				path: 'link',
 				component: () => import('@/views/student/link/index.vue'),
 				meta: {
-					title: '超链接'
+					title: '教师资源超链接'
 				}
 			},
 			{

+ 4 - 2
src/store/global.js

@@ -1,4 +1,4 @@
- 
+
 import { defineStore } from 'pinia'
 import { changeColor } from '@/utils/themeUtil'
 import config from '@/config'
@@ -55,7 +55,9 @@ export const globalStore = defineStore({
 		// 默认应用
 		module: getCacheConfig('SNOWY_MENU_MODULE_ID')
 	}),
-	getters: {},
+	getters: {
+		getUserInfo: (state) => state.userInfo,
+	},
 	actions: {
 		setIsMobile(key) {
 			this.isMobile = key

+ 374 - 0
src/store/miniyun.js

@@ -0,0 +1,374 @@
+import {defineStore} from 'pinia'
+
+import {ref, onMounted} from 'vue'
+import axios from 'axios'
+import SparkMD5 from 'spark-md5'
+import tool from '@/utils/tool'
+import {message} from 'ant-design-vue'
+import sysConfig from '@/config/index'
+import resourceAuditApi from '@/api/resourceAudit.js'
+import EventBus from "@/utils/EventBus";
+export const miniyunStore = defineStore({
+	id: 'miniyun',
+	state: () => ({
+		pauseFlags: {}, // 控制每个文件是否暂停 { md5: true/false }
+		uploadingTasks: {}, // 正在上传的任务 { md5: true }
+		checkingFiles: new Set(), // 正在检查的文件MD5
+		checkedFiles: new Map(), // 已检查的文件结果缓存
+		//当前选中的文件
+		currentFile: null,
+		spinning: false,
+		chunkSize: 5 * 1024 * 1024,
+
+		uploadedSize: 0, // 已上传文件大小(字节)
+
+		chunkCount: 0,
+		chunksUploaded: 0,
+		fileMd5: '', //
+		// const emit = defineEmits(['onUpLoading', 'onSuccess'])
+		progress: {
+			strokeColor: {
+				'0%': '#108ee9',
+				'100%': '#87d068'
+			},
+			strokeWidth: 3,
+			format: (percent) => `${parseFloat(percent.toFixed(2))}%`,
+			class: 'test'
+		},
+		allChunks: 0, // 文件的md5值
+		successfulChunkPercents: 0, // 上传成功的分片百分比
+		fileSuffix: '', // 文件后缀
+		chunkList: [], // 文件后缀
+		uploadList: [], // 文件后缀
+		fileList: [],// 文件后缀
+		uploadFileList: [], // 文件后缀
+		uploadChunks: [],// 文件后缀
+		uploadFileListTemp: [],
+		upLoadTag: false, // 文件的md5值
+		startTime: 0, // 开始时间戳(毫秒
+		totalSize: 0, // 开始时间戳(毫秒
+		tempIndex: 0,
+		//文件数据放一起
+		fileForms: [],
+	}),
+	getters: {
+		getFileForms: (state) => state.fileForms,
+		getPauseFlags: (state) => state.pauseFlags,
+	},
+	actions: {
+		async addFileForms(fileForm) {
+			this.fileForms.push(fileForm)
+
+			for (let i = 0; i < fileForm.uploadFileList.length; i++) {
+
+				await this.checkMd5List(fileForm.uploadFileList[i])
+				await this.uploadSingleFile(fileForm.uploadFileList[i])
+			}
+			//准备开启去下载
+			// for (let i = 0; i < fileForm.uploadFileList.length; i++) {
+			// 	this.tempIndex+=1
+			// 	fileForm.uploadFileList[i].tempIndex = this.tempIndex
+			// 	this.uploadFileListTemp.push(fileForm.uploadFileList[i])
+			// }
+		},
+		handlerRemoveItem(mmyIndex, mmmyIndex) {
+			// let myIndex = -1
+			// for (let i = 0; i < this.uploadFileListTemp.length; i++) {
+			// 	if(this.uploadFileListTemp[i].tempIndex == item.tempIndex){
+			// 		myIndex = i
+			// 	}
+			// }
+			// if(myIndex != -1){
+			// 	this.uploadFileListTemp.splice(myIndex, 1)
+			// }
+			// let mmyIndex = -1
+			// let mmmyIndex = -1
+			// for (let i = 0; i < this.fileForms.length; i++) {
+			// 	for (let ii = 0; ii < this.fileForms[i].uploadFileList.length; ii++) {
+			// 		if(this.fileForms[i].uploadFileList[ii].tempIndex == item.tempIndex){
+			// 			mmyIndex = i
+			// 			mmmyIndex = ii
+			// 		}
+			// 	}
+			// }
+			// if(mmyIndex != -1 && mmmyIndex != -1){
+			// 	this.fileForms[mmyIndex].uploadFileList.splice(mmmyIndex, 1)
+			// }
+
+			this.fileForms[mmyIndex].uploadFileList.splice(mmmyIndex, 1)
+
+		},
+		pauseUpload(md5) {
+			this.pauseFlags[md5] = true
+		},
+		resumeUpload(md5) {
+			this.pauseFlags[md5] = false
+		},
+		async checkMd5List(uploadFile) {
+			// 标记文件正在检查
+			this.checkingFiles.add(uploadFile.md5)
+
+			const md5List = [{
+				md5: uploadFile.md5,
+				size: uploadFile.size,
+				chunkSize: uploadFile.chunks.length,
+				fileName: uploadFile.name,
+				fileSuffix: uploadFile.fileSuffix,
+				affiliationFuncType : 0
+			}]
+			await axios
+				.post(sysConfig.API_URL + '/api/webapp/minio/checkMd5List', md5List, {headers: {Token: tool.data.get('TOKEN')}})
+				.then((res) => {
+					console.log('文件上传返回结果:', res.data)
+					// return
+					var list = res.data
+					if (list.length !== 0) {
+						let upList = []
+						for (let item of list) {
+							console.log('item回来的', JSON.stringify(item))
+							if (uploadFile.md5 === item.md5) {
+								uploadFile.userFileId = item.userFileId
+								//重要的步骤
+								if (item.userFileId) {
+									uploadFile.percents = 100
+									this.checkedFiles.set(uploadFile.md5, {
+										userFileId: item.userFileId,
+										status: 'uploaded'
+									})
+									// emit('onSuccess', uploadFile)
+								}
+								// upList.push(item)
+							}
+						}
+						console.log('upList是:', upList)
+						// uploadFileList.value.push(uploadFile)
+						// emit('onSuccess', uploadFileList.value)
+					}
+					// 从正在检查列表中移除
+					this.checkingFiles.delete(uploadFile.md5)
+					return uploadFile
+					// 文件均存在minio中了,无需上传
+					// if (uploadFileList.value.length === 0) {
+					// 	successfulChunkPercents.value = 100
+					// 	alert('文件上传成功')
+					// }
+				})
+				.catch((error) => {
+					console.log('检查返回错误', error)
+					// 从正在检查列表中移除
+					this.checkingFiles.delete(uploadFile.md5)
+					throw error
+				})
+		},
+
+
+		async uploadFilesChunk(data, onSuccess) {
+			console.log('进入了uploadFileChunk方法...')
+			let retryTime = 5 //重试次数
+			const formData = new FormData()
+			formData.append('md5', data.md5)
+			// formData.append('md5', md5)
+			formData.append('chunkIndex', data.chunkIndex)
+			formData.append('chunk', data.chunk)
+			formData.append('chunkSize', data.chunkSize)
+			formData.append('fileSuffix', data.fileSuffix)
+			formData.append('fileName', data.fileName)
+			formData.append('affiliationFuncType', data.affiliationFuncType)
+			return axios
+				.post(sysConfig.API_URL + '/api/webapp/minio/upload', formData, {
+					headers: {'Content-Type': 'multipart/form-data', Token: tool.data.get('TOKEN')}
+				})
+				.then((res) => onSuccess())
+				.catch((error) => {
+					console.log('上传分片失败了...', error)
+					if (retryTime > 0) {
+						retryTime--
+						return this.uploadChunk(data, onSuccess)
+					}
+				})
+		},
+
+		// 上传分片 旧
+		async uploadChunk(data, onSuccess) {
+			let retryTime = 5 //重试次数
+			const formData = new FormData()
+			// formData.append('identifier', fileMd5.value)
+			formData.append('md5', data.md5)
+			// formData.append('md5', md5)
+			formData.append('chunkIndex', data.chunkIndex)
+			formData.append('chunk', data.chunk)
+			formData.append('chunkSize', data.chunkSize)
+			formData.append('fileSuffix', data.fileSuffix)
+			formData.append('fileName', data.fileName)
+			formData.append('affiliationFuncType', data.affiliationFuncType)
+			return axios
+				.post(sysConfig.API_URL + '/api/webapp/minio/upload', formData, {
+					headers: {'Content-Type': 'multipart/form-data', Token: tool.data.get('TOKEN')}
+				})
+				.then((res) => onSuccess())
+				.catch((error) => {
+					if (retryTime > 0) {
+						retryTime--
+						return this.uploadChunk(data, onSuccess)
+					}
+				})
+		},
+		calculateSpeed(startTime, uploadedSize) {
+			const currentTime = new Date().getTime()
+			const timeElapsed = (currentTime - startTime) / 1000 // 单位:秒
+			if (timeElapsed > 0) {
+				const speed = uploadedSize / timeElapsed // 单位:字节/秒
+				return speed
+			}
+			return 0
+		},
+		estimateRemainingTime(startTime, uploadedSize, totalSize) {
+			console.log('疑问', ' 总的 ', totalSize, ' 变化的 ', uploadedSize)
+			const remainingSize = totalSize - uploadedSize // 剩余文件大小
+			const speed = this.calculateSpeed(startTime, uploadedSize) // 平均上传速度(字节/秒)
+
+			if (speed > 0) {
+				const remainingTimeSeconds = remainingSize / speed // 剩余时间(秒)
+				return remainingTimeSeconds
+			}
+			return Infinity // 如果上传速度为 0,则无法估算
+		},
+		formatTime(seconds) {
+			const minutes = Math.floor(seconds / 60)
+			const secs = Math.floor(seconds % 60)
+			if (minutes == 0 && secs == 0) {
+				return ''
+			}
+			return `${minutes} 分 ${secs} 秒`
+		},
+		async uploadSingleFile(fileObj) {
+			const file = fileObj
+			const md5 = file.md5
+			// const index = uploadFileList.value.findIndex((item) => item.md5 === md5)
+			//
+			// if (index === -1) return
+
+			const item = fileObj
+
+			if (item && item.userFileId) {
+				this.getUp(fileObj)
+				return
+			}
+
+			// // 如果是暂停状态则不执行上传
+			// while (pauseFlags.value[md5]) {
+			// 	await new Promise((resolve) => setTimeout(resolve, 500))
+			// }
+			// 添加到正在上传任务中
+			// uploadingTasks.value[md5] = true
+			file.startTime = new Date().getTime()
+			file.uploadedSize = 0
+
+			const chunkPromises = []
+
+			for (let i = 0; i < item.chunks.length; i++) {
+				let chunk = item.chunks[i]
+
+				while (this.pauseFlags[md5]) {
+					await new Promise((resolve) => setTimeout(resolve, 500))
+				}
+
+				chunkPromises.push(
+					await this.uploadFilesChunk(
+						{
+							affiliationFuncType : 0,
+							md5,
+							chunk,
+							chunkIndex: i + 1,
+							fileSuffix: item.fileSuffix,
+							chunkSize: item.chunks.length,
+							fileName: item.name
+						},
+						() => {
+							// chunksUploaded.value++
+							// uploadChunks.value++
+
+							// successfulChunkPercents.value = 100 * (uploadChunks.value / allChunks.value).toFixed(2)
+							item.uploadedSize += chunk.size // 更新已上传大小
+							const remainingTime = this.estimateRemainingTime(item.startTime, item.uploadedSize, item.size)
+							console.log(`预计剩余时间: ${this.formatTime(remainingTime)}`)
+							// item.percents = (100 * (uploadChunks.value / item.chunks.length)).toFixed(2)
+							item.time = this.formatTime(remainingTime)
+
+							const percent = ((i + 1) / item.chunks.length) * 100
+							item.percents = percent.toFixed(2)
+							console.log(`我得名字: `, item.name, ' i ', i, ' item.chunks.length ', item.chunks.length)
+							// item.time = formatTime(estimateRemainingTime())
+						}
+					)
+				)
+			}
+
+			await Promise.all(chunkPromises)
+			item.affiliationFuncType = 0
+			// 合并分片
+			const mergeResult = await axios.post(
+				sysConfig.API_URL + `/api/webapp/minio/merge?md5=${md5}&fileSuffix=${item.fileSuffix}&chunkTotal=${item.chunks.length}&fileName=${item.name}&fileSize=${item.size}&affiliationFuncType=${item.affiliationFuncType}`,
+				null,
+				{headers: {Token: tool.data.get('TOKEN')}}
+			)
+			console.log('怎么说呢', ' 啊网络请求 ', mergeResult)
+			fileObj.userFileId = mergeResult.data.userFileId
+			fileObj.percents = 100
+
+			// uploadingTasks.value[item.md5] = false
+			// item.time = '上传完成'
+			// 尝试恢复一个被暂停的任务
+			// autoResumePausedUpload()
+			// upLoadTag.value = false
+			// emit('onSuccess', uploadFileList.value)
+
+
+			this.getUp(fileObj)
+		},
+		async getUp(fileObj) {
+
+			for (let i = 0; i < this.fileForms.length; i++) {
+				console.log('怎么说呢', this.fileForms[i].upTag != undefined,' 里面的 ', this.fileForms[i], ' 外面的1 ', fileObj)
+
+				if (this.fileForms[i].upTag == undefined) {
+					let count = 0
+					let list = []
+					for (let ii = 0; ii < this.fileForms[i].uploadFileList.length; ii++) {
+						console.log('怎么说呢', ' 里面的 ', this.fileForms[i], ' 外面的2 ', fileObj)
+						let item = this.fileForms[i].uploadFileList[ii]
+						if (item.userFileId != undefined && item.percents == 100){
+							count ++
+							list.push(item.userFileId)
+						}
+					}
+					if(this.fileForms[i].uploadFileList.length == count){
+					let formData = this.fileForms[i]
+						formData.userfileIds = list.join(',')
+
+						//去上传
+					let res = await resourceAuditApi.stuLinkResourceRecordAdd(formData)
+							// .then((res) => {
+							// 	Modal.success({ content: '资源上传成功' })
+							// })
+							// .catch((err) => {
+							// 	Modal.success({ content: '资源上传失败' })
+							// 	console.log(err)
+							// })
+						console.log('上传至hi偶',res)
+						this.fileForms[i].upTag = true
+						EventBus.emit('onUpTag')
+					}
+				}
+			}
+
+		}
+
+	},
+
+
+
+
+
+
+})

+ 2 - 2
src/views/courseAdd/components/courseProduction/userSelection.vue

@@ -1,6 +1,6 @@
 <template>
 	<a-drawer
-		v-model:visible="visible"
+		v-model:visible="props.visible"
 		title="选择可见成员"
 		placement="right"
 		width="50%"
@@ -169,7 +169,7 @@
 	// 替换原来的onSelect方法
 	const onCheck = (checkedKeysValue, { checked, node, checkedNodes }) => {
 		// 过滤掉非叶子节点
-		const leafNodes = checkedNodes.filter((node) => node.isLeaf)
+		const leafNodes = checkedNodes.filter((node) => node.isLeaf&& node.infoType=="user")
 		checkedKeys.value = leafNodes.map((node) => node.id)
 		selectedUsers.value = leafNodes.map((node) => ({
 			id: node.id,

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

@@ -36,7 +36,7 @@
 			<div class="audit-info">
 				<div class="info-section">
 					<p><strong>上传人:</strong>{{ detailData.resourceCreaterUserName || '--' }}</p>
-					<p><strong>所属院系:</strong>{{ detailData?.collegeAllIdName || '--' }}</p>
+					<p><strong>所属单位:</strong>{{ detailData?.collegeAllIdName || '--' }}</p>
 					<p><strong>所属专业:</strong>{{ detailData?.majorIdName || '--' }}</p>
 					<p><strong>资源类型:</strong>{{ detailData?.resourceTypeName || '--' }}</p>
 					<p><strong>资源格式:</strong>{{ detailData?.suffix || '--' }}</p>

+ 449 - 307
src/views/myResources/myResources.vue

@@ -1,287 +1,331 @@
 <template>
-	<a-card v-if="visible">
-		<a-button type="primary" style="margin-left: 0px" @click="handleBack">返回</a-button>
-		<!-- 标签页 -->
-		<a-tabs v-model:activeKey="formState.verifyStatus" @change="tabChange">
-			<a-tab-pane key="0,3" tab="未发布" v-if="!pageType"></a-tab-pane>
-			<a-tab-pane key="1" tab="待审核"></a-tab-pane>
-			<a-tab-pane key="2" tab="已发布"></a-tab-pane>
-			<!-- <a-tab-pane key="3" tab="已审核" v-if="pageType == 'economize'"></a-tab-pane> -->
-			<a-tab-pane key="4" tab="回收站"></a-tab-pane>
-		</a-tabs>
-		<!-- 搜索和操作区域 -->
-		<a-row :gutter="16" style="margin-bottom: 16px">
-			<a-col :span="18">
-				<a-input v-model:value="formState.fileName" placeholder="请输入资源名称" style="width: 150px" />
-				<a-cascader
-					style="width: 200px; margin-left: 8px"
-					v-model:value="majorIdName"
-					:options="collegeMajorOptions"
-					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
-					placeholder="请选择院系"
-					changeOnSelect
-					@change="changeCollegeMajor"
-				/>
-				<!-- <a-select
-					v-model:value="formState.majorId"
-					style="width: 150px; margin-left: 8px"
-					:fieldNames="{ label: 'majorName', value: 'majorCode' }"
-					:options="majorOptions"
-					placeholder="请选择专业"
-				/> -->
-				<a-cascader
-					style="width: 200px; margin-left: 8px"
-					v-model:value="resourceName"
-					:options="resourceTypeOptions"
-					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
-					placeholder="请选择资源类型"
-					changeOnSelect
-					@change="changeCollegeResource"
-				/>
-				<!-- <a-select
-					v-model:value="formState.resourceType"
-					style="width: 150px; margin-left: 8px"
-					:options="resourceTypeOptions"
-					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
-					placeholder="请选择资源类型"
-				/> -->
-				<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-if="['0,3', '0', '3'].includes(formState.verifyStatus)"
-					v-model:value="isVerifyStatus"
-					placeholder="请选择资源状态"
-					style="width: 150px; margin-left: 8px"
-				>
-					<a-select-option value="0">未发布</a-select-option>
-					<a-select-option value="3">未通过</a-select-option>
-				</a-select>
-				<!-- <a-select
-					v-model:value="formState.suffix"
-					style="width: 200px; margin-left: 8px"
-					:options="suffixTypeOptions"
-					placeholder="请选择课件格式"
-				/> -->
-				<a-button type="primary" style="margin-left: 8px" @click="handleSearch">查询</a-button>
-				<a-button style="margin-left: 8px" @click="handleReset">重置</a-button>
-			</a-col>
-			<a-col :span="6" style="text-align: right">
-				<a-button
-					type="primary"
-					style="margin-right: 8px"
-					v-if="formState.verifyStatus === '0,3' && !pageType"
-					@click="batchPublish"
-					:disabled="selectedRowKeys.length === 0"
-				>
-					+ 批量发布
-				</a-button>
-				<a-button type="primary" v-if="!pageType" @click="showUploadModal">+ 上传资源</a-button>
-			</a-col>
-		</a-row>
-
-		<!-- 表格 -->
-		<a-table
-			:columns="columnsPending"
-			:data-source="dataSource"
-			:pagination="false"
-			:loading="loading"
-			bordered
-			:row-key="(record) => record.id"
-			:row-selection="rowSelection"
-			:scroll="{ x: 1500, y: 600 }"
-		>
-			<template #bodyCell="{ column, text, record }">
-				<template
-					v-if="
+		<a-card v-if="visible">
+      <a-button type="primary"  @click="handleBack">返回</a-button>
+			<!-- 标签页 -->
+			<a-tabs v-model:activeKey="formState.verifyStatus" @change="tabChange">
+				<a-tab-pane key="0,3" tab="未发布" v-if="!pageType"></a-tab-pane>
+				<a-tab-pane key="1" tab="待审核"></a-tab-pane>
+				<a-tab-pane key="2" tab="已发布"></a-tab-pane>
+				<!-- <a-tab-pane key="3" tab="已审核" v-if="pageType == 'economize'"></a-tab-pane> -->
+				<a-tab-pane key="4" tab="回收站"></a-tab-pane>
+			</a-tabs>
+			<!-- 搜索和操作区域 -->
+			<a-card style="margin-bottom: 16px">
+				<div style="display: flex; justify-content: space-between ;">
+					<div style="display: flex; flex-direction: column;  width: 200%;">
+						<!-- 第一行:名称、院系、类型 -->
+						<a-row :gutter="16" style="margin-bottom: 16px">
+							<a-col :span="6">
+								<span>名称:</span>
+								<a-input
+									v-model:value="formState.fileName"
+									placeholder="请输入名字"
+									style="width: calc(100% - 50px); margin-left: 8px"
+								/>
+							</a-col>
+							<a-col :span="6">
+								<span>层级:</span>
+								<a-cascader
+									style="width: calc(100% - 50px); margin-left: 8px"
+									v-model:value="majorIdName"
+									:options="collegeMajorOptions"
+									:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
+									placeholder="请选择"
+									changeOnSelect
+									@change="changeCollegeMajor"
+								/>
+							</a-col>
+							<a-col :span="6">
+								<span>类型:</span>
+								<a-cascader
+									style="width: calc(100% - 50px); margin-left: 8px"
+									v-model:value="resourceName"
+									:options="resourceTypeOptions"
+									:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
+									placeholder="请选择"
+									changeOnSelect
+									@change="changeCollegeResource"
+								/>
+							</a-col>
+
+						</a-row>
+
+						<!-- 第二行:状态、租户、创建人 -->
+						<a-row :gutter="16">
+							<a-col :span="6">
+								<span>状态:</span>
+								<a-select
+									v-if="['0,3', '0', '3'].includes(formState.verifyStatus)"
+									v-model:value="isVerifyStatus"
+									placeholder="请选择"
+									style="width: calc(100% - 50px); margin-left: 8px"
+								>
+									<a-select-option value="0">未发布</a-select-option>
+									<a-select-option value="3">未通过</a-select-option>
+								</a-select>
+								<a-select
+									v-else
+									v-model:value="formState.suffix"
+									style="width: calc(100% - 50px); margin-left: 8px"
+									:options="fileformatOptions"
+									:fieldNames="{ label: 'fileExtendName', value: 'fileExtendName', children: 'children' }"
+									placeholder="请选择"
+								/>
+							</a-col>
+							<!--				<a-col :span="6">-->
+							<!--					<span>租户:</span>-->
+							<!--					<a-input placeholder="请选择" style="width: calc(100% - 50px); margin-left: 8px" disabled />-->
+							<!--				</a-col>-->
+<!--							<a-col :span="6">-->
+<!--								<span>创建人:</span>-->
+<!--								<a-input placeholder="请输入名字" style="width: calc(100% - 64px); margin-left: 8px" />-->
+<!--							</a-col>-->
+
+						</a-row>
+						<a-row style="margin-top: 16px">
+							<a-col :span="24" style="text-align: left">
+								<a-button
+									type="primary"
+									style="margin-right: 8px"
+									v-if="formState.verifyStatus === '0,3' && !pageType"
+									@click="batchPublish"
+									:disabled="selectedRowKeys.length === 0"
+								>
+									+ 批量发布
+								</a-button>
+								<a-button type="primary" v-if="!pageType && formState.verifyStatus === '0,3'" @click="showUploadModal">+ 新增</a-button>
+							</a-col>
+						</a-row>
+					</div>
+
+					<div>
+						<a-button type="primary" @click="handleSearch">查询</a-button>
+						<a-button @click="handleReset" style="margin-top: 20px">重置</a-button>
+					</div>
+				</div>
+
+
+
+
+
+
+			</a-card>
+			<!-- 表格 -->
+			<a-table
+				:columns="columnsPending"
+				:data-source="dataSource"
+				:pagination="false"
+				:loading="loading"
+				bordered
+				:row-key="(record) => record.id"
+				:row-selection="rowSelection"
+				:scroll="{ x: 1300 }"
+			>
+				<template #bodyCell="{ column, text, record }">
+					<template
+						v-if="
 						['fileName', 'collegeAllIdName', 'majorIdName', 'resourceALLTypeName', 'suffix', 'uploadTime'].includes(
 							column.dataIndex
 						)
 					"
-				>
-					<div class="multiLine-ellipsis" :title="text">{{ text || '-' }}</div>
-				</template>
-				<!-- 状态列 -->
-				<template v-if="column.key === 'verifyStatus'">
+					>
+						<div class="multiLine-ellipsis" :title="text">{{ text || '-' }}</div>
+					</template>
+					<!-- 状态列 -->
+					<template v-if="column.key === 'verifyStatus'">
 					<span v-if="record.verifyStatus === '0'">
 						<a-badge status="processing" text="未发布" />
 					</span>
 
-					<span v-else-if="record.verifyStatus === 'uploaded'">
+						<span v-else-if="record.verifyStatus === 'uploaded'">
 						<a-badge status="success" text="已上传" />
 					</span>
-					<span v-else-if="record.verifyStatus === '1'">
+						<span v-else-if="record.verifyStatus === '1'">
 						<a-badge status="default" text="待审核" />
 					</span>
-					<span v-else-if="record.verifyStatus === '2'">
+						<span v-else-if="record.verifyStatus === '2'">
 						<a-badge status="success" text="已发布" />
 					</span>
-					<span v-if="record.verifyStatus === '3'">
+						<span v-if="record.verifyStatus === '3'">
 						<a-badge status="error" text="未通过" />
 					</span>
-					<span v-else-if="record.verifyStatus === '4'">
+						<span v-else-if="record.verifyStatus === '4'">
 						<a-badge status="error" text="已删除" />
 					</span>
+					</template>
+					<template v-if="column.key === 'isConvert'">
+						<span v-if="record.isConvert === '0'">转换中</span>
+						<span v-if="record.isConvert === '1'">转换完成</span>
+					</template>
+					<template v-if="column.dataIndex === 'fileUrl'">
+						<!-- 动态图标 + 格式提示 -->
+						<a-tooltip :title="`${record.suffix || '未知'}`">
+							<component
+								:is="fileTypeIcons[record.suffix?.toLowerCase()] || fileTypeIcons['*']"
+								:style="{ fontSize: '24px', color: getIconColor(record.suffix) }"
+							/>
+						</a-tooltip>
+					</template>
+					<!-- 个人资源操作列 -->
+					<template v-if="column.key === 'action' && !pageType">
+						<div class="editable-cell">
+							<span v-if="formState.verifyStatus === '0,3' && record.isConvert == 1" style="cursor:  pointer" @click="handlePublish(record)">发布</span>
+							<span v-if="formState.verifyStatus === '4'"  style="cursor:  pointer" @click="handleRestore(record)">恢复</span>
+							<span style="cursor:  pointer; margin-left: 10px;  margin-right: 10px" @click="handleRestName(record)">修改资源名称</span>
+							<a-dropdown>
+								<span class="ant-dropdown-link" style="cursor:  pointer">
+									更多
+									<DownOutlined />
+								</span>
+								<template #overlay>
+									<a-menu>
+										<a-menu-item v-if="record.isConvert == 1">
+											<a href="javascript:;" @click="handleView(record)">预览</a>
+										</a-menu-item>
+										<a-menu-item v-if="record.isConvert == 1">
+											<a
+												target="_blank"
+												style="display: block; color: inherit"
+												:href="$file.getDownloadFilePath3(record)"
+												:download="record.fileName"
+											>
+												下载
+											</a>
+										</a-menu-item>
+										<a-menu-item v-if="formState.verifyStatus == '2'">
+											<a-popconfirm title="确认下架吗?" @confirm="resourcesDown(record)">
+												<a href="javascript:;">下架</a>
+											</a-popconfirm>
+										</a-menu-item>
+										<a-menu-item >
+											<a href="javascript:;" @click="edit(record)">编辑</a>
+										</a-menu-item>
+										<a-menu-item v-if="formState.verifyStatus !== '2' && record.isConvert == 1">
+											<a-popconfirm title="确认删除吗?" @confirm="resourcesDelete(record)">
+												<a href="javascript:;">删除</a>
+											</a-popconfirm>
+										</a-menu-item>
+									</a-menu>
+								</template>
+							</a-dropdown>
+						</div>
+					</template>
+					<!-- 资源管理操作列 -->
+					<template v-if="column.key === 'action' && pageType == 'economize'">
+						<div class="editable-cell">
+							<a v-if="formState.verifyStatus === '1'" @click="handleAudit(record)">审核</a>
+							<a v-if="formState.verifyStatus === '2'" @click="handlePermission(record)">权限</a>
+							<a v-if="formState.verifyStatus === '4'" @click="handleRestore(record)">恢复</a>
+							<span style="cursor:  pointer; margin-left: 10px;  margin-right: 10px" @click="handleRestName(record)">修改资源名称</span>
+							<a-divider type="vertical" />
+							<a-dropdown>
+								<a class="ant-dropdown-link">
+									更多
+									<DownOutlined />
+								</a>
+								<template #overlay>
+									<a-menu>
+										<a-menu-item>
+											<a href="javascript:;" @click="handleView(record)">预览</a>
+										</a-menu-item>
+										<a-menu-item>
+											<a
+												target="_blank"
+												style="display: block; color: inherit"
+												:href="$file.getDownloadFilePath3(record)"
+												:download="record.fileName"
+											>
+												下载
+											</a>
+										</a-menu-item>
+										<a-menu-item v-if="formState.verifyStatus !== '1'">
+											<a-popconfirm title="确认删除吗?" @confirm="resourcesDelete(record)">
+												<a href="javascript:;">删除</a>
+											</a-popconfirm>
+										</a-menu-item>
+									</a-menu>
+								</template>
+							</a-dropdown>
+						</div>
+					</template>
 				</template>
-				<template v-if="column.dataIndex === 'fileUrl'">
-					<!-- 动态图标 + 格式提示 -->
-					<a-tooltip :title="`${record.suffix || '未知'}`">
-						<component
-							:is="fileTypeIcons[record.suffix?.toLowerCase()] || fileTypeIcons['*']"
-							:style="{ fontSize: '24px', color: getIconColor(record.suffix) }"
-						/>
-					</a-tooltip>
-				</template>
-				<!-- 个人资源操作列 -->
-				<template v-if="column.key === 'action' && !pageType">
-					<div class="editable-cell">
-						<a v-if="formState.verifyStatus === '0,3'" @click="handlePublish(record)">发布</a>
-						<a v-if="formState.verifyStatus === '4'" @click="handleRestore(record)">恢复</a>
-						<a-divider type="vertical" />
-						<a-dropdown>
-							<a class="ant-dropdown-link">
-								更多
-								<DownOutlined />
-							</a>
-							<template #overlay>
-								<a-menu>
-									<a-menu-item>
-										<a href="javascript:;" @click="handleView(record)">预览</a>
-									</a-menu-item>
-									<a-menu-item>
-										<a
-											target="_blank"
-											style="display: block; color: inherit"
-											:href="$file.getDownloadFilePath3(record)"
-											:download="record.fileName"
-										>
-											下载
-										</a>
-									</a-menu-item>
-									<a-menu-item v-if="formState.verifyStatus !== '2'">
-										<a href="javascript:;" @click="edit(record)">编辑</a>
-									</a-menu-item>
-									<a-menu-item v-if="formState.verifyStatus !== '2'">
-										<a-popconfirm title="确认删除吗?" @confirm="resourcesDelete(record)">
-											<a href="javascript:;">删除</a>
-										</a-popconfirm>
-									</a-menu-item>
-								</a-menu>
-							</template>
-						</a-dropdown>
-					</div>
-				</template>
-				<!-- 资源管理操作列 -->
-				<template v-if="column.key === 'action' && pageType == 'economize'">
-					<div class="editable-cell">
-						<a v-if="formState.verifyStatus === '1'" @click="handleAudit(record)">审核</a>
-						<a v-if="formState.verifyStatus === '2'" @click="handlePermission(record)">权限</a>
-						<a v-if="formState.verifyStatus === '4'" @click="handleRestore(record)">恢复</a>
-						<a-divider type="vertical" />
-						<a-dropdown>
-							<a class="ant-dropdown-link">
-								更多
-								<DownOutlined />
-							</a>
-							<template #overlay>
-								<a-menu>
-									<a-menu-item>
-										<a href="javascript:;" @click="handleView(record)">预览</a>
-									</a-menu-item>
-									<a-menu-item>
-										<a
-											target="_blank"
-											style="display: block; color: inherit"
-											:href="$file.getDownloadFilePath3(record)"
-											:download="record.fileName"
-										>
-											下载
-										</a>
-									</a-menu-item>
-									<a-menu-item v-if="formState.verifyStatus !== '1'">
-										<a-popconfirm title="确认删除吗?" @confirm="resourcesDelete(record)">
-											<a href="javascript:;">删除</a>
-										</a-popconfirm>
-									</a-menu-item>
-								</a-menu>
-							</template>
-						</a-dropdown>
-					</div>
-				</template>
-			</template>
-		</a-table>
-		<div class="dis-flex-sb margin-top">
-			<div>
-				<a-button @click="selectAll">选择全部</a-button>
-				<a-button @click="invertSelection" style="margin-left: 8px">反选选择</a-button>
-			</div>
-			<div>
-				<CustomPagination
-					:total="pagination.total"
-					:current="pagination.pageNum"
-					:pageSize="pagination.pageSize"
-					:showQuickJumper="true"
-					:showSizeChanger="true"
-					:showTotal="(total) => `共 ${total} 条数据`"
-					@change="handlePageChange"
-					@showSizeChange="handlePageSizeChange"
-				/>
+			</a-table>
+			<div class="dis-flex-sb margin-top">
+				<div>
+					<a-button @click="selectAll">选择全部</a-button>
+					<a-button @click="invertSelection" style="margin-left: 8px">反选选择</a-button>
+				</div>
+				<div>
+					<CustomPagination
+						:total="pagination.total"
+						:current="pagination.pageNum"
+						:pageSize="pagination.pageSize"
+						:showQuickJumper="true"
+						:showSizeChanger="true"
+						:showTotal="(total) => `共 ${total} 条数据`"
+						@change="handlePageChange"
+						@showSizeChange="handlePageSizeChange"
+					/>
+				</div>
 			</div>
-		</div>
-		<!-- 权限树模态框 -->
-		<permissionTree v-if="permissionTreeVisible" @close="permissionTreeVisible = false"></permissionTree>
-		<!-- 审核播放模态框 -->
-		<auditModal
-			v-if="auditModalVisible"
-			:recordData="publishedData"
-			:isAudit="auditState"
-			@confirm="auditConfirm"
-			@close="auditModalVisible = false"
-		></auditModal>
-		<!-- 资源上传模态框 -->
-		<resourceUpload
-			v-if="uploadModalVisible"
-			:isState="isState"
-			:isVerifyStatus="formState.verifyStatus"
-			:resourcesId="editResourcesId"
-			@close="uploadModalVisible = false"
-			@getList="getList"
-		></resourceUpload>
-		<!-- 发布模态框 -->
-		<releaseModal v-if="releaseVisible" @close="releaseVisible = false" @confirm="releaseConfirm"></releaseModal>
-	</a-card>
+			<!-- 权限树模态框 -->
+			<permissionTree v-if="permissionTreeVisible" @close="permissionTreeVisible = false"></permissionTree>
+			<!-- 审核播放模态框 -->
+			<auditModal
+				v-if="auditModalVisible"
+				:recordData="publishedData"
+				:isAudit="auditState"
+				@confirm="auditConfirm"
+				@close="auditModalVisible = false"
+			></auditModal>
+			<!-- 资源上传模态框 -->
+			<resourceUpload
+				ref="resourceUploadRef"
+				:isState="isState"
+				:isVerifyStatus="formState.verifyStatus"
+				:resourcesId="editResourcesId"
+				@close="uploadModalVisible = false"
+				@getList="getList"
+			></resourceUpload>
+			<!-- 发布模态框 -->
+			<releaseModal v-if="releaseVisible" @close="releaseVisible = false" @confirm="releaseConfirm"></releaseModal>
+			<div style="height: 20px"></div>
+			<span>容量</span><span style="color: #6abe39"> {{ bToGb(storageMin) }} GB</span><span> / </span
+		><span style="color: red">{{ bToGb(storageMax) }} GB</span>
+		</a-card>
+
+		<NameModal ref="nameModalRef" @confirm="confirm"></NameModal>
 </template>
 
 <script setup>
 	import { ref, onMounted } from 'vue'
 	import { DownOutlined } from '@ant-design/icons-vue'
 	import releaseModal from './releaseModal.vue'
+	import NameModal from './nameModal.vue'
 	import resourceUpload from './resourceUpload.vue'
 	import resourceAuditApi from '@/api/resourceAudit.js'
 	import permissionTree from './permissionTree.vue'
 	import auditModal from './auditModal.vue'
+	import { getstorage } from '@/api/myResource/user'
 	import CustomPagination from '@/components/customPagination.vue'
 	import tool from '@/utils/tool'
 	import { useRoute, useRouter } from 'vue-router'
-	const router = useRouter()
-	import {
-		FileOutlined,
-		FileImageOutlined,
-		FilePdfOutlined,
-		FileWordOutlined,
-		FileExcelOutlined,
-		FilePptOutlined,
-		FileTextOutlined,
-		FileZipOutlined,
-		PlaySquareOutlined
-	} from '@ant-design/icons-vue'
-	const emit = defineEmits(['handleBack'])
-	const visible = ref(false)
+	import EventBus from "@/utils/EventBus";
+
+	import { globalStore } from '@/store/global'
+	import {storeToRefs} from "pinia";
+	const myglobalStore = globalStore()
+	// 使用 storeToRefs 保持响应性
+	const { getUserInfo} = storeToRefs(myglobalStore)
 
+	// getFileModel 保持了与 store 的响应式连接
+	const userInfo = computed({
+		get: () => getUserInfo.value
+	})
+	const resourceUploadRef = ref(null)
+
+	const router = useRouter()
+  const emit = defineEmits('handleBack')
 	// eslint-disable-next-line vue/no-setup-props-destructure
 	const { pageType } = defineProps({
 		pageType: {
@@ -321,13 +365,19 @@
 	const dataSource = ref([])
 	//发布按钮状态
 	const releaseVisible = ref(false)
+	const nameModalRef = ref(null)
 	const permissionTreeVisible = ref(false) //权限树
 	const auditModalVisible = ref(false) //播放审核
 	const isPublishBulk = ref(false) //是否批量发布
 	const loading = ref(false) // 列表loading
+	const visible = ref(false) // 列表loading
 	const isState = ref(0) //是否是编辑  0:新增 1:编辑
 	const isVerifyStatus = ref(null) //未发布状态 0未发布 3已驳回
 	const editResourcesId = ref(null) //资源id
+
+	const storageMin = ref(0) //资源id
+	const storageMax = ref(0) //资源id
+
 	// 搜索值
 	const searchValue = ref('')
 	//课程类型
@@ -350,6 +400,7 @@
 		resourceTwoType: null, //资源类型二级
 		suffix: null
 	})
+	const teacherData = ref(null)
 	// 添加选择状态
 	const majorIdName = ref([])
 	const resourceName = ref([])
@@ -387,17 +438,18 @@
 	})
 	// 列定义
 	const columnsUnpublished = [
-		{
-			title: '编号',
-			align: 'center',
-			dataIndex: 'fileId',
-			key: 'fileId'
-		},
+		// {
+		// 	title: '编号',
+		// 	align: 'center',
+		// 	dataIndex: 'fileId',
+		// 	key: 'fileId'
+		// },
 		{
 			title: '资源名称',
 			align: 'center',
 			dataIndex: 'fileName',
-			key: 'fileName'
+			key: 'fileName',
+
 		},
 		{
 			title: '课件格式',
@@ -425,26 +477,27 @@
 		{
 			title: '操作',
 			align: 'center',
-			width: 150,
+			width: 200,
 			key: 'action'
 		}
 	]
 
 	const columnsPending = [
-		{
-			title: '编号',
-			dataIndex: 'id',
-			align: 'center',
-			key: 'id'
-		},
+		// {
+		// 	title: '编号',
+		// 	dataIndex: 'id',
+		// 	align: 'center',
+		// 	key: 'id'
+		// },
 		{
 			title: '资源名称',
 			align: 'center',
 			dataIndex: 'fileName',
-			key: 'fileName'
+			key: 'fileName',
+			width: 150
 		},
 		{
-			title: '所属院系',
+			title: '所属单位',
 			align: 'center',
 			dataIndex: 'collegeAllIdName',
 			key: 'collegeAllIdName'
@@ -465,40 +518,59 @@
 			title: '资源类型',
 			align: 'center',
 			dataIndex: 'resourceALLTypeName',
-			key: 'resourceALLTypeName'
+			key: 'resourceALLTypeName',
+			width: 110
 		},
 		{
 			title: '资源格式',
 			align: 'center',
 			dataIndex: 'suffix',
-			key: 'suffix'
+			key: 'suffix',
+			width: 90
 		},
 		{
 			title: '上传时间',
 			align: 'center',
 			dataIndex: 'uploadTime',
-			key: 'uploadTime'
+			key: 'uploadTime',
+			width: 180
 		},
 		{
 			title: '状态',
 			align: 'center',
-			key: 'verifyStatus'
+			key: 'verifyStatus',
+			width: 100
+		},
+		{
+			title: '转换状态',
+			align: 'center',
+			key: 'isConvert',
+			width: 100
 		},
 		{
 			title: '资源缩略图',
 			align: 'center',
 			dataIndex: 'fileUrl',
-			key: 'fileUrl'
+			key: 'fileUrl',
+			width: 110
 		},
 		{
 			title: '操作',
 			align: 'center',
-			width: 150,
+			width: 250,
 			fixed: 'right',
 			key: 'action'
 		}
 	]
 
+
+  const handleBack= () =>{
+    emit('handleBack')
+  }
+  const close= () =>{
+    visible.value = false
+  }
+
 	const columnsPublished = [...columnsPending]
 	const columnsRecycle = [...columnsPending]
 	const collegeMajorOptions = ref([]) //院校下拉数据
@@ -532,6 +604,8 @@
 	const getListData = () => {
 		loading.value = true
 		let params = {
+			stulinkType : 1,
+			teacherId : teacherData.value.teacherId,
 			current: pagination.pageNum,
 			size: pagination.pageSize,
 			verifyStatus: isVerifyStatus.value ? isVerifyStatus.value : formState.verifyStatus,
@@ -543,10 +617,10 @@
 			collegeTwoId: formState.collegeTwoId,
 			collegeId: formState.collegeId,
 			collegeThreeId: formState.collegeThreeId,
-			isSelf: 0
+			isSelf: !pageType ? 1 : 0
 		}
 		resourceAuditApi
-			.page(params)
+			.stuLinkResourceRecordPage(params)
 			.then((res) => {
 				console.log(res, '资源审核列表')
 				dataSource.value = res.data.records
@@ -560,6 +634,40 @@
 				loading.value = false
 			})
 	}
+
+  const open = (data) => {
+	  teacherData.value = data
+	  console.log('什么呢任务信息',data)
+    visible.value = true
+    if (pageType == 'economize') {
+      formState.verifyStatus = '1'
+    }
+    getOrgTreeSelector()
+    getFileformat()
+    getResourceTypeTree()
+    getListData()
+    getstorage().then((res) => {
+      console.log('容量', res)
+
+      storageMin.value = res.data.storageSize
+      storageMax.value = res.data.totalStorageSize
+    })
+
+  }
+
+	const bToMb = (b) => {
+		if (typeof b !== 'number' || b < 0) {
+			throw new Error('请输入有效的非负数字')
+		}
+		return (b / (1024 * 1024)).toFixed(2) // 保留两位小数
+	}
+	const bToGb = (b) => {
+		if (typeof b !== 'number' || b < 0) {
+			throw new Error('请输入有效的非负数字')
+		}
+		return (b / (1024 * 1024 * 1024)).toFixed(2) // 保留两位小数
+	}
+
 	const changeCollegeMajor = (value, selectedOptions) => {
 		console.log('Selected:', value, selectedOptions)
 		if (!value) {
@@ -591,6 +699,18 @@
 
 		console.log('已经修改:', formState)
 	}
+
+	const confirm = (data) =>{
+		resourceAuditApi.editUserFileName(data).then((res)=>{
+
+
+			getOrgTreeSelector()
+			getFileformat()
+			getResourceTypeTree()
+			getListData()
+		})
+	}
+
 	const getList = () => {
 		getListData()
 		uploadModalVisible.value = false
@@ -670,10 +790,6 @@
 		dataSource.value = []
 		getListData()
 	}
-	const handleBack = () => {
-		emit('handleBack')
-	}
-
 	//发布
 	const handlePublish = (record) => {
 		publishedData.value = record
@@ -717,16 +833,16 @@
 			const params = {
 				ids: selectedRows.value.map((item) => item.id).join(','),
 				coverImage: obj.coverImageId,
-				resourceDesc: obj.resourceDesc,
+				resourceDesc: obj.courseDesc,
 				verifyStatus: 1
 			}
 			console.log(params, '批量发布参数')
-			// handleRelease(params)
+			handleRelease(params)
 		} else {
 			const params = {
 				ids: publishedData.value.id,
 				coverImage: obj.coverImageId,
-				resourceDesc: obj.resourceDesc,
+				resourceDesc: obj.courseDesc,
 				verifyStatus: 1
 			}
 			console.log(params, '发布参数')
@@ -762,7 +878,7 @@
 	}
 	const handleView = (record) => {
 		router.push({
-			path: '/portal/resourceDetails',
+			path: '/student/resourceDetails',
 			query: {
 				id: record.id,
 				state: 1
@@ -823,6 +939,24 @@
 	const handleDelete = (record) => {
 		console.log('Delete:', record)
 	}
+	const handleRestName = (record)=>{
+		console.log('修改资源名称:', record)
+		// fileId
+		// 	:
+		// 	"1981553204412776448"
+		// fileName
+		// 	:
+		// 	"p一百"
+		resourceAuditApi.detail({id : record.id}).then((res)=>{
+			console.log('修改资源名称: 单条', res)
+			console.log('修改资源名称: ', nameModalRef.value)
+			nameModalRef.value.open({
+				fileId : res.data.fileId,
+				fileName : res.data.fileName
+			})
+			// fileName
+		})
+	}
 	const handleRestore = (record) => {
 		const params = {
 			ids: record.id,
@@ -840,9 +974,14 @@
 	//资源编辑
 	const edit = (record) => {
 		console.log('Restore:', record)
-		uploadModalVisible.value = true
+
+		console.log('打开什么呢:', formState.verifyStatus)
 		isState.value = 1
-		editResourcesId.value = record.id
+		formState.verifyStatus = '0,3'
+		editResourcesId.value = record
+
+		resourceUploadRef.value.open(teacherData.value,record)
+
 	}
 	//资源删除
 	const resourcesDelete = (record) => {
@@ -875,14 +1014,31 @@
 				})
 		}
 	}
+	const resourcesDown = (record) => {
+			const params =
+				{
+					ids: record.id,
+					verifyStatus : 0
+				}
+
+		resourceAuditApi
+			.updateStatus(params)
+			.then((res) => {
+				getListData()
+			})
+			.catch((err) => {
+				console.error(err)
+			})
+	}
 
 	// 上传资源模态框
 	const uploadModalVisible = ref(false)
 
 	// 显示上传模态框
 	const showUploadModal = () => {
-		isState.value = 0
-		uploadModalVisible.value = true
+		// isState.value = 0
+		// uploadModalVisible.value = true
+		EventBus.emit('openMiniYun',teacherData.value)
 	}
 	// 翻页
 	const handlePageChange = (page) => {
@@ -895,31 +1051,17 @@
 		pagination.pageSize = size
 		getListData()
 	}
-
-	onMounted(() => {
-		// if (pageType == 'economize') {
-		// 	formState.verifyStatus = '1'
-		// }
-		// getOrgTreeSelector()
-		// getFileformat()
-		// getResourceTypeTree()
-		// getListData()
-	})
-	const open = () => {
-		visible.value = true
-		if (pageType == 'economize') {
-			formState.verifyStatus = '1'
-		}
-		getOrgTreeSelector()
-		getFileformat()
-		getResourceTypeTree()
+	const onUpTag = () => {
+		pagination.pageNum = 1
 		getListData()
 	}
-	const close = () => {
-		visible.value = false
-	}
+	onMounted(() => {
+
+	})
+
 
-	defineExpose( {open,close})
+	EventBus.on('onUpTag',onUpTag)
+defineExpose( {open,close})
 </script>
 
 <style scoped>

+ 161 - 0
src/views/myResources/nameModal.vue

@@ -0,0 +1,161 @@
+<template>
+	<a-modal v-model:visible="visibles" title="资源信息" @cancel="handleCancel" @ok="handleOk" width="800px">
+		<a-form :model="dataForm"
+				ref="formRef"
+				:rules="formRules"
+				:label-col="{ span: 6 }"
+				:wrapper-col="{ span: 16 }">
+			<!-- <a-form-item label="资源名称" name="resourceName">
+				<a-input v-model:value="uploadForm.resourceName" placeholder="请输入资源名称" />
+			</a-form-item> -->
+
+			<!-- <a-form-item label="资源类型" name="resourceType">
+				<a-select v-model:value="uploadForm.resourceType" placeholder="请选择资源类型">
+					<a-select-option value="type1">类型1</a-select-option>
+					<a-select-option value="type2">类型2</a-select-option>
+				</a-select>
+			</a-form-item>
+
+			<a-form-item label="课程名称" name="courseName">
+				<a-input v-model:value="uploadForm.courseName" placeholder="请输入课程名称" />
+			</a-form-item> -->
+
+			<a-form-item label="资源名称" name="fileName">
+				<a-input v-model:value="dataForm.fileName" placeholder="请输入资源名称" :rows="4"  maxlength="50" showCount/>
+			</a-form-item>
+
+		</a-form>
+	</a-modal>
+</template>
+
+<script setup>
+	import { ref, defineProps, computed } from 'vue'
+	import { required } from '@/utils/formRules'
+	import resourceAuditApi from '@/api/resourceAudit.js'
+	import VideoPlayer from './VideoPlayer.vue'
+	import sysConfig from '@/config/index'
+	// import videoPlayer from 'vue-video-player'
+	const emit = defineEmits(['confirm'])
+	const visibles = ref(false)
+	const dataForm = reactive({
+		fileId: undefined,
+		fileName: undefined
+	})
+	// fileId
+	// 	:
+	// 	"1981553204412776448"
+	// fileName
+	// 	:
+	// 	"p一百"
+	const formRules = {
+		fileName: [required('请输入资源名称')],
+	}
+	const handleCancel = () => {
+		close()
+	}
+	const handleOk = () => {
+		emit('confirm', dataForm)
+		close()
+	}
+	const open = (data) => {
+		dataForm.fileId = data.fileId
+		dataForm.fileName = data.fileName
+		visibles.value = true
+	}
+	const close = () => {
+		visibles.value = false
+	}
+	const playerReadied = (player) => {
+		// player ready
+	}
+	// 获取资源详情
+	const getDetail = () => {
+		resourceAuditApi.detail({ id: props.recordData.id }).then((res) => {
+			console.log(res.data, '资源详情')
+			detailData.value = res.data
+		})
+	}
+	onMounted(() => {
+
+	})
+
+	defineExpose({open})
+</script>
+<style scoped>
+	.audit-container {
+		display: flex;
+		gap: 20px;
+		height: 500px;
+		border-bottom: 1px solid #ccc;
+		padding-bottom: 10px;
+	}
+
+	.file-preview {
+		flex: 1;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		background-color: #f5f5f5;
+		border-radius: 4px;
+		overflow: hidden;
+	}
+
+	.preview-placeholder {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		gap: 10px;
+		color: #666;
+	}
+
+	.audit-info {
+		flex: 1;
+		display: flex;
+		flex-direction: column;
+		gap: 20px;
+	}
+
+	.info-section {
+		background: #fff;
+		border-radius: 4px;
+	}
+
+	.info-section h4 {
+		margin-bottom: 12px;
+		font-size: 16px;
+		color: #333;
+	}
+
+	.description-box {
+		padding: 12px;
+		border: 1px solid #d9d9d9;
+		border-radius: 4px;
+		max-height: 150px;
+		overflow-y: auto;
+	}
+
+	.audit-action {
+		margin-top: auto;
+		padding-top: 20px;
+		border-top: 1px dashed #d9d9d9;
+	}
+
+	/* 调整描述列表样式 */
+	:deep(.ant-descriptions-item-label) {
+		width: 100px;
+		font-weight: normal;
+	}
+	.ant-modal-footer {
+		display: none !important; /* 检查是否被覆盖 */
+	}
+	.preview-iframe {
+		width: 100%;
+		height: 100%;
+		min-height: 500px;
+	}
+	.preview-iframe iframe {
+		width: 100%;
+		height: 100%;
+		border: none;
+	}
+</style>

+ 1 - 1
src/views/myResources/personalResources/index.vue

@@ -122,7 +122,7 @@
 			case 'resources':
 				return myResources
 			case 'favorites':
-				return defineAsyncComponent(() => import('@/views/myFavorites/index.vue'))
+				return defineAsyncComponent(() => import('@/views/student/myFavorites/index.vue'))
 			// case 'albums':
 			// 	return defineAsyncComponent(() => import('@/views/myResources/personalResources/Albums.vue'))
 			// case 'questionBank':

+ 114 - 91
src/views/myResources/releaseModal.vue

@@ -7,7 +7,11 @@
 		@ok="handleUploadOk"
 		@cancel="handleUploadCancel"
 	>
-		<a-form :model="uploadForm" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
+		<a-form :model="uploadForm"
+				ref="formRef"
+				:rules="formRules"
+				:label-col="{ span: 6 }"
+				:wrapper-col="{ span: 16 }">
 			<!-- <a-form-item label="资源名称" name="resourceName">
 				<a-input v-model:value="uploadForm.resourceName" placeholder="请输入资源名称" />
 			</a-form-item> -->
@@ -24,122 +28,141 @@
 			</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.courseDesc" placeholder="请输入资源描述" :rows="4"  maxlength="50" showCount/>
 			</a-form-item>
 
 			<a-form-item label="上传封面" name="coverImageId">
-				<coverUpload @handleChangeCover="handleChangeCover" @handleRemoveCover="handleRemoveCover"></coverUpload>
+				<coverUpload
+					:sizeMax="50000"
+					@handleChangeCover="handleChangeCover"
+							 @handleRemoveCover="handleRemoveCover"></coverUpload>
 			</a-form-item>
 		</a-form>
 	</a-modal>
 </template>
 
 <script setup>
-	import { ref, reactive } from 'vue'
-	import { Modal, Upload, message } from 'ant-design-vue'
-	import { PlusOutlined } from '@ant-design/icons-vue'
-	import coverUpload from './coverUpload/index.vue'
-	import tool from '@/utils/tool'
-	import sysConfig from "@/config";
-
-	// const props = defineProps({
-	// 	isState: {
-	// 		type: Number,
-	// 		required: true,
-	// 		default: 0
-	// 	}
-	// })
-	const headers = ref({
-		token: tool.data.get('TOKEN')
-	})
-	const action = ref(sysConfig.API_URL+'/api/webapp/dev/file/uploadMinioReturnId')
-	const emit = defineEmits(['close', 'confirm'])
-
-	// 表单数据
-	const uploadForm = reactive({
+import {ref, reactive} from 'vue'
+import {Modal, Upload, message} from 'ant-design-vue'
+import {PlusOutlined} from '@ant-design/icons-vue'
+import coverUpload from './coverUpload/index.vue'
+import tool from '@/utils/tool'
+import sysConfig from "@/config";
+import {required} from "@/utils/formRules";
+import dictApi from "@/api/dev/dictApi";
+
+// const props = defineProps({
+// 	isState: {
+// 		type: Number,
+// 		required: true,
+// 		default: 0
+// 	}
+// })
+const headers = ref({
+	token: tool.data.get('TOKEN')
+})
+const formRef = ref(null)
+
+const formRules = {
+	courseDesc: [required('请输入资源描述', 'blur')],
+	coverImageId: [required('请选择封面', 'change')],
+}
+const action = ref(sysConfig.API_URL + '/api/webapp/dev/file/uploadMinioReturnId')
+const emit = defineEmits(['close', 'confirm'])
+
+// 表单数据
+const uploadForm = reactive({
+	// resourceName: null,
+	// resourceType: null,
+	// courseName: null,
+	courseDesc: undefined,
+	coverImageId: undefined
+})
+
+// 封面文件列表
+const coverFileList = ref([])
+const fileList = ref([])
+
+const uploadModalVisible = ref(true)
+
+// 显示上传模态框
+// const showUploadModal = () => {
+//   uploadModalVisible.value = true
+// }
+
+// 关闭模态框
+const handleUploadCancel = () => {
+	// uploadModalVisible.value = false
+	Object.assign(uploadForm, {
 		// resourceName: null,
-		// resourceType: null,
+		courseDesc: undefined,
 		// courseName: null,
-		resourceDesc: null,
-		coverImageId: '123'
+		// courseDesc: null,
+		coverImageId: undefined
 	})
+	fileList.value = []
+	coverFileList.value = []
+	emit('close')
+}
 
-	// 封面文件列表
-	const coverFileList = ref([])
-	const fileList = ref([])
-
-	const uploadModalVisible = ref(true)
-
-	// 显示上传模态框
-	// const showUploadModal = () => {
-	//   uploadModalVisible.value = true
-	// }
+const handleUploadOk = () => {
+	formRef.value.validate().then(() => {
 
-	// 关闭模态框
-	const handleUploadCancel = () => {
-		// uploadModalVisible.value = false
-		Object.assign(uploadForm, {
-			// resourceName: null,
-			resourceDesc: null,
-			// courseName: null,
-			// courseDesc: null,
-			coverImageId: null
-		})
-		fileList.value = []
-		coverFileList.value = []
-		emit('close')
-	}
-
-	const handleUploadOk = () => {
 		// 这里可以添加实际的上传逻辑
-		console.log('Upload confirmed:', uploadForm.fileId, fileList.value, coverFileList.value)
-		emit('confirm', uploadForm)
+		// console.log('Upload confirmed:', uploadForm.fileId, fileList.value, coverFileList.value)
+		console.log('追一下',uploadForm)
+		let json = JSON.parse(JSON.stringify(uploadForm))
+		emit('confirm', json)
 		// uploadModalVisible.value = false
 		Object.assign(uploadForm, {
 			// resourceName: null,
 			// resourceType: null,
 			// courseName: null,
-			resourceDesc: null,
-			coverImageId: null
+			courseDesc: undefined,
+			coverImageId: undefined
 		})
 		fileList.value = []
 		coverFileList.value = []
-	}
+	})
+
+
+}
 
-	// 封面文件id
-	const handleChangeCover = (fileId) => {
-		uploadForm.coverImageId = fileId
-	}
+// 封面文件id
+const handleChangeCover = (fileId) => {
+	uploadForm.coverImageId = fileId
+}
 
-	// 移除封面文件
-	const handleRemoveCover = () => {
-		uploadForm.coverImageId = null
-	}
+// 移除封面文件
+const handleRemoveCover = () => {
+	uploadForm.coverImageId = undefined
+}
 </script>
 
 <style scoped>
-	.upload-area {
-		border: 2px dashed #3ca9f5;
-		padding: 40px;
-		text-align: center;
-	}
-
-	.upload-area p {
-		margin: 10px 0;
-	}
-
-	.file-item {
-		display: flex;
-		align-items: center;
-		margin: 10px 0;
-	}
-
-	.file-item .ant-progress {
-		flex: 1;
-		margin: 0 10px;
-	}
-	.ant-upload-picture-card-wrapper .ant-upload-list-item-info .ant-upload-list-item-filename {
-		display: block; /* 确保文件名显示为块级元素 */
-		margin-top: 8px; /* 调整文件名与图片之间的间距 */
-	}
+.upload-area {
+	border: 2px dashed #3ca9f5;
+	padding: 40px;
+	text-align: center;
+}
+
+.upload-area p {
+	margin: 10px 0;
+}
+
+.file-item {
+	display: flex;
+	align-items: center;
+	margin: 10px 0;
+}
+
+.file-item .ant-progress {
+	flex: 1;
+	margin: 0 10px;
+}
+
+.ant-upload-picture-card-wrapper .ant-upload-list-item-info .ant-upload-list-item-filename {
+	display: block; /* 确保文件名显示为块级元素 */
+	margin-top: 8px; /* 调整文件名与图片之间的间距 */
+}
 </style>

+ 92 - 73
src/views/myResources/resourceUpload.vue

@@ -8,12 +8,12 @@
 		width="600px"
 	>
 		<a-form :model="formState" :rules="rules" ref="formRef">
-			<a-form-item label="院系" name="collegeId" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
+			<a-form-item label="单位" name="collegeId" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
 				<a-cascader
 					v-model:value="majorIdName"
 					:options="collegeMajorOptions"
 					:fieldNames="{ label: 'name', value: 'id', children: 'children' }"
-					placeholder="请选择院系"
+					placeholder="请选择单位"
 					style="width: 300px"
 					changeOnSelect
 					@change="changeCollegeMajor"
@@ -59,7 +59,7 @@
 					<a-tag
 						v-for="(keyword, index) in formState.keywordValue"
 						:key="index"
-						closable
+
 						@close="handleRemoveKeyword(keyword, index)"
 					>
 						{{ keyword }}
@@ -172,11 +172,12 @@
 	const action = ref(sysConfig.API_URL+'/api/webapp/dev/file/uploadMinioReturnId')
 	const formState = reactive({
 		userfileIds: null, //资源文件id
-		coverImage: null, //封面id
+		coverImage: undefined, //封面id
 		// courseId: null, //课程
 		// majorType: null, //专业
 		resourceType: null, //资源类型一级
 		resourceTwoType: null, //资源类型二级
+		resourceThreeType: null, //资源类型二级
 		collegeId: null, //院校一级id
 		collegeTwoId: null, //院校二级id
 		collegeThreeId: null, //院校三级id
@@ -191,6 +192,8 @@
 		isHot: 0 // 资源是否热门
 	})
 	const coverImagePath = ref() // 预览回显
+	const teacherData = ref(null) // 预览回显
+	const itemDatas = ref(null) // 预览回显
 	const formRef = ref() // 添加表单引用
 	const collegeMajorOptions = ref([]) //院系
 	const resourceTypeOptions = ref([]) //资源类型下拉数据
@@ -200,8 +203,9 @@
 	const majorOptions = ref([]) //专业
 	const courseOptions = ref([]) //课程
 	const userRelateIdss = ref([])
+	const myHot = ref({})
 	// 上传资源模态框
-	const uploadModalVisible = ref(true)
+	const uploadModalVisible = ref(false)
 	// 用户选择模态框
 	const userReleaseVisible = ref(false)
 
@@ -230,6 +234,23 @@
 		// 	console.error('部分请求失败:', err)
 		// }
 	}
+	const open=(data,itemData)=>{
+
+		uploadModalVisible.value = true
+		teacherData.value = data
+		itemDatas.value = itemData
+
+		console.log('没有吗',' data ',data,' itemData ',itemData )
+		getOrgTreeSelector()
+		getHotKeywords()
+		getResourceTypeTree()
+		getDetail(itemDatas.value)
+		// if (props.isState == 1) {
+		//
+		// } else {
+		// 	getformState()
+		// }
+	}
 	const onSuccess = (uploadFileList) => {
 		let list = []
 		console.log('formState.userfileIds是数组:', uploadFileList)
@@ -259,11 +280,11 @@
 
 	// 移除封面文件
 	const handleRemoveCover = () => {
-		formState.coverImage = null
+		formState.coverImage = undefined
 	}
 	const rules = {
 		// courseTypeName: [{ required: true, message: '请选择资源类型', trigger: 'change' }],
-		collegeId: [{ required: true, message: '请选择院系', trigger: 'blur' }],
+		collegeId: [{ required: true, message: '请选择单位', trigger: 'blur' }],
 		keywordValue: [
 			{ required: true, message: '请选择关键词', trigger: 'blur' },
 			{ validator: validateKeywords, trigger: 'blur' }
@@ -273,7 +294,8 @@
 		resourceDesc: [
 			{ required: true, message: '请输入资源描述', trigger: 'blur' },
 			{ pattern: /^[\s\S]{10,500}$/, message: '描述长度应在10-500字符之间', trigger: 'blur' }
-		]
+		],
+		coverImage: [{ required: true, message: '请上传封面', trigger: 'blur' }],
 	}
 	const newKeyword = ref('') //关键词
 	const handleAddKeyword = (e) => {
@@ -297,20 +319,53 @@
 	}
 
 	const handleRemoveKeyword = (keywordName, index) => {
-		console.log(keywordName, index)
-		// 1. 从显示列表中删除
-		formState.keywordValue = formState.keywordValue.filter((name) => name !== keywordName)
-		// 2. 更新checkbox的绑定值
-		formState.keyword = HotKeywordsOptions.value
-			.filter((option) => formState.keywordValue.includes(option.label))
-			.map((item) => item.value)
+
+		console.log('删除的东西 01 ', ' keywordName ',keywordName,' index ',index)
+		console.log('删除的东西 02 ', ' formState.keyword ',formState.keyword)
+
+
+		console.log(keywordName, index,myHot.value)
+		// // 1. 从显示列表中删除
+		// formState.keywordValue = formState.keywordValue.filter((name) => name !== keywordName)
+		// // console.log('看看点击取消了什么',' formState.keywordValue ',formState.keywordValue)
+		// // formState.keyword
+		// // 2. 更新checkbox的绑定值
+		// formState.keyword = HotKeywordsOptions.value
+			// .filter((option) => formState.keywordValue.includes(option.label))
+			// .map((item) => item.value)
+		// // formState.keyword = formState.keyword.filter((item, i) => i !== index)
+		// // console.log('看看点123123',' formState.keyword ',formState.keyword,' index ',index)
+
+		let myId = myHot.value[keywordName]
+		// console.log('换算出来',formState.keyword,index)
+
+		// for (let i = formState.keyword.length-1; i > 0 ; i--) {
+		// 	// console.log('看看',formState.keyword[i] , i , index)
+		// 	console.log('要删除第几个' ,i, index ,  formState.keyword[i])
+		// 	if( i == index){
+		//
+		// 		// formState.keyword.splice(i,1)
+		// 	}
+		// }
+		// console.log('换算出来  结果',formState.keyword)
+
+		// formState.keyword.filter((item, i) => item != keywordName)
+		// formState.keyword = [{'专业' : 1}]
+		// formState.keyword.filter((item, i) => item != index)
+		// formState.keyword.filter((option) => formState.keywordValue.includes(option.value))
+		// 	.map((item) => item.label)
+		// console.log('换算出来 剩下',formState.keyword)
+		// console.log('还有吗',formState.keywordValue)
+
 	}
 	const getHotKeywords = () => {
+		myHot.value = {}
 		resourceAuditApi
 			.HotKeywords()
 			.then((res) => {
 				console.log(res.data, '获取热门关键词')
 				HotKeywordsOptions.value = res.data.map((it) => {
+					myHot.value[it.wordName] = it.id
 					return {
 						value: it.id,
 						label: it.wordName
@@ -335,9 +390,11 @@
 	}
 	const handleChangeKeyword = (checkedValues) => {
 		formState.keyword = checkedValues
+		console.log('点击checkbox',checkedValues )
 		formState.keywordValue = HotKeywordsOptions.value
 			.filter((option) => checkedValues.includes(option.value))
 			.map((item) => item.label)
+		console.log('点击checkbox  123 ',formState.keywordValue )
 	}
 	const setPublicStatus = (status) => {
 		formState.authType = status
@@ -358,17 +415,6 @@
 			})
 	}
 	//院系组织查询
-	const getCourseAllList = () => {
-		resourceAuditApi
-			.courseAllList()
-			.then((res) => {
-				console.log(res.data, '获取全部课程')
-				courseOptions.value = res.data
-			})
-			.catch((err) => {
-				console.log(err)
-			})
-	}
 	const changeCollegeMajor = (value, selectedOptions) => {
 		console.log('Selected:', value, selectedOptions)
 		if (!value) {
@@ -398,8 +444,9 @@
 			return false
 		}
 		resourceName.value = selectedOptions.map((it) => it.name).join('/')
-		formState.resourceType = value[0] || null
-		formState.resourceTwoType = value[1] || null
+		formState.resourceType = value[0] || undefined
+		formState.resourceTwoType = value[1] || undefined
+		formState.resourceThreeType = value[2] || undefined
 	}
 	const getCollegeMajor = (id) => {
 		resourceAuditApi
@@ -422,16 +469,18 @@
 		try {
 			await formRef.value.validate()
 			console.log('formState.userfileIds是:提交了', formState.userfileIds)
+			console.log('itemData',' teacherData ',teacherData.value,' itemDatas ', itemDatas.value)
 			if (!formState.userfileIds) {
-				Modal.error({ content: '请先上传文件!' })
+				Modal.error({ content: '请先上传文件!!' })
 				return
 			}
-			if (props.isState == 1) {
 				const formData = {
-					id: props.resourcesId,
+					id: itemDatas.value.id,
+					teacherId : teacherData.value.teacherId,
 					userfileIds: formState.userfileIds,
 					resourceType: formState.resourceType,
 					resourceTwoType: formState.resourceTwoType,
+					resourceThreeType: formState.resourceThreeType,
 					collegeId: formState.collegeId,
 					collegeTwoId: formState.collegeTwoId,
 					collegeThreeId: formState.collegeThreeId,
@@ -446,7 +495,7 @@
 					userRelateIds: formState.authType == 1 ? formState.userRelateIds : null
 				}
 				resourceAuditApi
-					.edit(formData)
+					.stuLinkResourceRecordEdit(formData)
 					.then((res) => {
 						emit('getList')
 						Modal.success({ content: '资源编辑成功' })
@@ -455,36 +504,7 @@
 						Modal.success({ content: '资源编辑失败' })
 						console.log(err)
 					})
-			} else {
-				const formData = {
-					userfileIds: formState.userfileIds,
-					resourceType: formState.resourceType,
-					resourceTwoType: formState.resourceTwoType,
-					collegeId: formState.collegeId,
-					collegeTwoId: formState.collegeTwoId,
-					collegeThreeId: formState.collegeThreeId,
-					keywordValue: formState.keywordValue.join(','),
-					keyword: formState.keyword.join(','),
-					resourceDesc: formState.resourceDesc,
-					isHot: formState.isHot,
-					isRecommend: formState.isRecommend,
-					coverImage: formState.coverImage,
-					publicStatus: formState.publicStatus,
-					authType: formState.authType,
-					userRelateIds: formState.authType == 1 ? formState.userRelateIds : null
-				}
-				console.log(formData, '上传数据')
-				resourceAuditApi
-					.add(formData)
-					.then((res) => {
-						emit('getList')
-						Modal.success({ content: '资源上传成功' })
-					})
-					.catch((err) => {
-						Modal.success({ content: '资源上传失败' })
-						console.log(err)
-					})
-			}
+
 		} catch (error) {
 			if (error.errorFields) {
 				// 表单验证错误
@@ -497,14 +517,15 @@
 		}
 	}
 	// 获取资源详情
-	const getDetail = () => {
-		resourceAuditApi.detail({ id: props.resourcesId }).then((res) => {
+	const getDetail = (itemDatas) => {
+		resourceAuditApi.detail({ id: itemDatas.id}).then((res) => {
 			console.log(res.data, '资源详情')
 			formState.userfileIds = res.data.fileId
 			formState.userRelateIds = res.data.userRelateIdList?.join(',')
 			userRelateIdss.value = res.data.userRelateIdList
 			formState.resourceType = res.data.resourceType
 			formState.resourceTwoType = res.data.resourceTwoType
+			formState.resourceThreeType = res.data.resourceThreeType
 			formState.collegeId = res.data.collegeId
 			formState.coverImage = res.data.coverImage
 			coverImagePath.value = res.data.coverImagePath
@@ -517,6 +538,10 @@
 			// formState.majorId = res.data.majorId
 			formState.keywordValue = res.data.keywordValue?.split(',')
 			formState.keyword = res.data.keyword.split(',').filter(Boolean).map(Number)
+
+			formState.isRecommend = Number(res.data.isRecommend) || 0
+			formState.isHot = Number(res.data.isHot) || 0
+			formState.authType = res.data.authType
 		})
 	}
 	// 获取历史添加表单
@@ -527,6 +552,7 @@
 			formState.userRelateIds = res.data.userRelateIdList?.join(',')
 			userRelateIdss.value = res.data.userRelateIdList
 			formState.resourceTwoType = res.data.resourceTwoType
+			formState.resourceThreeType = res.data.resourceThreeType
 			formState.collegeId = res.data.collegeId
 			formState.collegeTwoId = res.data.collegeTwoId
 			formState.collegeThreeId = res.data.collegeThreeId
@@ -589,16 +615,9 @@
 		}
 	}
 	onMounted(() => {
-		getOrgTreeSelector()
-		getCourseAllList()
-		getHotKeywords()
-		getResourceTypeTree()
-		if (props.isState == 1) {
-			getDetail()
-		} else {
-			getformState()
-		}
+
 	})
+	defineExpose({open})
 </script>
 <style scoped>
 	.upload-area {

+ 2 - 2
src/views/myResources/userSelection.vue

@@ -1,6 +1,6 @@
 <template>
 	<a-drawer
-		v-model:visible="visible"
+		v-model:visible="props.visible"
 		title="选择可见成员"
 		placement="right"
 		width="50%"
@@ -169,7 +169,7 @@
 	// 替换原来的onSelect方法
 	const onCheck = (checkedKeysValue, { checked, node, checkedNodes }) => {
 		// 过滤掉非叶子节点
-		const leafNodes = checkedNodes.filter((node) => node.isLeaf)
+		const leafNodes = checkedNodes.filter((node) => node.isLeaf&& node.infoType=="user")
 		checkedKeys.value = leafNodes.map((node) => node.id)
 		selectedUsers.value = leafNodes.map((node) => ({
 			id: node.id,

+ 3 - 1
src/views/portal/components/Header.vue

@@ -16,10 +16,11 @@
 						<a-menu-item key="student/paper/4">我的作业</a-menu-item>
 						<a-menu-item key="student/classCollect">课程收藏</a-menu-item>
 					</a-sub-menu>
-					<a-menu-item key="student/link">超链接</a-menu-item>
+					<a-menu-item key="student/link">教师资源超链接</a-menu-item>
 					<a-menu-item key="student/myFavorites">我的收藏</a-menu-item>
 				</a-menu>
 			</div>
+			<MiniYun></MiniYun>
 			<div class="header-right">
 				<headerIcon></headerIcon>
 				<a-dropdown v-if="userInfo" class="mr-4">
@@ -63,6 +64,7 @@
 	import { useRouter, useRoute } from 'vue-router'
 	import tool from '@/utils/tool'
 	import headerIcon from './headerIcon.vue'
+	import MiniYun from "@/components/MiniYun/index.vue";
 	import { globalStore } from '@/store'
 	import { Modal, message } from 'ant-design-vue'
 	import EventBus from "@/utils/EventBus";

+ 1 - 1
src/views/resourceCenter/components/TabSwitcherNew.vue

@@ -2,7 +2,7 @@
 	<div class="tab-switcher">
 		<div :class="{ active: selectedTab === 'latest' }" @click="selectTab('latest')"><span style="font-size: 12px">最新</span></div>
 		<div :class="{ active: selectedTab === 'hot' }" @click="selectTab('hot')"><span style="font-size: 12px">热门</span></div>
-		<div :class="{ active: selectedTab === 'new' }" @click="selectTab('new')"><span style="font-size: 12px">素材库</span></div>
+		<div :class="{ active: selectedTab === 'new' }" @click="selectTab('new')"><span style="font-size: 12px">课程素材库</span></div>
 	</div>
 </template>
 

+ 2 - 2
src/views/resourceDetails/components/TallList.vue

@@ -17,8 +17,8 @@
 							<span style="display: block; font-size: 12px">{{ userInfo.name }}</span>
 						</div>
 
-						<div style="width: 100%; height: 100%; display: flex; position: relative">
-							<a-textarea placeholder="" :rows="4" v-model:value="talk" style="margin-top: 5px" :maxlength="500" />
+						<div style="width: 100%; height: 100%;  position: relative">
+							<a-textarea placeholder="" :rows="4" v-model:value="talk" style="margin-top: 5px" maxlength="100" showCount />
 						</div>
 					</div>
 					<div style="display: flex; justify-content: flex-end; margin-top: 10px">

+ 1 - 1
src/views/student/learningFootprint/index.vue

@@ -79,7 +79,7 @@
 			dataIndex: 'hourName'
 		},
 		{
-			title: '学习时长',
+			title: '课程时长',
 			dataIndex: 'duration'
 		},
 		{

+ 2 - 2
src/views/student/paper/index.vue

@@ -7,10 +7,10 @@
 			</div>
 		</a-card>
 		<a-card style="width: 100%" class="mt-3">
-			<div class="flc mb-4">
+			<div class="flc mb-4" v-if="examType == 1">
 				<div class="fcc mr-1">分类:</div>
 				<a-select
-					v-if="examType == 1"
+
 					v-model:value="examTypeSelect"
 					placeholder="所有分类"
 					style="width: 110px"