| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- <template>
- <div>
- <!-- 顶部标题栏 -->
- <div>
- <div style="position: relative; display: flex; align-items: center; justify-content: center; height: 48px; background: #fff;">
- <span style="position: absolute; left: 16px; cursor: pointer;" @click="handleBack">
- <i class="el-icon-arrow-left" style="font-size: 20px; color: #333;"></i>
- </span>
- <span style="font-weight: bold; font-size: 16px;">课程管理</span>
- </div>
- <el-select
- v-model="courseId"
- placeholder="请点击选择课程"
- class="courseSelect"
- popper-class="courseSelectList"
- @visible-change="handleSelectOpen"
- @change="courseChange"
- ref="courseSelect"
- >
- <el-option
- v-for="item in courseList"
- :key="item.courseId"
- :label="item.courseName"
- :value="item.courseId">
- </el-option>
- <div
- v-if="courseQueryParams.hasNextPage"
- class="load-more"
- v-loading="courseQueryParams.loading"
- >
- <span v-if="!courseQueryParams.loading">加载更多</span>
- </div>
- </el-select>
- <el-input
- placeholder="请输入课程名称"
- v-model="videoQueryParams.keyword"
- class="videoSearch"
- @keyup.enter.native="handleSearch"
- >
- </el-input>
- </div>
- <!-- 列表 -->
- <div :class="extend ? 'infinite-list-wrapper-extend' : 'infinite-list-wrapper'" style="overflow:auto">
- <ul class="video-list" v-infinite-scroll="loadMoreVideo">
- <li v-for="item in videoList" :key="item.videoId" class="video-item">
- <div class="video-thumbnail">
- <img :src="item.thumbnail || defaultImage" alt="" @error="handleImgError">
- <div class="video-duration">{{formatDuration(item.duration)}}</div>
- </div>
- <div class="video-info">
- <div class="video-title">{{item.title}}</div>
- <div class="video-meta">
- <i class="el-icon-s-order" style="font-size: 18px;margin-right: 5px"></i>
- <span class="video-date">{{formatDate(item.createTime)}}</span>
- </div>
- </div>
- <div class="video-actions">
- <el-button
- type="primary"
- size="mini"
- round
- :loading="sharingVideoId === item.videoId"
- :disabled="sharingVideoId === item.videoId"
- @click="handleShare(item)">分享课程</el-button>
- </div>
- </li>
- </ul>
- <div v-if="videoList.length === 0 && !videoQueryParams.loading" class="empty-tip">
- <i class="el-icon-video-camera"></i>
- <p>暂无视频</p>
- </div>
- <div v-if="videoQueryParams.loading" class="loading-more">
- <i class="el-icon-loading"></i>
- <span>加载中...</span>
- </div>
- <div v-if="!videoQueryParams.hasNextPage && videoList.length > 0" class="no-more">
- <span>没有更多了</span>
- </div>
- </div>
- </div>
- </template>
- <script>
- import {createMiniLink, getFsCourseListBySidebar, getFsCourseVideoListBySidebar} from "@/api/qw/im";
- export default {
- name: "courseManage",
- props: {
- userId: {
- type: Number,
- default: () => null
- },
- extend: {
- type: Boolean,
- default: false
- }
- },
- data() {
- return {
- courseId: null,
- courseQueryParams: {
- extId: this.userId,
- page: 1,
- limit: 10,
- hasNextPage: false,
- loading: false,
- },
- courseList: [],
- scrollEvent: null,
- videoQueryParams: {
- courseId: null,
- keyword: '',
- page: 1,
- limit: 10,
- hasNextPage: false,
- loading: false,
- },
- videoList: [],
- defaultImage: require('@/assets/image/default-image.png'),
- sharingVideoId: null
- }
- },
- created() {
- this.getFsCourseListBySidebar()
- },
- methods: {
- handleBack() {
- this.$emit('back')
- },
- getFsCourseListBySidebar() {
- this.courseQueryParams.loading = true;
- getFsCourseListBySidebar(this.courseQueryParams).then(response => {
- if (this.courseQueryParams.page === 1) {
- this.courseList = response.data.list;
- } else {
- this.courseList = [...this.courseList, ...response.data.list];
- }
- this.courseQueryParams.hasNextPage = response.data.hasNextPage;
- this.courseQueryParams.loading = false;
- }).catch(() => {
- this.courseQueryParams.loading = false;
- });
- },
- loadMore() {
- if (!this.courseQueryParams.hasNextPage || this.courseQueryParams.loading) return;
- this.courseQueryParams.page += 1;
- this.getFsCourseListBySidebar();
- },
- handleSelectOpen(visible) {
- if (visible) {
- // 下拉框打开时,添加滚动监听
- this.$nextTick(() => {
- const selectDropdown = document.querySelector('.el-select-dropdown.courseSelectList');
- if (selectDropdown) {
- const dropdownList = selectDropdown.querySelector('.el-select-dropdown__wrap');
- if (dropdownList) {
- // 移除之前可能存在的事件监听
- if (this.scrollEvent) {
- dropdownList.removeEventListener('scroll', this.scrollEvent);
- }
- // 添加新的滚动监听
- this.scrollEvent = () => {
- const { scrollTop, scrollHeight, clientHeight } = dropdownList;
- // 当滚动到距离底部20px时,加载更多
- if (scrollHeight - scrollTop - clientHeight < 20) {
- this.loadMore();
- }
- };
- dropdownList.addEventListener('scroll', this.scrollEvent);
- }
- }
- });
- } else {
- // 下拉框关闭时,移除滚动监听
- if (this.scrollEvent) {
- const selectDropdown = document.querySelector('.el-select-dropdown.courseSelectList');
- if (selectDropdown) {
- const dropdownList = selectDropdown.querySelector('.el-select-dropdown__wrap');
- if (dropdownList) {
- dropdownList.removeEventListener('scroll', this.scrollEvent);
- }
- }
- }
- }
- },
- courseChange(courseId) {
- this.videoQueryParams.courseId = courseId;
- this.handleSearch()
- },
- handleSearch() {
- if (!this.videoQueryParams.courseId) {
- return
- }
- this.videoQueryParams.page = 1;
- this.getFsCourseVideoListBySidebar()
- },
- getFsCourseVideoListBySidebar() {
- this.videoQueryParams.loading = true;
- getFsCourseVideoListBySidebar(this.videoQueryParams).then(response => {
- if (this.videoQueryParams.page === 1) {
- this.videoList = response.data.list;
- } else {
- this.videoList = [...this.videoList, ...response.data.list];
- }
- this.videoQueryParams.hasNextPage = response.data.hasNextPage;
- this.videoQueryParams.loading = false;
- console.log(this.videoList)
- }).catch(() => {
- this.videoQueryParams.loading = false;
- })
- },
- loadMoreVideo() {
- if (!this.videoQueryParams.hasNextPage || this.videoQueryParams.loading) return;
- this.videoQueryParams.page += 1;
- this.getFsCourseVideoListBySidebar()
- },
- handleImgError(e) {
- e.target.src = this.defaultImage;
- },
- formatDate(timestamp) {
- if (!timestamp) return '';
- const date = new Date(timestamp);
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
- },
- formatDuration(seconds) {
- if (!seconds) return '00:00';
- const minutes = Math.floor(seconds / 60);
- const remainingSeconds = seconds % 60;
- return `${String(minutes).padStart(2, '0')}:${String(remainingSeconds).padStart(2, '0')}`;
- },
- handleShare(item) {
- const params = {
- extId: this.userId,
- courseId: item.courseId,
- videoId: item.videoId,
- title: item.title,
- }
- this.sharingVideoId = item.videoId;
- createMiniLink(params).then(response => {
- const {code, data} = response;
- if (code === 200) {
- this.$store.dispatch('qwIm/shareCourse', data)
- }
- this.sharingVideoId = null;
- }).catch(() => {
- this.$message.error('分享课程失败');
- this.sharingVideoId = null;
- })
- }
- }
- }
- </script>
- <style scoped>
- .courseSelect {
- width: 100%;
- }
- .courseSelectList li {
- text-align: center;
- }
- ::v-deep .courseSelect input {
- border: unset;
- text-align: center;
- }
- .load-more {
- text-align: center;
- height: 34px;
- line-height: 34px;
- color: #909399;
- font-size: 12px;
- cursor: pointer;
- background-color: #f5f7fa;
- }
- .load-more:hover {
- background-color: #e4e7ed;
- }
- .videoSearch {
- padding: 10px;
- border-radius: 8px;
- }
- ::v-deep .videoSearch input {
- border-radius: 22px;
- }
- .infinite-list-wrapper {
- height: calc(100vh - 200px);
- overflow-y: auto;
- padding: 0 10px;
- background-color: #f5f7fa;
- }
- .infinite-list-wrapper-extend {
- height: calc(100vh - 330px);
- overflow-y: auto;
- padding: 0 10px;
- background-color: #f5f7fa;
- }
- .video-list {
- list-style: none;
- padding: 0;
- margin: 0;
- }
- .video-item {
- display: flex;
- padding: 15px;
- margin-bottom: 5px;
- background-color: #fff;
- border-radius: 8px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
- transition: transform 0.2s;
- }
- .video-item:hover {
- transform: translateY(-2px);
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
- }
- .video-thumbnail {
- position: relative;
- flex: 0 0 120px;
- height: 80px;
- margin-right: 15px;
- border-radius: 4px;
- overflow: hidden;
- }
- .video-thumbnail img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- .video-duration {
- position: absolute;
- bottom: 5px;
- right: 5px;
- background-color: rgba(0, 0, 0, 0.7);
- color: #fff;
- padding: 2px 4px;
- border-radius: 2px;
- font-size: 12px;
- }
- .video-info {
- flex: 1;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- }
- .video-title {
- font-size: 16px;
- font-weight: 500;
- color: #303133;
- margin-bottom: 5px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .video-meta {
- display: flex;
- align-items: flex-end;
- font-size: 12px;
- color: #909399;
- margin-bottom: 5px;
- }
- .video-date {
- margin-right: 15px;
- }
- .video-actions {
- display: flex;
- flex-direction: column;
- justify-content: center;
- gap: 8px;
- margin-left: 10px;
- }
- .loading-more {
- text-align: center;
- padding: 15px 0;
- color: #909399;
- }
- .no-more {
- text-align: center;
- padding: 15px 0;
- color: #909399;
- font-size: 13px;
- }
- .empty-tip {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 40px 0;
- color: #909399;
- }
- .empty-tip i {
- font-size: 48px;
- margin-bottom: 10px;
- color: #dcdfe6;
- }
- </style>
|