VideoDetails.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. <template>
  2. <div>
  3. <h1>{{ resName }}</h1>
  4. <div style="height: 25px"></div>
  5. <div class="user-info-container">
  6. <div class="video-info">
  7. <div
  8. v-if="videoFormat == 'jpg' || videoFormat == 'bmp' || videoFormat == 'png' || videoFormat == 'jpeg'"
  9. style="width: 100%; height: 350px"
  10. >
  11. <a-image width="100%" height="350px" :src="fileSrc(resSrc)" :preview="true" />
  12. </div>
  13. <div
  14. v-if="
  15. videoFormat == 'mkv' ||
  16. videoFormat == 'mp4' ||
  17. videoFormat == 'wmv' ||
  18. videoFormat == 'avi' ||
  19. videoFormat == 'flv' ||
  20. videoFormat == 'mpeg' ||
  21. videoFormat == 'mpg' ||
  22. videoFormat == 'rmvb' ||
  23. videoFormat == 'mov'
  24. "
  25. style="width: 100%; height: 350px"
  26. >
  27. <video :src="fileSrc(resSrc)" controls style="width: 100%; height: 100%" />
  28. </div>
  29. <div
  30. v-if="
  31. videoFormat == 'docx' ||
  32. videoFormat == 'doc' ||
  33. videoFormat == 'ppt' ||
  34. videoFormat == 'pptx' ||
  35. videoFormat == 'xls' ||
  36. videoFormat == 'xlsx' ||
  37. videoFormat == 'pdf'
  38. "
  39. style="width: 100%; height: 350px"
  40. >
  41. <FilePreviewer v-if="resSrc" :fileUrl="fileSrc(resSrc)" :fileName="resName" :fileType="fileType" />
  42. <div v-if="!resSrc">暂无数据</div>
  43. </div>
  44. <div style="height: 20px"></div>
  45. <!-- 用户信息部分 -->
  46. <div class="user-info" style="display: flex; flex-direction: column">
  47. <div style="display: flex; align-items: center; justify-content: space-between; width: 100%">
  48. <div style="display: flex; align-items: center">
  49. <div class="user-avatar"></div>
  50. <div class="user-details">
  51. <div class="user-name">{{ itemData.resourceCreaterUserName }}</div>
  52. <div class="publish-time">{{ itemData.uploadTime }}</div>
  53. </div>
  54. </div>
  55. <div style="display: flex; align-items: center" v-if="!isState">
  56. <div class="metrics">
  57. <div class="metric-item">
  58. <span>{{ itemData.viewCount }}</span>
  59. <span>播放量</span>
  60. </div>
  61. <div class="liene"></div>
  62. <div class="metric-item">
  63. <span>{{ talkNum }}</span>
  64. <span>评论</span>
  65. </div>
  66. <div class="liene"></div>
  67. <div class="metric-item">
  68. <span>{{ collectNum }}</span>
  69. <span>收藏</span>
  70. </div>
  71. </div>
  72. <div style="width: 10px"></div>
  73. <div class="actions">
  74. <div
  75. style="display: flex; align-items: center; cursor: pointer; margin-right: 5px"
  76. @click="handlerShareDialog"
  77. >
  78. <ExportOutlined />
  79. <div style="width: 10px"></div>
  80. <button class="share-btn">分享资源</button>
  81. </div>
  82. <div style="display: flex; align-items: center; cursor: pointer" @click="handlerCollection">
  83. <StarOutlined v-if="starTag == false" />
  84. <StarFilled v-if="starTag == true" />
  85. <div style="width: 10px"></div>
  86. <button class="favorite-btn">收藏资源</button>
  87. </div>
  88. </div>
  89. </div>
  90. </div>
  91. <!-- 课程信息部分 -->
  92. <div class="course-info">
  93. <span>
  94. {{ itemData.resourceDesc }}
  95. </span>
  96. </div>
  97. </div>
  98. </div>
  99. <div class="resInfo">
  100. <div style="display: flex; align-items: center">
  101. <div class="resInfoTitile"></div>
  102. <span style="display: block; font-weight: bold">资源信息</span>
  103. </div>
  104. <div style="display: flex; justify-content: center; align-items: center; height: 40px">
  105. <span style="display: block; font-weight: bold">{{ resName }}</span>
  106. </div>
  107. <div style="display: flex">
  108. <span style="font-weight: bold; margin-right: 10px">所属院系: </span>
  109. <span style="display: block; width: 200px">{{ department }}</span>
  110. </div>
  111. <div style="display: flex; align-items: center">
  112. <span style="font-weight: bold; margin-right: 10px">资源类型: </span>
  113. <span style="display: block; width: 200px">{{ courseType }}</span>
  114. </div>
  115. <div style="display: flex; align-items: center">
  116. <span style="font-weight: bold; margin-right: 10px">资源格式: </span>
  117. <span style="display: block; width: 200px">{{ videoFormat }}</span>
  118. </div>
  119. <div style="display: flex; align-items: center">
  120. <span style="font-weight: bold; margin-right: 10px">容量大小: </span>
  121. <span style="display: block; width: 200px">{{ videoSize }}</span>
  122. </div>
  123. <div style="display: flex; align-items: center">
  124. <span style="font-weight: bold; margin-right: 10px">发布时间: </span>
  125. <span style="display: block; width: 200px">{{ releaseTime }}</span>
  126. </div>
  127. <br />
  128. <span>资源标签</span>
  129. <div style="display: flex; width: 100%; flex-wrap: wrap">
  130. <a-tag style="margin-top: 5px" v-for="tag in tags" :key="tag">{{ tag }}</a-tag>
  131. </div>
  132. </div>
  133. </div>
  134. <ShareDialog ref="ShareDialogRef"></ShareDialog>
  135. </div>
  136. </template>
  137. <script setup>
  138. import { ref } from 'vue'
  139. import { Tag, Typography, Space, message } from 'ant-design-vue'
  140. import ShareDialog from './ShareDialog.vue'
  141. import { addViewCount, detail, add, cancel, queryList, resourcecentreDetail, getShareLink } from '@/api/portal'
  142. import { useRoute, useRouter } from 'vue-router'
  143. import sysConfig from '@/config/index'
  144. import pdfRes from '@/assets/images/pdf.png'
  145. import FilePreviewer from './FilePreviewer.vue'
  146. import EventBus from '@/utils/EventBus'
  147. import tool from '@/utils/tool'
  148. const route = useRoute()
  149. const router = useRouter()
  150. const props = defineProps({
  151. isState: {
  152. type: Number,
  153. default: () => null
  154. }
  155. })
  156. const itemData = ref({})
  157. const starTag = ref(false)
  158. const ShareDialogRef = ref(null)
  159. const resSrc = ref('')
  160. const fileType = ref('')
  161. const resName = ref('资源名称')
  162. const teacherName = ref('王某某')
  163. const department = ref('学院本级-航空机务教研室-三级架构名')
  164. const major = ref('初级飞行训练')
  165. const courseType = ref('必修')
  166. const videoFormat = ref('MP4')
  167. const videoDuration = ref('59:34')
  168. const videoSize = ref('598M')
  169. const releaseTime = ref('2025-10-01 11:33:59')
  170. const talkNum = ref(0)
  171. const collectNum = ref(0)
  172. const tags = ref(['标签名称1', '标签名称2', '标签名称3', '标签名称4', '标签名称5'])
  173. const emit = defineEmits(['selectTab', 'onGetPageCommentNew'])
  174. const listUnpublishedView = ref(null)
  175. const handleDownload = (src) => {
  176. window.open(src)
  177. }
  178. const handlerShareDialog = () => {
  179. getShareLink({ id: route.query.id })
  180. .then((res) => {
  181. if (res.code == 200) {
  182. ShareDialogRef.value.open(res.data.shareLink)
  183. }
  184. })
  185. .catch((err) => {})
  186. }
  187. const handlerCollection = async () => {
  188. const id = route.query.id
  189. if (id != undefined && id != '') {
  190. if (starTag.value == true) {
  191. await cancel({ resourceId: id })
  192. message.success('取消收藏')
  193. upDataDetailsNum()
  194. } else {
  195. await add({ resourceId: id })
  196. message.success('收藏成功')
  197. upDataDetailsNum()
  198. }
  199. queryList({ resourceId: id })
  200. .then((ress) => {
  201. if (ress.data == true) {
  202. starTag.value = true
  203. } else {
  204. starTag.value = false
  205. }
  206. })
  207. .catch((err) => {
  208. console.log(err)
  209. })
  210. }
  211. }
  212. const setData = (data) => {
  213. itemData.value = data
  214. }
  215. const fileSrc = computed(()=>e=>sysConfig.FILE_URL + e)
  216. const getData = (item) => {
  217. detail({ id: item.id })
  218. .then((res) => {
  219. if (res.code == 200) {
  220. itemData.value = res.data
  221. courseType.value = itemData.value.resourceALLTypeName.split(',').join('/')
  222. department.value = itemData.value.collegeAllIdName.split(',').join('/')
  223. teacherName.value = itemData.value.resourceCreaterUserName
  224. resName.value = itemData.value.fileName
  225. videoFormat.value = itemData.value.suffix
  226. releaseTime.value = itemData.value.uploadTime
  227. videoSize.value = itemData.value.FILESIZE ? itemData.value.FILESIZE + 'b' : ''
  228. tags.value = []
  229. itemData.value.keywordList.forEach((item) => {
  230. tags.value.push(item.wordName)
  231. })
  232. resSrc.value = itemData.value.priviewFileUrl
  233. fileType.value = itemData.value.suffix
  234. emit('onGetPageCommentNew', {
  235. resourceType: itemData.value.resourceType,
  236. resourceTwoType: itemData.value.resourceTwoType
  237. })
  238. }
  239. })
  240. .catch((err) => {})
  241. }
  242. const upDataList = (item) => {
  243. resourcecentreDetail({ id: item.id })
  244. .then((res) => {
  245. if (res.code == 200) {
  246. talkNum.value = res.data.commentNum
  247. collectNum.value = res.data.collectNum
  248. }
  249. })
  250. .catch((err) => {})
  251. }
  252. const fetchData = () => {
  253. const id = route.query.id
  254. if (id != undefined && id != '') {
  255. queryList({ resourceId: id })
  256. .then((res) => {
  257. if (res.data == true) {
  258. starTag.value = true
  259. } else {
  260. starTag.value = false
  261. }
  262. })
  263. .catch((err) => {
  264. console.log(err)
  265. })
  266. getData({ id: id })
  267. upDataList({ id: id })
  268. }
  269. }
  270. watch(
  271. () => route,
  272. (newRoute) => {
  273. fetchData() // 手动刷新数据的方法
  274. },
  275. { deep: true, immediate: true }
  276. )
  277. onMounted(() => {
  278. fetchData()
  279. })
  280. const upDataDetailsNum = () => {
  281. const id = route.query.id
  282. if (id != undefined && id != '') {
  283. upDataList({ id: id })
  284. }
  285. }
  286. const openResourceDetailsInner = (data) => {
  287. router.push({
  288. path: '/student/resourceDetails',
  289. query: {
  290. id: data.id,
  291. t: new Date().getTime()
  292. }
  293. })
  294. }
  295. defineExpose({
  296. setData
  297. })
  298. EventBus.off('upDataDetailsNum', upDataDetailsNum)
  299. EventBus.on('upDataDetailsNum', upDataDetailsNum)
  300. EventBus.off('openResourceDetailsInner', openResourceDetailsInner)
  301. EventBus.on('openResourceDetailsInner', openResourceDetailsInner)
  302. </script>
  303. <style scoped>
  304. .tab-switcher {
  305. display: flex;
  306. border-radius: 20px;
  307. border: 1px solid #1e90ff;
  308. overflow: hidden;
  309. }
  310. .tab-switcher div {
  311. padding: 2px 20px;
  312. background-color: #f5f5f5;
  313. cursor: pointer;
  314. }
  315. .tab-switcher div.active {
  316. background-color: #1e90ff;
  317. color: white;
  318. }
  319. .user-info {
  320. display: flex;
  321. align-items: center;
  322. width: 100%;
  323. height: 200px;
  324. border: 1px solid #dfe2e5;
  325. padding: 20px;
  326. }
  327. .resource-container {
  328. width: 100%;
  329. margin: 0 auto;
  330. padding: 20px;
  331. border: 1px solid #dfe2e5;
  332. }
  333. .user-avatar {
  334. width: 40px;
  335. height: 40px;
  336. background: #1e90ff;
  337. border-radius: 50%;
  338. margin-right: 10px;
  339. }
  340. .user-details {
  341. flex: 1;
  342. }
  343. .user-name {
  344. font-size: 13px;
  345. font-weight: bold;
  346. }
  347. .publish-time {
  348. font-size: 12px;
  349. color: #999;
  350. margin-top: 5px;
  351. }
  352. .metrics {
  353. display: flex;
  354. align-items: center;
  355. justify-content: center;
  356. }
  357. .metric-item {
  358. display: flex;
  359. flex-direction: column;
  360. align-items: center;
  361. padding-left: 10px;
  362. padding-right: 10px;
  363. }
  364. .metric-item span:first-child {
  365. font-size: 18px;
  366. font-weight: bold;
  367. }
  368. .metric-item span:last-child {
  369. font-size: 12px;
  370. color: #666;
  371. }
  372. .actions {
  373. display: flex;
  374. }
  375. .share-btn,
  376. .favorite-btn {
  377. background-color: transparent;
  378. border: none;
  379. cursor: pointer;
  380. }
  381. .course-info {
  382. margin-top: 20px;
  383. line-height: 1.6;
  384. display: flex;
  385. width: 100%;
  386. }
  387. .liene {
  388. height: 35px;
  389. width: 1px;
  390. background: #00000018;
  391. }
  392. .resource-container {
  393. width: 100%;
  394. margin: 0 auto;
  395. padding: 20px;
  396. border: 1px solid #dfe2e5;
  397. }
  398. h1 {
  399. text-align: center;
  400. margin-bottom: 20px;
  401. }
  402. .ant-descriptions {
  403. margin-top: 20px;
  404. }
  405. .ant-typography-title {
  406. margin-top: 20px;
  407. }
  408. .ant-typography-paragraph {
  409. margin-top: 10px;
  410. }
  411. .ant-space {
  412. margin-top: 10px;
  413. }
  414. .video-info {
  415. height: 100%; /* 确保填满容器高度 */
  416. flex: 1;
  417. }
  418. .resInfo {
  419. margin-left: 10px;
  420. width: 300px;
  421. height: 570px;
  422. border: 1px solid #dfe2e5;
  423. padding: 10px;
  424. }
  425. .user-info-container {
  426. display: flex;
  427. }
  428. .resInfoTitile {
  429. width: 5px;
  430. height: 14px;
  431. background-color: #1e90ff;
  432. margin-right: 5px;
  433. }
  434. .tallList {
  435. display: flex;
  436. flex-direction: column;
  437. width: 100%;
  438. height: 200px;
  439. border: 1px solid #dfe2e5;
  440. padding: 20px;
  441. margin-top: 10px;
  442. }
  443. .tallListInfo {
  444. color: rgba(0, 0, 0, 0.116);
  445. font-size: 12px;
  446. }
  447. </style>