generated from youfool-project/youfool-prj-springboot-template
Merge pull request 'AI generated changes for conversation 42' (#3) from xsha/lroyia/task-20251031-085600 into master
Reviewed-on: #3
This commit is contained in:
commit
6503e8a5d9
|
|
@ -19,9 +19,17 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||||
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
|
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
|
||||||
import com.chinaweal.youfool.framework.springboot.rest.BaseResultCode;
|
import com.chinaweal.youfool.framework.springboot.rest.BaseResultCode;
|
||||||
|
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||||
|
import java.io.File;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 页面控制器
|
* 页面控制器
|
||||||
|
|
@ -223,6 +231,152 @@ public class PageController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布课程页面
|
||||||
|
*
|
||||||
|
* @return 发布课程页面
|
||||||
|
*/
|
||||||
|
@GetMapping("/publish")
|
||||||
|
public String publishCoursePage(Model model) {
|
||||||
|
if (!StpUtil.isLogin()) {
|
||||||
|
return "redirect:/course/login";
|
||||||
|
}
|
||||||
|
return "course-publish";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布课程
|
||||||
|
*
|
||||||
|
* @param courseName 课程名称
|
||||||
|
* @param courseDesc 课程描述
|
||||||
|
* @param videoFile 课程视频文件(可选)
|
||||||
|
* @param attachment 课程附件文件(可选)
|
||||||
|
* @param model 模型对象
|
||||||
|
* @return 发布结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/publish")
|
||||||
|
@DSTransactional
|
||||||
|
@ResponseBody
|
||||||
|
public RestResult<String> publishCourse(
|
||||||
|
@RequestParam String courseName,
|
||||||
|
@RequestParam String courseDesc,
|
||||||
|
@RequestParam(required = false) MultipartFile videoFile,
|
||||||
|
@RequestParam(required = false) MultipartFile attachment,
|
||||||
|
Model model) {
|
||||||
|
|
||||||
|
if (!StpUtil.isLogin()) {
|
||||||
|
return RestResult.error(BaseResultCode.BUSINESS_LOGIC_ERROR, "请先登录");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (courseName == null || courseName.trim().isEmpty()) {
|
||||||
|
return RestResult.error(BaseResultCode.BUSINESS_LOGIC_ERROR, "课程标题不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (courseDesc == null || courseDesc.trim().isEmpty()) {
|
||||||
|
return RestResult.error(BaseResultCode.BUSINESS_LOGIC_ERROR, "课程描述不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建课程
|
||||||
|
Course course = new Course();
|
||||||
|
course.setCourseName(courseName.trim());
|
||||||
|
course.setCourseDesc(courseDesc.trim());
|
||||||
|
course.setCourseDate(LocalDate.now());
|
||||||
|
course.setCreateBy(StpUtil.getLoginIdAsString());
|
||||||
|
course.setCreateTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
boolean courseSaved = courseService.save(course);
|
||||||
|
if (!courseSaved) {
|
||||||
|
return RestResult.error(BaseResultCode.BUSINESS_LOGIC_ERROR, "创建课程失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
String courseId = course.getId();
|
||||||
|
|
||||||
|
// 处理视频文件
|
||||||
|
if (videoFile != null && !videoFile.isEmpty()) {
|
||||||
|
String videoAttachmentId = saveUploadFile(videoFile, courseId, "video");
|
||||||
|
if (videoAttachmentId != null) {
|
||||||
|
CourseVideo courseVideo = new CourseVideo();
|
||||||
|
courseVideo.setCourseId(courseId);
|
||||||
|
courseVideo.setSortIdx(1);
|
||||||
|
courseVideo.setVideoName(videoFile.getOriginalFilename());
|
||||||
|
courseVideo.setAttachmentId(videoAttachmentId);
|
||||||
|
courseVideo.setCreateBy(StpUtil.getLoginIdAsString());
|
||||||
|
courseVideo.setCreateTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
courseVideoService.save(courseVideo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理附件文件
|
||||||
|
if (attachment != null && !attachment.isEmpty()) {
|
||||||
|
saveUploadFile(attachment, courseId, "attachment");
|
||||||
|
}
|
||||||
|
|
||||||
|
return RestResult.ok(courseId, "课程发布成功");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return RestResult.error(BaseResultCode.BUSINESS_LOGIC_ERROR, "课程发布失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存上传的文件
|
||||||
|
*
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @param courseId 课程ID
|
||||||
|
* @param type 文件类型(video或attachment)
|
||||||
|
* @return 附件ID
|
||||||
|
*/
|
||||||
|
private String saveUploadFile(MultipartFile file, String courseId, String type) {
|
||||||
|
try {
|
||||||
|
if (file.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
if (originalFilename == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileFormat = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
|
||||||
|
String newFileName = UUID.randomUUID().toString() + "." + fileFormat;
|
||||||
|
|
||||||
|
// 创建上传目录
|
||||||
|
String uploadDir = "../uploads/courses/" + LocalDate.now().getYear() + "/" +
|
||||||
|
String.format("%02d", LocalDate.now().getMonthValue()) + "/" +
|
||||||
|
String.format("%02d", LocalDate.now().getDayOfMonth());
|
||||||
|
Path uploadPath = Paths.get(uploadDir);
|
||||||
|
if (!uploadPath.toFile().exists()) {
|
||||||
|
uploadPath.toFile().mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
String absolutePath = uploadDir + "/" + newFileName;
|
||||||
|
|
||||||
|
// 保存文件
|
||||||
|
File dest = new File(absolutePath);
|
||||||
|
file.transferTo(dest);
|
||||||
|
|
||||||
|
// 创建附件记录
|
||||||
|
CourseAttachment attachment = new CourseAttachment();
|
||||||
|
attachment.setCourseId(courseId);
|
||||||
|
attachment.setFileName(originalFilename);
|
||||||
|
attachment.setFileFormat(fileFormat);
|
||||||
|
attachment.setAbsoluteName(absolutePath);
|
||||||
|
attachment.setFileSize(file.getSize());
|
||||||
|
attachment.setCreateBy(StpUtil.getLoginIdAsString());
|
||||||
|
attachment.setCreateTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
boolean saved = courseAttachmentService.save(attachment);
|
||||||
|
return saved ? attachment.getId() : null;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流式播放课程视频
|
* 流式播放课程视频
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,366 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>发布课程 - 课程管理系统</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
.back-btn, .logout-btn {
|
||||||
|
background-color: #6c757d;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
.back-btn:hover {
|
||||||
|
background-color: #5a6268;
|
||||||
|
}
|
||||||
|
.logout-btn:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
.form-container {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
.form-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.form-label .required {
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
.form-control {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
}
|
||||||
|
.form-control:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #007bff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
|
||||||
|
}
|
||||||
|
textarea.form-control {
|
||||||
|
min-height: 120px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
.file-input-group {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.file-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.file-info {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.submit-btn {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 2rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
.submit-btn:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
.submit-btn:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.alert {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.alert-success {
|
||||||
|
background-color: #d4edda;
|
||||||
|
border: 1px solid #c3e6cb;
|
||||||
|
color: #155724;
|
||||||
|
}
|
||||||
|
.alert-danger {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
.loading {
|
||||||
|
display: none;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.header {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
margin: 1rem auto;
|
||||||
|
}
|
||||||
|
.form-container {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<h1>课程管理系统</h1>
|
||||||
|
<div class="user-info">
|
||||||
|
<a href="/course/index" class="back-btn">返回首页</a>
|
||||||
|
<span>欢迎,<span id="username"></span></span>
|
||||||
|
<a href="/course/user/auth/logout" class="logout-btn">退出登录</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="content">
|
||||||
|
<div class="form-container">
|
||||||
|
<h2 class="form-title">发布新课程</h2>
|
||||||
|
|
||||||
|
<div id="alert" class="alert"></div>
|
||||||
|
|
||||||
|
<form id="publishForm" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">
|
||||||
|
课程标题 <span class="required">*</span>
|
||||||
|
</label>
|
||||||
|
<input type="text"
|
||||||
|
id="courseName"
|
||||||
|
name="courseName"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="请输入课程标题"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">
|
||||||
|
课程描述 <span class="required">*</span>
|
||||||
|
</label>
|
||||||
|
<textarea id="courseDesc"
|
||||||
|
name="courseDesc"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="请输入课程描述,详细介绍课程内容、目标等"
|
||||||
|
required></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">
|
||||||
|
课程视频 <small>(可选,支持 mp4、avi、mov 等格式)</small>
|
||||||
|
</label>
|
||||||
|
<div class="file-input-group">
|
||||||
|
<input type="file"
|
||||||
|
id="videoFile"
|
||||||
|
name="videoFile"
|
||||||
|
class="file-input"
|
||||||
|
accept="video/*">
|
||||||
|
<div class="file-info">
|
||||||
|
请选择视频文件,文件大小不超过100MB
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">
|
||||||
|
课程附件 <small>(可选,支持 pdf、doc、ppt 等格式)</small>
|
||||||
|
</label>
|
||||||
|
<div class="file-input-group">
|
||||||
|
<input type="file"
|
||||||
|
id="attachment"
|
||||||
|
name="attachment"
|
||||||
|
class="file-input"
|
||||||
|
accept=".pdf,.doc,.docx,.ppt,.pptx,.xls,.xlsx,.txt">
|
||||||
|
<div class="file-info">
|
||||||
|
请选择课程附件,文件大小不超过100MB
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" id="submitBtn" class="submit-btn">
|
||||||
|
发布课程
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loading" class="loading">
|
||||||
|
正在发布课程,请稍候...
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// 获取用户信息
|
||||||
|
async function loadUserInfo() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/course/user/auth/login/info');
|
||||||
|
if (response.data.code === 200 && response.data.data) {
|
||||||
|
document.getElementById('username').textContent = response.data.data.username || '用户';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户信息失败:', error);
|
||||||
|
window.location.href = '/course/login';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示提示信息
|
||||||
|
function showAlert(message, type) {
|
||||||
|
const alert = document.getElementById('alert');
|
||||||
|
alert.className = `alert alert-${type}`;
|
||||||
|
alert.textContent = message;
|
||||||
|
alert.style.display = 'block';
|
||||||
|
|
||||||
|
// 3秒后自动隐藏
|
||||||
|
setTimeout(() => {
|
||||||
|
alert.style.display = 'none';
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示/隐藏加载状态
|
||||||
|
function setLoading(loading) {
|
||||||
|
const submitBtn = document.getElementById('submitBtn');
|
||||||
|
const loadingDiv = document.getElementById('loading');
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.textContent = '发布中...';
|
||||||
|
loadingDiv.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.textContent = '发布课程';
|
||||||
|
loadingDiv.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布课程
|
||||||
|
async function publishCourse(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('courseName', document.getElementById('courseName').value);
|
||||||
|
formData.append('courseDesc', document.getElementById('courseDesc').value);
|
||||||
|
|
||||||
|
const videoFile = document.getElementById('videoFile').files[0];
|
||||||
|
if (videoFile) {
|
||||||
|
formData.append('videoFile', videoFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
const attachment = document.getElementById('attachment').files[0];
|
||||||
|
if (attachment) {
|
||||||
|
formData.append('attachment', attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/course/publish', formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.code === 200) {
|
||||||
|
showAlert('课程发布成功!', 'success');
|
||||||
|
// 2秒后跳转到首页
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/course/index';
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
showAlert(response.data.message || '课程发布失败', 'danger');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发布课程失败:', error);
|
||||||
|
const errorMessage = error.response?.data?.message || error.message || '课程发布失败,请稍后重试';
|
||||||
|
showAlert(errorMessage, 'danger');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件选择时显示文件信息
|
||||||
|
function handleFileSelect(input, infoText) {
|
||||||
|
const file = input.files[0];
|
||||||
|
if (file) {
|
||||||
|
const fileSize = (file.size / 1024 / 1024).toFixed(2);
|
||||||
|
infoText.textContent = `已选择: ${file.name} (${fileSize}MB)`;
|
||||||
|
} else {
|
||||||
|
infoText.textContent = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载时初始化
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
loadUserInfo();
|
||||||
|
|
||||||
|
// 绑定表单提交事件
|
||||||
|
document.getElementById('publishForm').addEventListener('submit', publishCourse);
|
||||||
|
|
||||||
|
// 绑定文件选择事件
|
||||||
|
document.getElementById('videoFile').addEventListener('change', function() {
|
||||||
|
const infoDiv = this.parentElement.querySelector('.file-info');
|
||||||
|
handleFileSelect(this, infoDiv);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('attachment').addEventListener('change', function() {
|
||||||
|
const infoDiv = this.parentElement.querySelector('.file-info');
|
||||||
|
handleFileSelect(this, infoDiv);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -211,6 +211,7 @@
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<h1>课程管理系统</h1>
|
<h1>课程管理系统</h1>
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
|
<a href="/course/publish" class="logout-btn" style="background-color: #28a745;">发布课程</a>
|
||||||
<span>欢迎,<span id="username"></span></span>
|
<span>欢迎,<span id="username"></span></span>
|
||||||
<a href="/course/user/auth/logout" class="logout-btn">退出登录</a>
|
<a href="/course/user/auth/logout" class="logout-btn">退出登录</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue