This commit is contained in:
Chester.X 2024-07-04 21:21:59 +08:00
parent e9c59574c5
commit 0293c09ebd
7 changed files with 381 additions and 47 deletions

View File

@ -0,0 +1,111 @@
<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>

View File

@ -0,0 +1,123 @@
<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>

View File

@ -1,9 +1,9 @@
// import CourseList from '../views/course-management/CourseList.vue'
// import CourseDetail from '../views/course-management/CourseDetail.vue'
// import CourseEdit from '../views/course-management/CourseEdit.vue'
import CourseManagement from '../views/course-management/CourseManagement.vue';
export default [
// { path: '/courses', component: CourseList },
// { path: '/courses/:id', component: CourseDetail },
// { path: '/courses/:id/edit', component: CourseEdit }
]
{
path: '/courses',
name: 'Courses',
component: CourseManagement
}
];

View File

@ -0,0 +1,27 @@
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();

View File

@ -25,6 +25,10 @@ const login = async () => {
console.error("Login error:", error);
}
};
const goToRegister = () => {
router.push('/register');
};
</script>
<template>
@ -40,6 +44,7 @@ const login = async () => {
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit">Login</el-button>
<el-button type="default" @click="goToRegister">Register</el-button>
</el-form-item>
</el-form>
</div>

View File

@ -1,48 +1,51 @@
<template>
<div>
<h2>Register</h2>
<form @submit.prevent="register">
<div>
<label for="username">Username:</label>
<input type="text" v-model="username" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" v-model="password" required>
</div>
<div>
<label for="email">Email:</label>
<input type="email" v-model="email" required>
</div>
<div>
<label for="phoneNumber">Phone Number:</label>
<input type="text" v-model="phoneNumber">
</div>
<div>
<label for="company">Company:</label>
<input type="text" v-model="company">
</div>
<div>
<label for="role">Role:</label>
<select v-model="role">
<option value="USER">User</option>
<option value="ADMIN">Admin</option>
</select>
</div>
<div>
<label for="verificationCode">Verification Code:</label>
<input type="text" v-model="verificationCode" required>
<button type="button" @click="getVerificationCode">Get Verification Code</button>
</div>
<button type="submit">Register</button>
</form>
<p v-if="message">{{ message }}</p>
</div>
<el-container class="register-container">
<el-main>
<el-card class="register-card">
<el-header>
<h2>Register</h2>
</el-header>
<el-form @submit.prevent="register" label-position="top" class="register-form">
<el-form-item label="Username" required>
<el-input type="text" v-model="username"></el-input>
</el-form-item>
<el-form-item label="Password" required>
<el-input type="password" v-model="password"></el-input>
</el-form-item>
<el-form-item label="Email" required>
<el-input type="email" v-model="email"></el-input>
</el-form-item>
<el-form-item label="Phone Number" required>
<el-input type="text" v-model="phoneNumber"></el-input>
</el-form-item>
<el-form-item label="Company" required>
<el-input type="text" v-model="company"></el-input>
</el-form-item>
<el-form-item label="Role">
<el-select v-model="role" placeholder="Select role">
<el-option label="User" value="USER"></el-option>
<el-option label="Admin" value="ADMIN"></el-option>
</el-select>
</el-form-item>
<el-form-item label="Verification Code" required>
<el-input type="text" v-model="verificationCode"></el-input>
<el-button class="verification-button" type="primary" @click="getVerificationCode">Get Verification Code</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit">Register</el-button>
<el-button type="default" @click="goToLogin">Back</el-button>
</el-form-item>
</el-form>
<el-alert v-if="message" type="info" :closable="false">{{ message }}</el-alert>
</el-card>
</el-main>
</el-container>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import authenticationService from '../../services/authenticationService';
const username = ref('');
@ -81,4 +84,50 @@ const register = async () => {
message.value = error.response.data.message;
}
};
const goToLogin = () => {
router.push('/login');
};
</script>
<style scoped>
.register-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-image: url('@public/background1.jpg');
background-size: cover;
background-position: center;
padding: 20px;
overflow: hidden;
width: 100%;
}
.register-card {
padding: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 8px;
background-color: white;
width: 100%;
max-width: 400px;
}
.register-form {
width: 100%;
}
.el-header h2 {
text-align: center;
margin: 0 0 20px;
}
.verification-button {
margin-top: 20px;
}
.el-alert {
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,19 @@
<template>
<div>
<CourseList />
</div>
</template>
<script>
import CourseList from '@/components/CourseList.vue';
export default {
components: {
CourseList
}
};
</script>
<style>
/* 添加你的样式 */
</style>