提交 1d99e640 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 a9ec1dc6
差异被折叠。
......@@ -15,33 +15,35 @@
"dependencies": {
"@element-plus/icons-vue": "^2.0.6",
"@tinymce/tinymce-vue": "^5.0.0",
"@vueuse/core": "^8.9.3",
"@vueuse/core": "^8.9.4",
"axios": "^0.27.2",
"blueimp-md5": "^2.19.0",
"element-plus": "^2.2.9",
"element-plus": "^2.2.10",
"file-saver": "^2.0.5",
"pinia": "^2.0.16",
"qs": "^6.11.0",
"video.js": "^7.19.2",
"video.js": "^7.20.1",
"vue": "^3.2.37",
"vue-router": "^4.1.2"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.0",
"@types/blueimp-md5": "^2.18.0",
"@types/file-saver": "^2.0.5",
"@types/node": "^16.11.43",
"@types/qs": "^6.9.7",
"@types/video.js": "^7.3.42",
"@vitejs/plugin-vue": "^3.0.0",
"@vitejs/plugin-vue": "^3.0.1",
"@vue/eslint-config-typescript": "^11.0.0",
"@vue/tsconfig": "^0.1.3",
"ali-oss": "^6.17.1",
"eslint": "^8.5.0",
"eslint-plugin-vue": "^9.2.0",
"npm-run-all": "^4.1.5",
"sass": "^1.53.0",
"sass": "^1.54.0",
"typescript": "~4.7.4",
"unplugin-auto-import": "^0.9.3",
"unplugin-auto-import": "^0.10.1",
"vite": "^3.0.2",
"vue-tsc": "^0.38.5"
"vue-tsc": "^0.39.0"
}
}
......@@ -63,8 +63,8 @@ export function getQuestionCategory(params: { project_tag: string }) {
return httpRequest.get(`/api/qbs/admin/v2/question-category/tree/${params.project_tag}`, { params })
}
// 获取试题分类
export function collectionResource(params: {
// 收藏/取消收藏
export function collectionResource(data: {
course_id: string
semester_id: string
chapter_id: string
......@@ -73,5 +73,5 @@ export function collectionResource(params: {
type: number
status: number
}) {
return httpRequest.get('/api/saas/api/v1/collection/resource', { params })
return httpRequest.post('/api/saas/api/v1/collection/resource', data)
}
<script setup lang="ts">
interface Props {
resource_type: number
type: string
resourceType: number
}
defineProps<Props>()
</script>
<template>
<img src="@/assets/images/icon_chapter.png" />
<div class="icon-resource">
<img src="@/assets/images/icon_mp4.png" v-if="resourceType === 2" />
<img src="@/assets/images/icon_work.png" v-else-if="resourceType === 3" />
<img src="@/assets/images/icon_exam.png" v-else-if="resourceType === 9" />
<img src="@/assets/images/icon_doc.png" v-else />
<!-- <img src="@/assets/images/icon_ppt.png" v-else-if="resourceType === 10" />
<img src="@/assets/images/icon_rar.png" v-else-if="resourceType === 4" /> -->
</div>
</template>
<style lang="scss">
.icon-resource {
display: inline-block;
img {
margin-right: 10px;
width: 14px;
}
}
</style>
import { collectionResource } from '@/api/base'
export function toggleCollectionResource(params) {
return collectionResource(params).then(res => {
console.log(res)
})
export async function useCollectionResource(params: {
course_id: string
semester_id: string
chapter_id: string
section_id: string
source_id: string
type: number
status: number
}) {
const res = await collectionResource(params)
console.log(res)
}
......@@ -21,11 +21,23 @@ export function topCourse(data: { id: string; status: number }) {
export function getCourse(params: { course_id: string; semester_id: string }) {
return httpRequest.get(`/api/saas/api/v1/course/${params.course_id}/detail/${params.semester_id}`, { params })
}
// 获取章节列表
export function getChapterTreeList(data: { course_id: string; semester_id: string }) {
return httpRequest.post('/api/saas/api/v1/chapter/tree', data)
}
// 收藏/取消收藏
export function collectionResource(data: {
course_id: string
semester_id: string
chapter_id: string
section_id: string
source_id: string
type: number
status: number
}) {
return httpRequest.post('/api/saas/api/v1/collection/resource', data)
}
// 获取大作业详情
export function getCourseWork(data: { course_id: string; semester_id: string }) {
return httpRequest.post('/api/saas/api/v1/job/detail', data)
......
<script setup lang="ts">
import type { CourseType } from '@/types'
import type { CourseListItemTypes } from '../types'
import { useMapStore } from '@/stores/map'
import { topCourse } from '../api'
import { ElMessage } from 'element-plus'
interface Props {
data: CourseType
data: CourseListItemTypes
}
const props = defineProps<Props>()
......@@ -28,7 +28,7 @@ const electiveTypeText = computed(() => {
// 是否选中
const isActive = computed(() => props.data.course_id === courseId)
// 置顶
function handleTop(data: CourseType) {
function handleTop(data: CourseListItemTypes) {
topCourse({ id: data.id, status: data.is_top ? 0 : 1 }).then(() => {
emit('change')
ElMessage.success('操作成功')
......
<!-- 学习 -->
<script setup lang="ts">
import { Star } from '@element-plus/icons-vue'
import type { ChapterType } from '@/types'
import * as api from '../api'
let chapterList = $ref<ChapterType[]>([])
import type { CourseChapterTypes, CourseSectionTypes, CourseResourceTypes } from '../types'
import ResourceIcon from '@/components/ResourceIcon.vue'
import { getChapterTreeList, collectionResource } from '../api'
let chapterList = $ref<CourseChapterTypes[]>([])
const { query } = useRoute()
const courseId = $ref(query.course_id as string)
const semesterId = $ref(query.semester_id as string)
......@@ -12,13 +13,35 @@ function fetchList() {
if (!courseId || !semesterId) {
return
}
api.getChapterTreeList({ course_id: courseId, semester_id: semesterId }).then(res => {
getChapterTreeList({ course_id: courseId, semester_id: semesterId }).then(res => {
chapterList = res.data.items
})
}
onMounted(() => {
fetchList()
})
// 是否可以收藏
function canCollection(data: CourseResourceTypes) {
return [4, 2, 10, 11, 3].includes(data.resource_type)
}
// 收藏/取消收藏
function toggleCollection(resource: CourseResourceTypes, section: CourseSectionTypes, chapter: CourseChapterTypes) {
// 资源类型: 1章节,2视频,3作业,4其他,6腾讯会议,9考试,10课件,11教案
// 收藏类型: 1其他, 2视频,3课件,4教案,5作业,6帖子
const typeMap: Record<number, number> = { 4: 1, 2: 2, 10: 3, 11: 4, 3: 5 }
collectionResource({
course_id: courseId,
semester_id: semesterId,
chapter_id: chapter.id,
section_id: section.id,
source_id: resource.resource_id,
type: typeMap[resource.resource_type],
status: resource.collection_count > 0 ? 0 : 1
}).then(res => {
resource.collection_count = resource.collection_count > 0 ? 0 : 1
console.log(res)
})
}
</script>
<template>
<el-collapse class="course-chapters">
......@@ -28,16 +51,22 @@ onMounted(() => {
<el-collapse-item :name="section.id" v-for="section in item.sections" :key="section.id">
<template #title><i class="icon-chapter"></i>{{ item.name }}</template>
<ul>
<li v-for="resource in section.resources" :key="resource.id">
<li class="course-resource-item" v-for="resource in section.resources" :key="resource.id">
<router-link
:to="`/course/player?course_id=${courseId}&section_id=${
section.id
}&semester_id=${semesterId}&&source_id=${resource.info?.source_id || ''}`"
target="_blank"
>
<ResourceIcon :resourceType="resource.resource_type" />
{{ resource.name }}
</router-link>
<el-icon><Star /></el-icon>
<i
class="icon-star"
:class="!!resource.collection_count ? 'is-active' : ''"
@click="toggleCollection(resource, section, item)"
v-if="canCollection(resource)"
></i>
</li>
</ul>
</el-collapse-item>
......@@ -47,7 +76,7 @@ onMounted(() => {
</template>
<style lang="scss" scoped>
li {
.course-resource-item {
display: flex;
align-items: center;
padding: 0 48px 0 60px;
......@@ -56,6 +85,21 @@ li {
border-bottom: 1px solid #e6e6e6;
a {
flex: 1;
&:hover {
color: var(--main-color);
}
}
}
.icon-star {
display: inline-block;
width: 16px;
height: 16px;
background: url(@/assets/images/icon_star.png) no-repeat;
background-size: contain;
cursor: pointer;
&.is-active {
background: url(@/assets/images/icon_star_hover.png) no-repeat;
background-size: contain;
}
}
.icon-chapter {
......@@ -63,7 +107,7 @@ li {
display: inline-block;
width: 12px;
height: 14px;
background: url(@/assets/images/icon_chapter.png);
background: url(@/assets/images/icon_chapter.png) no-repeat;
background-size: contain;
}
.course-chapters {
......
......@@ -37,9 +37,11 @@ const update = () => {
})
}
// 获取作业
let workInfo = $ref('')
function fetchInfo() {
getCourseWork({ course_id: courseId, semester_id: semesterId }).then(res => {
const { detail } = res.data
const { detail, essay } = res.data
workInfo = essay
Object.assign(form, detail)
})
}
......@@ -49,18 +51,52 @@ onMounted(() => {
</script>
<template>
<el-form ref="formRef" :model="form" :rules="rules" hide-required-asterisk label-position="top" disabled>
<el-form-item label="作业标题" prop="old_password">
<el-input v-model="form.title" />
</el-form-item>
<el-form-item label="作业正文" prop="password">
<el-input type="textarea" v-model="form.content" />
</el-form-item>
<el-form-item label="上传附件" prop="password_r">
<AppUpload v-model="form.attachments" disabled></AppUpload>
</el-form-item>
<el-form-item>
<el-button type="primary" round auto-insert-space @click="handleSubmit">保存</el-button>
</el-form-item>
</el-form>
<div class="course-work">
<div class="course-work-item">
<h2>作业说明</h2>
<div v-html="workInfo"></div>
</div>
<div class="course-work-item">
<h2>上传作业</h2>
<el-form
ref="formRef"
:model="form"
:rules="rules"
hide-required-asterisk
label-position="top"
:disabled="disabled"
>
<el-form-item label="作业标题" prop="old_password">
<el-input v-model="form.title" />
</el-form-item>
<el-form-item label="作业正文" prop="password">
<el-input type="textarea" v-model="form.content" />
</el-form-item>
<el-form-item label="上传附件" prop="password_r">
<AppUpload v-model="form.attachments" :disabled="disabled"></AppUpload>
</el-form-item>
<div style="text-align: center">
<el-button type="primary" round auto-insert-space @click="handleSubmit">保存</el-button>
</div>
</el-form>
</div>
</div>
</template>
<style lang="scss" scoped>
.course-work {
h2 {
font-size: 16px;
font-weight: 500;
line-height: 28px;
color: #333;
padding-bottom: 10px;
}
}
.course-work-item {
padding: 20px 0;
}
.course-work-item + .course-work-item {
border-top: 1px dashed #e6e6e6;
}
</style>
import type { CourseType, ChapterType, ResourceType } from '@/types'
export interface CourseListParamsTypes {
[key: string]: any
elective_types: string[]
id: string
semester_ids: string[]
elective_types: string[]
}
export interface CourseListItemTypes extends CourseType {
schedule: string
section?: ChapterType
semester_id: string
watch_video_length: number
}
// 课程章类型
export interface CourseChapterTypes {
id: string
name: string
resource_id: string
resource_type: number
sections: CourseSectionTypes[]
}
// 课程节类型
export interface CourseSectionTypes {
id: string
name: string
resource_id: string
resource_type: number
resources: CourseResourceTypes[]
}
// 课程资源类型
export interface CourseResourceTypes {
id: string
name: string
resource_id: string
resource_type: number
collection_count: number
info: ResourceType
}
export interface PlayItemTypes {
......
<script setup lang="ts">
import CourseListSearch from '../components/CourseListSearch.vue'
import CourseListItem from '../components/CourseListItem.vue'
import type { CourseType } from '@/types'
import type { CourseListParamsTypes } from '../types'
import type { CourseListParamsTypes, CourseListItemTypes } from '../types'
import { getCourseList } from '../api'
// 列表参数
const listParams = reactive<CourseListParamsTypes>({ id: '', semester_ids: [], elective_types: [] })
let courseList = $ref<CourseType[]>([])
let courseList = $ref<CourseListItemTypes[]>([])
// 获取课程列表
function fetchList() {
const params = Object.assign({}, listParams, {
......
......@@ -26,10 +26,7 @@ const electiveTypeText = computed(() => {
const detail = reactive<CourseType>({
id: '',
course_id: '',
semester_id: '',
schedule: '',
represent: '',
watch_video_length: 0,
is_finished: 0,
is_top: 0,
name: '',
......
import httpRequest from '@/utils/axios'
// 获取收藏列表
export function getCollectionList(params?: { type?: string; course_id?: string; semester_id?: string }) {
return httpRequest.get('/api/saas/api/v1/collection/list', { params })
}
// 收藏/取消收藏
export function collectionResource(data: {
course_id: string
semester_id: string
chapter_id: string
section_id: string
source_id: string
type: number
status: number
}) {
return httpRequest.post('/api/saas/api/v1/collection/resource', data)
}
// 获取所有课程
export function getCourseList() {
return httpRequest.get('/api/saas/api/v1/course/all')
}
import type { CourseType, ResourceType } from '@/types'
export interface CollectionType {
chapter_id: string
course_id: string
id: string
info: ResourceType
section_id: string
semester_id: string
semester: CollectionSemesterType
source_id: string
status: number
type: number
}
export interface CollectionSemesterType {
id: string
name: string
}
export type AllCourseType = Pick<CourseType, 'id' | 'name' | 'course_id' | 'cover' | 'semester'>
<script setup lang="ts">
const tabValue = $ref('')
import type { CollectionType, AllCourseType } from '../types'
import ResourceIcon from '@/components/ResourceIcon.vue'
import { Download } from '@element-plus/icons-vue'
import { saveAs } from 'file-saver'
import { getCollectionList, getCourseList, collectionResource } from '../api'
const tabs = $ref([
{ label: '全部', value: '' },
{ label: '视频', value: '1' },
{ label: '课件', value: '2' },
{ label: '教案', value: '3' },
{ label: '作业', value: '4' },
{ label: '帖子', value: '5' },
{ label: '其它资料', value: '6' }
{ label: '视频', value: '2' },
{ label: '课件', value: '3' },
{ label: '教案', value: '4' },
{ label: '作业', value: '5' },
{ label: '帖子', value: '6' },
{ label: '其它资料', value: '1' }
])
// 收藏列表
const params = reactive({
type: '',
course_id: '',
semester_id: ''
})
let list = $ref<CollectionType[]>([])
function fetchList() {
getCollectionList(params).then(res => {
list = res.data.items?.map((item: CollectionType) => {
item.status = 1
return item
})
})
}
// 所有课程列表
let courseList = $ref<AllCourseType[]>([])
const courseActive = $ref()
function fetchCourseList() {
getCourseList().then(res => {
courseList = res.data.items || []
})
}
function handleCourseChange(data: AllCourseType) {
params.course_id = data.course_id || ''
params.semester_id = data.semester?.id || ''
fetchList()
}
onMounted(() => {
fetchList()
fetchCourseList()
})
// 收藏/取消收藏
function toggleCollection(data: CollectionType) {
collectionResource({
course_id: data.course_id,
semester_id: data.semester_id,
chapter_id: data.chapter_id,
section_id: data.section_id,
source_id: data.source_id,
type: data.type,
status: data.status ? 0 : 1
}).then(() => {
data.status = data.status ? 0 : 1
})
}
// 是否可以下载
function canDownload(type: number) {
return [1, 3, 4, 5].includes(type)
}
// 下载资源
function downloadFile(data: CollectionType) {
data.info.url && saveAs(data.info.url, data.info.name)
}
</script>
<template>
<AppCard>
<el-tabs v-model="tabValue">
<el-tabs v-model="params.type" @tab-change="fetchList">
<el-tab-pane v-for="item in tabs" :label="item.label" :name="item.value" :key="item.value"></el-tab-pane>
</el-tabs>
<el-select></el-select>
<el-select v-model="courseActive" placeholder="所属课程" @change="handleCourseChange" clearable value-key="id">
<el-option v-for="item in courseList" :key="item.id" :label="item.name" :value="item" />
</el-select>
<ul v-if="list.length">
<li class="collection-item" v-for="item in list" :key="item.id">
<p>
<router-link
:to="`/course/player?course_id=${item.course_id}&section_id=${item.section_id}&semester_id=${item.semester_id}&source_id=${item.info?.source_id}`"
target="_blank"
>
<ResourceIcon :resourceType="item.type" />
{{ item.info.name || item.info.paper_title }}
</router-link>
</p>
<div class="actions">
<i class="icon-star" :class="!!item.status ? 'is-active' : ''" @click="toggleCollection(item)"></i>
<i class="icon-download" @click="downloadFile(item)" v-if="canDownload(item.type)"><Download /></i>
</div>
</li>
</ul>
<el-empty description="暂无数据" v-else />
</AppCard>
</template>
<style lang="scss" scoped>
.collection-item {
display: flex;
align-items: center;
height: 48px;
border-bottom: 1px solid #e6e6e6;
p {
flex: 1;
line-height: 48px;
}
&:hover {
color: var(--main-color);
}
.actions {
width: 60px;
display: flex;
align-items: center;
justify-content: space-between;
}
}
.icon-star {
display: inline-block;
width: 16px;
height: 16px;
background: url(@/assets/images/icon_star.png) no-repeat;
background-size: contain;
cursor: pointer;
&.is-active {
background: url(@/assets/images/icon_star_hover.png) no-repeat;
background-size: contain;
}
}
.icon-download {
display: inline-block;
width: 16px;
height: 16px;
cursor: pointer;
}
</style>
......@@ -8,41 +8,41 @@ export interface IMenuItem {
children?: IMenuItem[]
}
// 用户信息
// 用户类型
export interface UserType {
avatar: string
email: string
id: string
mobile: string
name: string
email: string
username: string
avatar: string
}
// 项目信息
// 项目类型
export interface ProjectType {
id: string
tab: string
name: string
tab: string
}
// 机构信息
// 机构类型
export interface OrganizationType {
contact_information: string
contact_name: string
id: string
is_valid: 1 | 2
name: string
contact_name: string
contact_information: string
validity_date: string
is_valid: 1 | 2
}
// 角色信息
// 角色类型
export interface RoleType {
desc: string
id: string
name: string
desc: string
}
// 权限信息
// 权限类型
export interface PermissionType {
desc: string
effect_uris: string
......@@ -50,33 +50,29 @@ export interface PermissionType {
name: string
parent_id: string
system_tag: number
type: number
tag: string
type: number
}
// 课程信息
// 课程类型
export interface CourseType {
id: string
course_id: string
semester_id: string
schedule: string
represent: string
watch_video_length: number
cover: string
credit: number
elective_type: number
id: string
is_finished: number
is_top: number
lecturers?: LecturerType[]
name: string
cover: string
elective_type: number
online_type: number
section?: ChapterType
semester: SemesterType
credit: number
previous_preparation: string
represent: string
semester: SemesterType
target: string
lecturers?: LecturerType[]
}
// 章节信息
// 章节类型
export interface ChapterType {
id: string
name: string
......@@ -85,7 +81,7 @@ export interface ChapterType {
is_finished: number
}
// 学期信息
// 学期类型
export interface SemesterType {
id: string
name: string
......@@ -93,9 +89,22 @@ export interface SemesterType {
end_time?: string
}
// 讲师类型
export interface LecturerType {
avatar: string
id: string
name: string
avatar: string
summarize: string
}
// 资源类型
export interface ResourceType {
id: string
knowledge_points: string
name: string
pdf?: string
url?: string
size: number
source_id: string
paper_title?: string
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论