提交 01b5c5a9 authored 作者: matian's avatar matian

Merge remote-tracking branch 'origin/master'

<template>
<div>
<video ref="videoPlayer" class="video-js vjs-default-skin vjs-big-play-centered vjs-16-9"></video>
<div ref="videoWrapper">
<!-- <video ref="videoPlayer" class="video-js vjs-default-skin vjs-big-play-centered vjs-16-9" id="videoJS"></video> -->
</div>
</template>
......@@ -26,7 +26,8 @@ export default {
},
data() {
return {
player: null
player: null,
times: Date.parse(new Date())
}
},
computed: {
......@@ -42,20 +43,27 @@ export default {
this.player.dispose()
this.player = null
}
this.initPlayer()
this.createVideo()
this.$nextTick(() => {
this.initPlayer()
})
}
}
},
mounted() {
!this.player && this.initPlayer()
this.player && this.initPlayer()
},
beforeUnmount() {
this.player && this.player.dispose()
},
methods: {
createVideo() {
const html = `<video class="video-js vjs-default-skin vjs-big-play-centered vjs-16-9" id="videoJS"></video>`
this.$refs.videoWrapper.innerHTML = html
},
initPlayer() {
const self = this
this.player = videojs(this.$refs.videoPlayer, this.videoOptions, function onPlayerReady() {
this.player = videojs(document.querySelector('#videoJS'), this.videoOptions, function onPlayerReady() {
self.$emit('ready', this)
const DEFAULT_EVENTS = [
'abort',
......
......@@ -12,7 +12,14 @@ export function getVideoList(params: {
}) {
return httpRequest.get('/api/resource/v1/course/course/search-video', { params })
}
// 获取视频列表
// 获取视频详情
export function getVideoDetails(params: { id: string }) {
return httpRequest.get('/api/resource/v1/resource/video/view', { params })
}
// 获取课程列表
export function getCourseList(params: {
tab: string
name?: string
......@@ -38,141 +45,47 @@ export function getCourseStatistics() {
return httpRequest.get('/api/resource/v1/course/course/statistics')
}
// 获取教案列表
export function getLessonList(params?: {
tab: string
status?: string
authorized?: string
classification?: string
page?: number
['per-page']?: number
}) {
return httpRequest.get('/api/resource/v1/course/course/search-lesson-plan', { params })
}
// 获取其他资料列表
export function getOtherList(params?: {
tab: string
status?: string
authorized?: string
classification?: string
page?: number
['per-page']?: number
}) {
return httpRequest.get('/api/resource/v1/course/course/search-other-information', { params })
}
// 试卷搜索
export function gatExamList(data: {
nonce: string
timestamp: string
signature: string
paper_title: string
limit: string
is_all: number
project_prefix: string
}) {
return httpRequest.post('/api/qbs/api/v1/question-papers/search', data)
// 获取章节
export function getCharacter(params: { course_id: string; type: string }) {
return httpRequest.get('/api/resource/v1/course/course/chapters', { params })
}
// 获取视频详情
export function getVideoDetails(params: { id: string }) {
return httpRequest.get('/api/resource/v1/resource/video/view', { params })
}
// 获取课件详情
export function getCourseDetails(params: { id: string }) {
return httpRequest.get('/api/resource/v1/resource/courseware/view', { params })
// 获取课程详情
export function getViewCourseDetails(params: { id: string }) {
return httpRequest.get('/api/resource/v1/course/course/view', { params })
}
// 获取教案详情
export function getLessonDetails(params: { id: string }) {
return httpRequest.get('/api/resource/v1/resource/lesson-plan/view', { params })
}
// 获取其他资料详情
export function getOtherDetails(params: { id: string }) {
return httpRequest.get('/api/resource/v1/resource/other-information/view', { params })
}
// 章节结构调整
export function dragChapterList(data: { course_id: string; id: string; brother_id: string; type: string }) {
return httpRequest.post('/api/resource/v1/course/course/drag', data)
}
// 更新视频
export function updateVideo(data: {
id: string
name: string
classification: string
knowledge_points?: string
cover?: string
}) {
return httpRequest.post('/api/resource/v1/resource/video/update', data)
}
// 部门共享
export function setDepartment(data: { id: string }) {
return httpRequest.post('/api/resource/v1/resource/video/set-department', data)
return httpRequest.post('/api/resource/v1/course/course/set-department', data)
}
// 平台共享
export function setPlatform(data: { id: string }) {
return httpRequest.post('/api/resource/v1/resource/video/set-platform', data)
return httpRequest.post('/api/resource/v1/course/course/set-platform', data)
}
// 上下线
export function setStatus(data: { id: string }) {
return httpRequest.post('/api/resource/v1/resource/video/set-status', data)
return httpRequest.post('/api/resource/v1/course/course/set-status', data)
}
// 更改负责人
export function setBelong(data: { id: string; belong_operator: string }) {
return httpRequest.post('/api/resource/v1/resource/video/set-belong', data)
}
// 获取视频详情
export function getVideoPpt(params: { id: string }) {
return httpRequest.get('/api/resource/v1/resource/video/get-ppt', { params })
}
// 创建课件
export function createPpt(data: { id: string; ppts: string }) {
return httpRequest.post('/api/resource/v1/resource/video/create-ppt', data)
}
// 编辑课件
export function updatePpt(data: { id: string; ppt_id: string; name: string; point: string; url: string }) {
return httpRequest.post('/api/resource/v1/resource/video/update-ppt', data)
return httpRequest.post('/api/resource/v1/course/course/set-belong', data)
}
// 获取章节
export function deletePpt(data: { course_id: string; type: string }) {
return httpRequest.post('/api/resource/v1/resource/video/delete-ppt', data)
// 课程授权
export function courseAuthorization(data: { id: string; organ_ids: string }) {
return httpRequest.post('/api/resource/v1/course/course/authorization', data)
}
// 获取章节
export function getCharacter(params: { course_id: string; type: string }) {
return httpRequest.get('/api/resource/v1/course/course/chapters', { params })
}
// 新建章节
export function createCharacter(data: {
course_id: string
resource_type: string
name: string
parent_id?: string
resource_id?: string
}) {
return httpRequest.post('/api/resource/v1/course/course/create-chapter', data)
}
// 章节修改
export function editCharacter(data: { id: string; course_id: string; name: string; resource_id: string }) {
return httpRequest.post('/api/resource/v1/course/course/update-chapter', data)
}
// 章节删除
export function delCharacter(data: { id: string; course_id: string }) {
return httpRequest.post('/api/resource/v1/course/course/delete-chapter', data)
}
// 直播列表
export function getLiveList(params: { name: string; page?: number;['page_size']?: number }) {
return httpRequest.get('/api/resource/v1/course/course/search-live', { params })
}
// 获取课程详情
export function getViewCourseDetails(params: { id: string }) {
return httpRequest.get('/api/resource/v1/course/course/view', { params })
// 复制课程
export function courseCopy(data: { id: string; }) {
return httpRequest.post('/api/resource/v1/course/course/copy', data)
}
......@@ -10,12 +10,12 @@ const props: any = defineProps<{ data: object; tabIndex: string }>()
<div class="cover-img" :style="`background-image:url(${props.data.cover})`"></div>
<div class="tool-pop-btn">
<div style="min-width: 100%">
<router-link v-if="props.data.auth_edit" :to="`/resource/video/update?id=${props.data.id}`">
<router-link v-if="props.data.auth_edit" :to="`/course/update-course?id=${props.data.id}`">
<div class="edit-btn">编辑</div>
</router-link>
</div>
<div style="min-width: 100%">
<router-link v-if="props.data.auth_view" :to="`/resource/video/view?id=${props.data.id}`">
<router-link v-if="props.data.auth_view" :to="`/course/my/view?id=${props.data.id}`">
<div class="view-btn">查看</div>
</router-link>
</div>
......
<script setup lang="ts">
import { courseCopy, setDepartment, setPlatform, setStatus, setBelong, courseAuthorization } from '../api'
import { useProjectList } from '@/composables/useGetProjectList'
import { ElMessage, ElMessageBox } from 'element-plus'
// 筛选部门
const departmentList: any = useProjectList('', '79806610719731712').departmentList
const router = useRouter()
const route = useRoute()
const props = defineProps(['data'])
// 详情id
const id = route.query.id as string
// 设置部门共享
const handleDepartment = () => {
ElMessageBox.confirm(
`
${
parseInt(props.data.department_public) === 0
? `该操作将会使本课件资源在您所在的部门“${props.data.organ_id_name}”内部共享,管理者不变,其余人员只能共享使用该资源,确认部门共享吗?`
: `该操作将会取消本课件资源在您所在的部门“${props.data.organ_id_name}”内部共享,部门其余人员将不能再看到该共享资源,确认取消部门共享吗?`
}
`,
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
setDepartment({ id: id }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更改成功', type: 'success' })
setTimeout(() => {
router.go(0)
}, 500)
}
})
})
}
// 设置平台共享
const handlePlatform = () => {
ElMessageBox.confirm(
`
${
parseInt(props.data.platform_public) === 0
? '该操作将会使本课件资源在e-SaaS平台中公开共享供所有老师使用,资源的管理者不变,其余人员只能共享使用该资源,确认公开该资源吗?'
: '该操作将会取消本课件资源在e-SaaS平台中公开共享,平台所有人员将不能再看到该共享资源,确认取消平台共享吗?'
}
`,
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
setPlatform({ id: id }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更改成功', type: 'success' })
setTimeout(() => {
router.go(0)
}, 500)
}
})
})
}
// 上下线设置
const handleStatus = () => {
ElMessageBox.confirm(
`
${
parseInt(props.data.status) === 1
? '已下线的资源将不能被关联到课程使用,确认下线该资源吗?'
: '确认再次上线该资源吗?'
}
`,
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
setStatus({ id: id }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更改成功', type: 'success' })
setTimeout(() => {
router.go(0)
}, 500)
}
})
})
}
// 更该负责人
const form = reactive({
members: ''
})
const dialogFormVisible = ref(false)
// 人员列表
let members: any = ref([])
// 点击更改负责人按钮弹窗
const handleMembers = () => {
dialogFormVisible.value = true
members.value = useProjectList(props.data.organ_id).members
}
// 更改负责人确定
const handlesetBelong = () => {
setBelong({ id: id, belong_operator: form.members }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '更改成功', type: 'success' })
setTimeout(() => {
dialogFormVisible.value = false
}, 500)
}
})
}
// 授权
const dialogVisibleAuthorize = ref(false)
const authorizeCheck = ref([])
const courseAuthorizeConfirm = () => {
const organIds = authorizeCheck.value.toString()
courseAuthorization({ id: id, organ_ids: organIds }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '授权成功', type: 'success' })
dialogVisibleAuthorize.value = false
}
})
}
// 数据回显
watch(
() => props.data,
value => {
authorizeCheck.value = value.auth_departments.reduce((a: any, b: any) => a.push(b.department_id) && a, [])
}
)
// 复制
const copyCourse = () => {
courseCopy({ id: id }).then((res: any) => {
if (res.code === 0) {
ElMessage({ message: '复制成功', type: 'success' })
}
})
}
</script>
<template>
<div class="tool-btn-box" v-if="$route.query.id" style="margin-bottom: 20px">
<div v-if="props.data.auth_authorized" class="btn-item" @click="dialogVisibleAuthorize = true">授权</div>
<div v-if="props.data.auth_copy" class="btn-item" @click="copyCourse">复制</div>
<div v-if="props.data.auth_platform" class="btn-item" @click="handlePlatform">
{{ props.data.platform_public == 0 ? '平台共享' : '取消平台共享' }}
</div>
<div v-if="props.data.auth_department" class="btn-item" @click="handleDepartment">
{{ props.data.department_public == 0 ? '部门共享' : '取消部门共享' }}
</div>
<div v-if="props.data.auth_platform" class="btn-item" @click="handlePlatform">
{{ props.data.platform_public == 0 ? '平台共享' : '取消平台共享' }}
</div>
<div v-if="props.data.auth_status" class="btn-item" @click="handleStatus">
{{ props.data.status == 0 ? '资源上线' : '资源下线' }}
</div>
<div v-if="props.data.auth_belong" class="btn-item" @click="handleMembers">更改负责人</div>
</div>
<el-dialog v-if="props.data.auth_belong" v-model="dialogFormVisible" title="更改负责人" center>
<el-form :model="form">
<el-form-item>
<div style="width: 500px">
<el-row>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
资源创建人: {{ props.data.created_operator_name }}
</el-col>
<el-col :span="12"
><div class="grid-content ep-bg-purple-light" />
创建时间: {{ props.data.created_time }}
</el-col>
</el-row>
<el-row>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
资源负责人: {{ props.data.belong_operator_name }}
</el-col>
<el-col :span="12"
><div class="grid-content ep-bg-purple-light" />
更新时间: {{ props.data.updated_time }}
</el-col>
</el-row>
</div>
<el-select style="width: 500px; margin-top: 20px" v-model="form.members" placeholder="请选择新的资源负责人">
<el-option v-for="item in members.value" :label="item.name" :value="item.id" :key="item.id" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="handlesetBelong">确认</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="dialogVisibleAuthorize" title="项目/学校" width="30%">
<el-checkbox-group v-model="authorizeCheck">
<el-checkbox :label="item.id" v-for="item in departmentList" :key="item.id">{{ item.name }}</el-checkbox>
</el-checkbox-group>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisibleAuthorize = false">取消</el-button>
<el-button type="primary" @click="courseAuthorizeConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<style lang="scss" scoped>
.tool-btn-box {
display: flex;
justify-content: right;
.btn-item {
width: 127px;
line-height: 36px;
background: #aa1941;
border-radius: 20px;
margin-right: 10px;
font-size: 14px;
color: #ffffff;
text-align: center;
cursor: pointer;
}
}
</style>
<script setup lang="ts">
defineProps<{ data: any[]; isBlack?: boolean }>()
const route = useRoute()
const id = route.query.id ? route.query.id : route.params.courseId as string
</script>
<template>
<div :class="isBlack ? 'chapter-box active-black' : 'chapter-box'">
<div class="title">课程章节</div>
<div class="chapter-list">
<div class="item" v-for="(item, index) in data" :key="item.id">
<div class="order">{{ index + 1 }}</div>
<div class="chapter-text">
<div class="chapter">{{ item.name }}</div>
<div class="sections" v-for="(child, childIndex) in item.children" :key="child.id">
<router-link :to="`/course/my/view/${child.id}/${id}`">
{{ index + 1 }}.{{ childIndex + 1 }} {{ child.name }}
</router-link>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.chapter-box {
width: 300px;
background: #aa1941;
border-radius: 10px;
margin-left: 20px;
height: fit-content;
&.active-black {
background: #1f1e24;
border-radius: 0;
.chapter-list {
.item {
border-bottom: 1px dotted #fff;
}
}
}
.title {
width: 271px;
margin: 0 auto;
border-bottom: 1px solid #fff;
font-size: 16px;
font-weight: 500;
line-height: 52px;
color: #ffffff;
text-align: center;
}
.chapter-list {
margin: 0 auto;
padding: 0 23px 70px;
.item {
display: flex;
border-bottom: 1px dotted #e06386;
margin-top: 30px;
.order {
width: 26px;
height: 26px;
background: rgba(255, 255, 255, 1);
border-radius: 50%;
font-size: 16px;
line-height: 26px;
color: #aa1941;
text-align: center;
margin-right: 7px;
}
.chapter-text {
.chapter {
font-size: 16px;
font-weight: 500;
line-height: 100%;
color: #ffffff;
margin-bottom: 20px;
margin-top: 6px;
cursor: pointer;
}
.sections {
font-size: 14px;
line-height: 100%;
color: #ffffff;
margin-bottom: 20px;
cursor: pointer;
}
}
}
}
}
</style>
......@@ -8,9 +8,12 @@ const props: any = defineProps({
const form: any = ref({})
watch(() => props.data, value => {
form.value = value
})
watch(
() => props.data,
value => {
form.value = value
}
)
interface IBasicInfo {
icon: string
......@@ -19,13 +22,6 @@ interface IBasicInfo {
key: string
}
// interface ILecturerInfo {
// icon: string
// label: string
// value: string
// key: string
// }
const basicInfo = computed((): IBasicInfo[] => {
const basicList = [
{
......
<script setup lang="ts">
import { getVideoDetails } from '../api'
import AppVideoPlayer from '@/components/base/AppVideoPlayer.vue'
const props: any = defineProps({
data: {
type: Array
}
})
// 资源详情
const resourceData: any = reactive({})
// 获取当前资源
const getResourceData = (id: string) => {
getVideoDetails({ id }).then((res: any) => {
Object.assign(resourceData, res.data)
})
}
// 切换视频
let videoIndex = $ref(0)
const videoOptions = computed(() => {
return {
sources: [
{
src: resourceData.play_auth?.play_info_list[0].PlayURL
}
]
}
})
const video = $computed<{ id: string }>(() => {
return props.data[videoIndex]
})
watch(
() => videoIndex,
() => {
getResourceData(video.id)
},
{ immediate: true }
)
const changeVideo = (index: number) => {
videoIndex = index
}
</script>
<template>
<div>
<div style="position: relative">
<div style="max-width: 900px; min-width: 900px">
<AppVideoPlayer :options="videoOptions"></AppVideoPlayer>
</div>
</div>
<div class="cover-box">
<div
class="cover-img"
:style="`background-image: url(${item.cover})`"
v-for="(item, index) in props.data"
:key="item.id"
@click="changeVideo(index)"
>
<div :class="item.id === props.data.currentId ? 'border active' : 'border'"></div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.cover-box {
display: flex;
}
.cover-img {
width: 200px;
height: 100px;
background-size: cover;
margin-right: 20px;
cursor: pointer;
.border {
width: 100%;
height: 100%;
box-sizing: border-box;
&.active {
border: 10px solid #aa1941;
}
}
}
</style>
......@@ -7,7 +7,9 @@ export const routes: Array<RouteRecordRaw> = [
component: AppLayout,
children: [
{ path: '', component: () => import('./views/List.vue') },
{ path: '/course/my/view', component: () => import('./views/View.vue') }
{ path: '/course/my/view', component: () => import('./views/View.vue') },
// id: 章节id courseId课程id
{ path: '/course/my/view/:id/:courseId', component: () => import('./views/ViewDetails.vue') }
]
}
]
......@@ -91,7 +91,7 @@ const changeCard = () => {
<el-icon class="video-head-icon" @click="changeCard"><Expand /></el-icon>
</div>
<div class="video-tool-btn">
<router-link v-if="tabValue === '1'" to="/course/my/StepTwo" target="_blank">
<router-link v-if="tabValue === '1'" to="/course/update-course">
<el-button type="primary" round>新建课程</el-button>
</router-link>
</div>
......@@ -123,7 +123,7 @@ const changeCard = () => {
<router-link v-if="row.auth_edit" :to="`/course/update-course?id=${row.id}`">
<el-button plain>编辑</el-button>
</router-link>
<router-link v-permission="'v1-resource-video-view'" :to="`/resource/video/view?id=${row.id}`">
<router-link v-permission="'v1-resource-video-view'" :to="`/course/my/view?id=${row.id}`">
<el-button type="primary" plain>查看</el-button>
</router-link>
</el-space>
......
<script setup lang="ts">
import ViewTop from '../components/ViewTop.vue'
import ViewCourseInfo from '../components/ViewCourseInfo.vue'
import ViewCourseChapter from '../components/ViewCourseChapter.vue'
import Operation from '../components/Operation.vue'
import { getViewCourseDetails } from '../api'
const route = useRoute()
......@@ -10,18 +12,20 @@ const id = route.query.id as string
const courseDetails:any = ref({})
onMounted(() => {
// 课程详情
getViewCourseDetails({ id: id }).then((res:any) => {
courseDetails.value = res.data
console.log(res, 'getViewCourseDetails')
})
})
</script>
<template>
<AppCard title="查阅课程">
<Operation :data="courseDetails"></Operation>
<div class="course-view">
<div class="course-left_info">
<ViewTop :data="courseDetails"></ViewTop>
<ViewCourseInfo :data="courseDetails"></ViewCourseInfo>
<ViewCourseChapter v-if="Object.keys(courseDetails).length" :data="courseDetails.chapters[0].children"></ViewCourseChapter>
</div>
</div>
</AppCard>
......@@ -31,6 +35,7 @@ onMounted(() => {
display: flex;
.course-left_info{
width: 840px;
display: flex;
}
}
</style>
<script setup lang="ts">
import { getCharacter } from '../api'
import ViewCourseChapter from '../components/ViewCourseChapter.vue'
import ViewDetailsVideo from '../components/ViewDetailsVideo.vue'
interface IChapterDetails {
name: string
children: any[]
}
// 路由
const route = useRoute()
const courseId = route.params.courseId as string
const chapterId = route.params.id
// 章节数
const chapterTree: any = ref([])
// 当前章节的资源内容
const chapterDetails = ref<IChapterDetails>()
onMounted(() => {
getCharacter({ course_id: courseId, type: 'tree' }).then((res: any) => {
if (res.code === 0) {
chapterTree.value = res.data[0]?.children || []
res.data[0]?.children.forEach((item: any) => {
const findItem = item.children.find((i: { id: string }) => i.id === chapterId)
if (findItem) {
chapterDetails.value = findItem
}
})
}
getChapterVideo()
})
})
// 获取树结构里面的视频资源
interface IResource {
cover: string
id: string
}
const videoData = ref<IResource[]>([])
const getChapterVideo = () => {
chapterDetails.value?.children.forEach((item: { resource_type: string; resource: IResource }) => {
if (item.resource_type === '2') {
videoData.value.push(item.resource)
}
})
}
// 获取当前视频详情
// const currentVideoDetails = (id: string) => {
// getVideoDetails({ id: id }).then((res: any) => {
// videoData.currentVideo = res.data
// videoData.currentId = id
// })
// }
// const btnList = [
// {
// btn_name: '视频',
// resource_type: '2'
// },
// {
// btn_name: '课件',
// resource_type: '10'
// },
// {
// btn_name: '教案',
// resource_type: '11'
// },
// {
// btn_name: '资料',
// resource_type: '4'
// },
// {
// btn_name: '作业',
// resource_type: '3'
// },
// {
// btn_name: '考试',
// resource_type: '9'
// },
// {
// btn_name: '直播',
// resource_type: '6'
// }
// ]
</script>
<template>
<AppCard title="查阅课程">
<div class="chapter-box">
<div class="title">{{ chapterDetails?.name }}</div>
<div class="chapter-content">
<div class="content-left">
<ViewDetailsVideo v-if="Object.keys(videoData).length" :data="videoData"></ViewDetailsVideo>
</div>
<ViewCourseChapter :isBlack="true" :data="chapterTree"></ViewCourseChapter>
</div>
</div>
</AppCard>
</template>
<style lang="scss" scoped>
.chapter-box {
.title {
font-size: 20px;
font-weight: 500;
color: #333333;
}
.chapter-content {
display: flex;
.content-left {
width: 900px;
}
}
}
</style>
......@@ -2,7 +2,6 @@
const props: any = defineProps<{ data: object; tabIndex: string }>()
</script>
<template>
<!-- <div>{{ props.data }}</div> -->
<div class="card-item">
<div class="card-item-top">
<div class="title">{{ props.data.name }}</div>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论