改了很多
This commit is contained in:
parent
0293c09ebd
commit
e0c0a10a43
3
package-lock.json
generated
3
package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"element-plus": "^2.7.6",
|
"element-plus": "^2.7.6",
|
||||||
"vue": "^3.4.29",
|
"vue": "^3.4.29",
|
||||||
@ -41,7 +42,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@element-plus/icons-vue": {
|
"node_modules/@element-plus/icons-vue": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
|
||||||
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
|
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"element-plus": "^2.7.6",
|
"element-plus": "^2.7.6",
|
||||||
"vue": "^3.4.29",
|
"vue": "^3.4.29",
|
||||||
|
|||||||
@ -1,111 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="search-bar">
|
|
||||||
<input v-model="searchName" placeholder="请输入课程名称">
|
|
||||||
<input v-model="searchOrder" type="number" placeholder="请输入课程排序">
|
|
||||||
<button @click="searchCourses">搜索</button>
|
|
||||||
<button @click="exportCourses">导出</button>
|
|
||||||
</div>
|
|
||||||
<div class="button-bar">
|
|
||||||
<button @click="showCreateModal">新增</button>
|
|
||||||
</div>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>课程ID</th>
|
|
||||||
<th>课程名称</th>
|
|
||||||
<th>课程简介</th>
|
|
||||||
<th>操作</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="course in courses" :key="course.id">
|
|
||||||
<td>{{ course.id }}</td>
|
|
||||||
<td>{{ course.name }}</td>
|
|
||||||
<td>{{ course.description }}</td>
|
|
||||||
<td>
|
|
||||||
<button @click="showEditModal(course)">修改</button>
|
|
||||||
<button @click="deleteCourse(course.id)">删除</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<!-- 创建/编辑课程模态框 -->
|
|
||||||
<CreateEditCourseModal v-if="showModal" :course="currentCourse" @close="closeModal" @refresh="fetchCourses" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios';
|
|
||||||
import CreateEditCourseModal from './CreateEditCourseModel.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
CreateEditCourseModal
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
courses: [],
|
|
||||||
searchName: '',
|
|
||||||
searchOrder: '',
|
|
||||||
showModal: false,
|
|
||||||
currentCourse: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fetchCourses() {
|
|
||||||
axios.get('/api/courses')
|
|
||||||
.then(response => {
|
|
||||||
this.courses = response.data;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
searchCourses() {
|
|
||||||
axios.get('/api/courses', {
|
|
||||||
params: {
|
|
||||||
name: this.searchName,
|
|
||||||
sortOrder: this.searchOrder
|
|
||||||
}
|
|
||||||
}).then(response => {
|
|
||||||
this.courses = response.data;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
exportCourses() {
|
|
||||||
axios.get('/api/courses/export', { responseType: 'blob' })
|
|
||||||
.then(response => {
|
|
||||||
const url = window.URL.createObjectURL(new Blob([response.data]));
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = url;
|
|
||||||
link.setAttribute('download', 'courses.xlsx');
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
showCreateModal() {
|
|
||||||
this.currentCourse = null;
|
|
||||||
this.showModal = true;
|
|
||||||
},
|
|
||||||
showEditModal(course) {
|
|
||||||
this.currentCourse = course;
|
|
||||||
this.showModal = true;
|
|
||||||
},
|
|
||||||
deleteCourse(courseId) {
|
|
||||||
if (confirm('确定要删除该课程吗?')) {
|
|
||||||
axios.delete(`/api/courses/${courseId}`)
|
|
||||||
.then(() => {
|
|
||||||
this.fetchCourses();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closeModal() {
|
|
||||||
this.showModal = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.fetchCourses();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 添加你的样式 */
|
|
||||||
</style>
|
|
||||||
@ -1,123 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<h3>{{ course ? '编辑课程' : '新增课程' }}</h3>
|
|
||||||
<form @submit.prevent="submitForm">
|
|
||||||
<input v-model="form.name" placeholder="课程名称" required>
|
|
||||||
<textarea v-model="form.description" placeholder="课程简介" required></textarea>
|
|
||||||
<input v-model="form.author" placeholder="课程作者" required>
|
|
||||||
<input v-model="form.sortOrder" type="number" placeholder="课程排序" required>
|
|
||||||
|
|
||||||
<h2>上传封面图片</h2>
|
|
||||||
<input type="file" @change="handleFileChange">
|
|
||||||
<img v-if="imageUrl" :src="imageUrl" alt="Image preview" width="200" />
|
|
||||||
|
|
||||||
<h2>上传视频</h2>
|
|
||||||
<input type="file" @change="handleVideoChange">
|
|
||||||
<video v-if="videoUrl" :src="videoUrl" controls width="400"></video>
|
|
||||||
|
|
||||||
<button type="submit">确定</button>
|
|
||||||
<button @click="$emit('close')">取消</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import CourseService from '@/services/courseService';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: ['course'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
form: {
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
author: '',
|
|
||||||
sortOrder: '',
|
|
||||||
coverImage: null,
|
|
||||||
video: null
|
|
||||||
},
|
|
||||||
imageUrl: null,
|
|
||||||
videoUrl: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleFileChange(event) {
|
|
||||||
const file = event.target.files[0];
|
|
||||||
if (file) {
|
|
||||||
this.form.coverImage = file;
|
|
||||||
this.imageUrl = URL.createObjectURL(file);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleVideoChange(event) {
|
|
||||||
const file = event.target.files[0];
|
|
||||||
if (file) {
|
|
||||||
this.form.video = file;
|
|
||||||
this.videoUrl = URL.createObjectURL(file);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
submitForm() {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('name', this.form.name);
|
|
||||||
formData.append('description', this.form.description);
|
|
||||||
formData.append('author', this.form.author);
|
|
||||||
formData.append('sortOrder', this.form.sortOrder);
|
|
||||||
formData.append('coverImage', this.form.coverImage);
|
|
||||||
formData.append('video', this.form.video);
|
|
||||||
|
|
||||||
if (this.course) {
|
|
||||||
CourseService.editCourse(this.course.id, formData)
|
|
||||||
.then(() => {
|
|
||||||
this.$emit('refresh');
|
|
||||||
this.$emit('close');
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
alert('提交失败: ' + error.response.data.message);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
CourseService.createCourse(formData)
|
|
||||||
.then(() => {
|
|
||||||
this.$emit('refresh');
|
|
||||||
this.$emit('close');
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
alert('提交失败: ' + error.response.data.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
course: {
|
|
||||||
immediate: true,
|
|
||||||
handler(newCourse) {
|
|
||||||
if (newCourse) {
|
|
||||||
this.form.name = newCourse.name;
|
|
||||||
this.form.description = newCourse.description;
|
|
||||||
this.form.author = newCourse.author;
|
|
||||||
this.form.sortOrder = newCourse.sortOrder;
|
|
||||||
} else {
|
|
||||||
this.form.name = '';
|
|
||||||
this.form.description = '';
|
|
||||||
this.form.author = '';
|
|
||||||
this.form.sortOrder = '';
|
|
||||||
this.form.coverImage = null;
|
|
||||||
this.form.video = null;
|
|
||||||
this.imageUrl = null;
|
|
||||||
this.videoUrl = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.modal {
|
|
||||||
/* 添加你的样式 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
/* 添加你的样式 */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,9 +1,16 @@
|
|||||||
import CourseManagement from '../views/course-management/CourseManagement.vue';
|
import Course from '../views/course-management/Course.vue';
|
||||||
|
import CourseList from '../views/course-management/CourseList.vue';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
path: '/courses',
|
path: '/course',
|
||||||
name: 'Courses',
|
name: 'Course',
|
||||||
component: CourseManagement
|
component: Course
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/courseList',
|
||||||
|
name: 'CourseList',
|
||||||
|
component: CourseList
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const API_URL = '/api';
|
|
||||||
|
|
||||||
class CourseService {
|
|
||||||
createCourse(course) {
|
|
||||||
return axios.post(`${API_URL}/course/create`, course, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
editCourse(courseId, course) {
|
|
||||||
return axios.put(`${API_URL}/course/edit/${courseId}`, course, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getCourse(courseId) {
|
|
||||||
return axios.get(`${API_URL}/course/get/${courseId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new CourseService();
|
|
||||||
352
src/views/course-management/Course.vue
Normal file
352
src/views/course-management/Course.vue
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted, ref} from 'vue';
|
||||||
|
import {
|
||||||
|
ElButton,
|
||||||
|
ElForm,
|
||||||
|
ElFormItem,
|
||||||
|
ElInput,
|
||||||
|
ElMessage,
|
||||||
|
ElMessageBox,
|
||||||
|
ElOption,
|
||||||
|
ElSelect,
|
||||||
|
ElUpload
|
||||||
|
} from 'element-plus';
|
||||||
|
|
||||||
|
import {Delete, Plus, Refresh, ZoomIn} from "@element-plus/icons-vue";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
import {useStore} from "vuex";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const store = useStore();
|
||||||
|
const token = ref('')
|
||||||
|
const form = ref({
|
||||||
|
token: '',
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
orderNo: '',
|
||||||
|
author: '',
|
||||||
|
videoPath: '',
|
||||||
|
imagePath: ''
|
||||||
|
});
|
||||||
|
const createMode = ref(false);
|
||||||
|
const modeTitle = ref('');
|
||||||
|
|
||||||
|
const videoFileList = ref([]); // 用于存储上传视频文件的列表
|
||||||
|
const imageFileList = ref([]); // 用于存储上传图片文件的列表
|
||||||
|
const basePath = '/api/courses'
|
||||||
|
const videoUploadUrl = basePath + '/upload'; // 上传视频的后端地址
|
||||||
|
const imageUploadUrl = videoUploadUrl; // 上传图片的后端地址
|
||||||
|
const dialogImageUrl = ref('');
|
||||||
|
const dialogVisible = ref(false); // 用于图片放大预览
|
||||||
|
|
||||||
|
//下面是编辑模式需要的一些变量
|
||||||
|
const id = ref('');
|
||||||
|
|
||||||
|
const beforeVideoUpload = (file) => {
|
||||||
|
const isMp4 = file.type === 'video/mp4';
|
||||||
|
if (!isMp4) {
|
||||||
|
ElMessage.error('只能上传 mp4 格式的视频文件!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLt500M = file.size / 1024 / 1024 < 500;
|
||||||
|
if (!isLt500M) {
|
||||||
|
ElMessage.error('上传视频大小不能超过 500MB!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const beforeImageUpload = (file) => {
|
||||||
|
const isImage = file.type === 'image/png' || file.type === 'image/jpg' || file.type === 'image/jpeg';
|
||||||
|
if (!isImage) {
|
||||||
|
ElMessage.error('只能上传 png/jpg/jpeg 格式的图片文件!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLt5M = file.size / 1024 / 1024 < 5;
|
||||||
|
if (!isLt5M) {
|
||||||
|
ElMessage.error('上传文件大小不能超过 5MB!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVideoSuccess = (response, file, fileList) => {
|
||||||
|
file.url = response.url;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImageSuccess = (response, file, fileList) => {
|
||||||
|
file.url = response.url;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVideoError = (error, file, fileList) => {
|
||||||
|
ElMessage.error('视频上传失败,请重试!');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImageError = (error, file, fileList) => {
|
||||||
|
ElMessage.error('图片上传失败,请重试!');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVideoChange = (file, fileList) => {
|
||||||
|
if (fileList.length > 1) {
|
||||||
|
fileList.splice(0, fileList.length - 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImageChange = (file, fileList) => {
|
||||||
|
if (fileList.length > 1) {
|
||||||
|
fileList.splice(0, fileList.length - 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePictureCardPreview = (file) => {
|
||||||
|
dialogImageUrl.value = file.url
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
const handleRemoveImage = (file) => {
|
||||||
|
imageFileList.value = imageFileList.value.filter((item) => item.uid !== file.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemoveVideo = (file) => {
|
||||||
|
videoFileList.value = videoFileList.value.filter((item) => item.uid !== file.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCommit = async () => {
|
||||||
|
if (form.value.title === '' || form.value.description === '' || form.value.orderNo === '' || form.value.author === '') {
|
||||||
|
await ElMessageBox.alert('请填写完整信息!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (imageFileList.value.length === 0 || videoFileList.value.length === 0) {
|
||||||
|
await ElMessageBox.alert('请选择封面图片和课程视频!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
form.value.imagePath = imageFileList.value[0].url;
|
||||||
|
form.value.videoPath = videoFileList.value[0].url;
|
||||||
|
if (createMode.value) {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(basePath, form.value);
|
||||||
|
ElMessage.success('添加成功!');
|
||||||
|
router.push('/courseList');
|
||||||
|
} catch (e) {
|
||||||
|
await ElMessageBox.alert(e.response.data.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const response = await axios.put(`${basePath}/${id.value}`, form.value);
|
||||||
|
ElMessage.success('修改成功!');
|
||||||
|
router.push('/courseList');
|
||||||
|
} catch (e) {
|
||||||
|
await ElMessageBox.alert(e.response.data.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isLayoutReady = ref(false);
|
||||||
|
|
||||||
|
const fetchCourseDetail = async () => {
|
||||||
|
let params = {
|
||||||
|
token: token.value,
|
||||||
|
}
|
||||||
|
const res = await axios.get(basePath + '/' + id.value, {params});
|
||||||
|
form.value.title = res.data.title;
|
||||||
|
form.value.description = res.data.description;
|
||||||
|
form.value.orderNo = res.data.orderNo;
|
||||||
|
form.value.author = res.data.author;
|
||||||
|
form.value.videoPath = res.data.videoPath;
|
||||||
|
form.value.imagePath = res.data.imagePath;
|
||||||
|
imageFileList.value.push({url: res.data.imagePath});
|
||||||
|
videoFileList.value.push({url: res.data.videoPath});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
token.value = store.getters['authentication/token'];
|
||||||
|
form.value.token = token.value;
|
||||||
|
if (route.query.mode === 'create' || route.params.mode === 'create') {
|
||||||
|
createMode.value = true;
|
||||||
|
modeTitle.value = '添加课程';
|
||||||
|
}
|
||||||
|
if (route.query.mode === 'edit' || route.params.mode === 'edit') {
|
||||||
|
id.value = route.params.id === undefined ? route.query.id : route.params.id;
|
||||||
|
createMode.value = false;
|
||||||
|
modeTitle.value = '修改课程';
|
||||||
|
fetchCourseDetail();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="form-container">
|
||||||
|
<ElForm :model="form" label-width="120px" @submit.prevent="handleCommit">
|
||||||
|
<h2>{{ modeTitle }}</h2>
|
||||||
|
<ElFormItem label="课程名称" required>
|
||||||
|
<ElInput v-model="form.title" placeholder="请输入课程名称" required></ElInput>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem label="课程封面" required>
|
||||||
|
<el-upload
|
||||||
|
:action="imageUploadUrl"
|
||||||
|
:limit="2"
|
||||||
|
:before-upload="beforeImageUpload"
|
||||||
|
:on-success="handleImageSuccess"
|
||||||
|
:on-error="handleImageError"
|
||||||
|
:on-change="handleImageChange"
|
||||||
|
:file-list="imageFileList"
|
||||||
|
list-type="picture-card"
|
||||||
|
auto-upload
|
||||||
|
v-model:file-list="imageFileList"
|
||||||
|
>
|
||||||
|
<template #file="{ file }">
|
||||||
|
<div>
|
||||||
|
<img class="el-upload-list__item-thumbnail" :src="file.url" alt=""/>
|
||||||
|
<span class="el-upload-list__item-actions">
|
||||||
|
<span
|
||||||
|
class="el-upload-list__item-preview"
|
||||||
|
@click="handlePictureCardPreview(file)"
|
||||||
|
>
|
||||||
|
<el-icon><zoom-in/></el-icon>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="el-upload-list__item-delete"
|
||||||
|
@click="handleRemoveImage(file)"
|
||||||
|
>
|
||||||
|
<el-icon><Delete/></el-icon>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-icon v-if="imageFileList.length === 0">
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
<div class="tip">
|
||||||
|
请上传大小不超过 <span style="color: red;">5MB</span> 格式为 <span style="color: red;">png/jpg/jpeg</span> 的文件
|
||||||
|
</div>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem label="课程视频" required>
|
||||||
|
<el-upload
|
||||||
|
:action="videoUploadUrl"
|
||||||
|
:limit="1"
|
||||||
|
:before-upload="beforeVideoUpload"
|
||||||
|
:on-success="handleVideoSuccess"
|
||||||
|
:on-error="handleVideoError"
|
||||||
|
:on-change="handleVideoChange"
|
||||||
|
:file-list="videoFileList"
|
||||||
|
list-type="picture-card"
|
||||||
|
auto-upload
|
||||||
|
v-model:file-list="videoFileList"
|
||||||
|
>
|
||||||
|
<template #file="{ file }">
|
||||||
|
<div>
|
||||||
|
<video class="el-upload-list__item-thumbnail" :src="file.url" controls></video>
|
||||||
|
<span class="el-upload-list__item-actions">
|
||||||
|
<span
|
||||||
|
class="el-upload-list__item-delete"
|
||||||
|
@click="handleRemoveVideo(file)"
|
||||||
|
>
|
||||||
|
<el-icon><Delete/></el-icon>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-icon v-if="videoFileList.length === 0">
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
<div class="tip">
|
||||||
|
请上传大小不超过 <span style="color: red;">500MB</span> 格式为 <span style="color: red;">mp4</span> 的文件
|
||||||
|
</div>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem label="课程简介" required>
|
||||||
|
<ElInput v-model="form.description" placeholder="请输入课程简介" required></ElInput>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem label="课程排序" required>
|
||||||
|
<ElInput v-model="form.orderNo" placeholder="请输入课程排序" required></ElInput>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem label="作者" required>
|
||||||
|
<ElInput v-model="form.author" placeholder="请输入作者" required></ElInput>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem>
|
||||||
|
<ElButton type="primary" native-type="submit">确定</ElButton>
|
||||||
|
<ElButton @click="router.push('/courses')">取消</ElButton>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElForm>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog v-model="dialogVisible" class="image-preview">
|
||||||
|
<img w-full :src="dialogImageUrl" alt="Preview Image"/>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.form-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
background: white;
|
||||||
|
overflow-y: auto; /* 使容器在内容溢出时出现滚动条 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #727272;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form {
|
||||||
|
background: white;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 55%;
|
||||||
|
max-height: 90vh; /* 确保表单不会超过视口高度 */
|
||||||
|
overflow-y: auto; /* 使表单在内容溢出时出现滚动条 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input,
|
||||||
|
.el-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button--primary {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button--primary:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-width-select {
|
||||||
|
min-width: 30%;
|
||||||
|
max-width: 100%;
|
||||||
|
width: auto; /* 使宽度根据内容调整 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
13
src/views/course-management/CourseList.vue
Normal file
13
src/views/course-management/CourseList.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "CourseList"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -1,19 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<CourseList />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import CourseList from '@/components/CourseList.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
CourseList
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 添加你的样式 */
|
|
||||||
</style>
|
|
||||||
Loading…
Reference in New Issue
Block a user