Lijy 5 napja
szülő
commit
b3b0a8206f

+ 154 - 0
fhKeeper/formulahousekeeper/course-pc/src/views/coursemanagement/addVideo.vue

@@ -0,0 +1,154 @@
+<template>
+  <div class="video-list-container">
+    <draggable v-model="videoListComputed" handle=".drag-handle" @end="onDragEnd">
+      <div
+        v-for="(item, index) in videoListComputed"
+        :key="index"
+        :class="['video-item', item.videoError ? 'error-video' : '']"
+        v-loading="item.videoLoading"
+        element-loading-text="上传中,请稍后..."
+        element-loading-spinner="el-icon-loading"
+        element-loading-background="rgba(0, 0, 0, 0.8)"
+        element-loading-customClass="loadingTextClolr"
+      >
+        <el-row :gutter="20">
+          <el-col :span="1">
+            <i class="el-icon-rank drag-handle" style="cursor: move; line-height: 32px;"></i>
+          </el-col>
+          <el-col :span="6">
+            <el-input v-model="item.videoName" size="small" placeholder="请输入视频名称"></el-input>
+          </el-col>
+          <el-col :span="4">
+            <el-select v-model="item.videoLecturerId" placeholder="请选择教师" size="small" style="width: 100%">
+              <el-option
+                v-for="type in videoTypes"
+                :key="type.id"
+                :label="type.teacherName"
+                :value="type.id"
+              ></el-option>
+            </el-select>
+          </el-col>
+          <el-col :span="8" style="display: flex; align-items: center;">
+            <div>试看时间(分钟)</div>
+            <el-input-number
+              size="small"
+              v-model="item.videoPreviewTime"
+              :min="0"
+              :max="600"
+              placeholder="试看时间(秒)"
+            ></el-input-number>
+          </el-col>
+          <el-col :span="5">
+            <el-button size="small" type="primary" @click="previewingVideoVisable = true">预览视频</el-button>
+            <el-button size="small" type="danger" @click="removeRow(index)">删除</el-button>
+          </el-col>
+        </el-row>
+      </div>
+    </draggable>
+
+    <!-- 预览视频 -->
+    <el-dialog title="预览视频" append-to-body :visible.sync="previewingVideoVisable" width="900px" top="6.5vh" :before-close="handleClose">
+      <div class="previewingVideo">
+        <video src="http://1.94.62.58:9007/upload/preview/preview_0cdd80f0-14c0-4e75-8736-b0b7764fcb7f.mp4" style="width: 100%;height: 100%;background: rgba(0,0,0,0.5);" poster="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" controls></video>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import draggable from 'vuedraggable'
+
+export default {
+  name: 'VideoListEditor',
+  components: {
+    draggable
+  },
+  props: {
+    value: {
+      type: Array,
+      required: true
+    }
+  },
+  computed: {
+    videoListComputed: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  },
+  data() {
+    return {
+      videoTypes: [],
+      previewingVideoVisable: false
+    }
+  },
+  methods: {
+    getAllTeachers() {
+      this.http.post(`/course-teacher/list`, {}, res => {
+        this.videoTypes = res.data || []
+      })
+    },
+    removeRow(index) {
+      const list = [...this.videoListComputed]
+      const row = list[index]
+      this.$confirm(`此操作将删除【${row.videoName}】视频, 是否继续?`, '删除视频', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$message({
+          type: 'success',
+          message: '删除成功!'
+        });
+
+        list.splice(index, 1)
+        this.videoListComputed = list
+      })
+    },
+    onDragEnd() {
+      console.log('排序完成', this.videoListComputed)
+    }
+  },
+  mounted() {
+    this.getAllTeachers()
+  }
+}
+</script>
+
+<style scoped>
+.video-list-container {
+  padding: 20px;
+}
+.video-item {
+  margin-bottom: 15px;
+  padding: 10px;
+  background: #f5f7fa;
+  border-radius: 4px;
+  position: relative;
+}
+.error-video {
+  background: #e6a23c;
+}
+</style>
+
+<style lang="scss">
+.video-list-container {
+  .el-loading-spinner i {
+    color: #fff !important;
+  }
+  .el-loading-text {
+    color: #fff !important;
+  }
+}
+
+.previewingVideo {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 68vh;
+}
+</style>

+ 976 - 0
fhKeeper/formulahousekeeper/course-pc/src/views/coursemanagement/list copy 2.vue

@@ -0,0 +1,976 @@
+<template>
+    <section>
+        <!--工具条-->
+        <el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
+            <el-form :inline="true" @submit.native.prevent>
+                <el-form-item label="课程分类">
+                    <el-select v-model="categoryValue" placeholder="请选择" clearable @change="searchList" size="small">
+                        <el-option v-for="item in categoryOptions" :key="item.value" :label="item.label"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item> 
+                <el-form-item label="课程名称">
+                    <el-input v-model="keyword" placeholder="请输入" clearable @change="searchList"
+                        size="small"></el-input>
+                </el-form-item>
+                <el-form-item label="讲师">
+                    <el-input v-model="instructor" placeholder="请输入" clearable @change="searchList"
+                        size="small"></el-input>
+                </el-form-item>
+                <el-form-item>
+                    <el-button type="primary" @click="addCourse()" size="small">添加课程</el-button>
+                </el-form-item>
+                <el-form-item>
+                    <el-button type="primary" @click="batchManage" size="small">分类管理</el-button>
+                </el-form-item>
+            </el-form>
+        </el-col>
+
+        <!--列表-->
+        <el-table :data="list" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;">
+            <el-table-column type="index" width="50" align="center" label="#"></el-table-column>
+            <el-table-column label="封面" width="120" align="center">
+                <template slot-scope="scope">
+                    <img v-if="scope.row.coverImage" :src="scope.row.coverImage" class="course-cover-image" />
+                    <span v-else>无封面</span>
+                </template>
+            </el-table-column>
+            <el-table-column prop="courseTypeName" label="课程分类" min-width="120" align="center"></el-table-column>
+            <el-table-column prop="courseName" label="课程名称" min-width="180" align="center"></el-table-column>
+            <el-table-column prop="courseInstructor" label="讲师" min-width="120" align="center"></el-table-column>
+            <el-table-column prop="coursePrice" label="价格" min-width="100" align="center">
+                <template slot-scope="scope">
+                    <span>¥{{ scope.row.coursePrice }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column prop="courseDuration" label="课程时间" min-width="120" align="center">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.courseDuration ? scope.row.courseDuration + '分钟' : '' }}</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="操作" width="340" class-name="btns" header-align="center" fixed="right">
+                <template slot-scope="scope">
+                    <el-button size="mini" type="primary" @click="addVideo(scope.row)">添加视频</el-button>
+                    <el-button size="mini" type="primary" @click="addCourse(scope.row)">编辑</el-button>
+                    <el-button size="mini" type="danger" @click="deleteItem(scope.row)">删除</el-button>
+                    <el-button size="mini" :type="scope.row.courseStatus === 1 ? 'warning' : 'success'"
+                        @click="uploadItem(scope.row)">{{ scope.row.courseStatus === 1 ? '下架' : '上架' }}</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+
+        <!--工具条-->
+        <el-col :span="24" class="toolbar">
+            <el-pagination
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+                :page-sizes="[10, 20, 50, 100]"
+                :page-size="size"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="total"
+                style="float:right;"
+            ></el-pagination>
+        </el-col>
+
+        <!-- 分类管理 -->
+        <el-dialog :visible.sync="categoryManageVisible" title="分类管理" width="600px">
+            <el-table :data="categoryList" style="width: 100%" max-height="400">
+                <el-table-column prop="label" label="分类名称" width="180"></el-table-column>
+                <el-table-column label="封面" width="180">
+                    <template slot-scope="scope">
+                        <img v-if="scope.row.coverImage" :src="scope.row.coverImage" class="category-cover-image" />
+                        <span v-else>无封面</span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="操作" width="180">
+                    <template slot-scope="scope">
+                        <el-button size="mini" type="primary" @click="setCategoryCover(scope.$index, scope.row)">设置封面</el-button>
+                        <el-button size="mini" type="danger" @click="deleteCategory(scope.$index, scope.row)">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div style="margin-top: 20px;">
+                <el-form :inline="true" :model="newCategory" class="demo-form-inline">
+                    <el-form-item label="分类名称">
+                        <el-input v-model="newCategory.label" placeholder="请输入分类名称"></el-input>
+                    </el-form-item>
+                    <el-form-item>
+                        <el-button type="primary" @click="addCategory">添加</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+            <span slot="footer" class="dialog-dialog">
+                <el-button @click="categoryManageVisible = false">关 闭</el-button>
+            </span>
+        </el-dialog>
+
+        <!-- 设置分类封面 -->
+        <el-dialog :visible.sync="coverDialogVisible" title="设置分类封面" width="500px">
+            <div class="cover-upload-container">
+                <el-upload
+                    class="cover-uploader"
+                    action="#"
+                    :show-file-list="false"
+                    :on-change="handleCoverChange"
+                    :auto-upload="false"
+                    :before-upload="beforeCoverUpload">
+                    <img v-if="coverImageUrl" :src="coverImageUrl" class="cover-image" />
+                    <i v-else class="el-icon-plus cover-uploader-icon"></i>
+                </el-upload>
+                <div class="cover-tip">请上传分类封面图片,建议尺寸 16:9</div>
+            </div>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="coverDialogVisible = false">取 消</el-button>
+                <el-button type="primary" @click="saveCategoryCover">确 定</el-button>
+            </span>
+        </el-dialog>
+
+        <!-- 添加课程 -->
+        <el-dialog :visible.sync="addDialogVisible" title="添加课程" width="800px" top="6.5vh">
+            <el-form :model="courseForm" :rules="courseRules" ref="courseForm" label-width="120px">
+                <el-form-item label="课程分类" prop="courseTypeId">
+                    <el-select v-model="courseForm.courseTypeId" placeholder="请选择" style="width:100%">
+                        <el-option v-for="item in categoryOptions" :key="item.value" 
+                            :label="item.label" :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="课程名称" prop="courseName">
+                    <el-input v-model="courseForm.courseName" placeholder="请输入课程名称"></el-input>
+                </el-form-item>
+                <el-form-item label="课程介绍" style="height: 200px;">
+                    <quill-editor style="height: 150px" ref="text" v-model="courseForm.courseDesc" class="myQuillEditor" :options="editorOption"/>
+                </el-form-item>
+                <el-form-item label="课程价格">
+                    <el-input-number v-model="courseForm.coursePrice" :min="0" :precision="2"></el-input-number>
+                </el-form-item>
+                <el-form-item label="课程时间(分钟)">
+                    <el-input-number v-model="courseForm.courseDuration" :min="0"></el-input-number>
+                </el-form-item>
+                <el-form-item label="课程封面">
+                    <el-upload
+                        class="cover-uploader"
+                        action="#"
+                        :show-file-list="false"
+                        :on-change="handleCourseCoverChange"
+                        :auto-upload="false"
+                        :before-upload="beforeCoverUpload">
+                        <img v-if="courseForm.coverImageUrl" :src="courseForm.coverImageUrl" class="cover-image" />
+                        <i v-else class="el-icon-plus cover-uploader-icon"></i>
+                    </el-upload>
+                    <div class="cover-tip">建议尺寸 16:9,大小不超过2MB</div>
+                </el-form-item>
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="addDialogVisible = false">取 消</el-button>
+                <el-button type="primary" @click="submitCourseForm">确 定</el-button>
+            </span>
+        </el-dialog>
+    </section>
+</template>
+
+<script>
+// 富文本样式
+import 'quill/dist/quill.core.css'
+import 'quill/dist/quill.snow.css'
+import 'quill/dist/quill.bubble.css'
+// 导入富文本
+import { quillEditor } from 'vue-quill-editor'
+export default {
+    components: {
+        quillEditor, // 富文本
+    },
+    data() {
+        return {
+            // 课程表单
+            courseForm: {
+                courseTypeId: '',
+                courseName: '',
+                courseDesc: '',
+                coursePrice: 0,
+                courseDuration: 0,
+                coverImageUrl: '',
+                coverImageFile: null
+            },
+            courseRules: {
+                courseTypeId: [
+                    { required: true, message: '请选择课程分类', trigger: 'change' }
+                ],
+                courseName: [
+                    { required: true, message: '请输入课程名称', trigger: 'blur' }
+                ]
+            },
+            // 课程分类选择
+            categoryValue: '',
+            categoryOptions: [],
+            // 搜索条件
+            keyword: null, // 课程名称
+            instructor: null, // 讲师
+
+            // 表格相关
+            tableHeight: 0,
+            listLoading: false,
+            total: 0,
+            page: 1,
+            size: 20,
+            list: [],
+
+            // 对话框控制
+            categoryManageVisible: false,
+            editDialogVisible: false,
+            addDialogVisible: false,
+
+            // 分类管理
+            categoryList: [],
+            newCategory: {
+                label: '',
+                value: '',
+                coverImage: ''
+            },
+
+            // 分类封面设置
+            coverDialogVisible: false,
+            currentCategoryIndex: -1,
+            coverImageUrl: '',
+            coverImageFile: null,
+            
+            // 用户信息
+            user: sessionStorage.user ? JSON.parse(sessionStorage.user) : {},
+            editorOption: { // 富文本框里面的默认值
+                placeholder: '请输入内容...',
+                modules: {
+                    toolbar:[
+                        ['bold', 'italic', 'underline', 'strike'],    //加粗,斜体,下划线,删除线
+                        // ['blockquote', 'code-block'],     //引用,代码块
+                
+                        [{ 'header': 1 }, { 'header': 2 }],        // 标题,键值对的形式;1、2表示字体大小
+                        // [{ 'list': 'ordered'}, { 'list': 'bullet' }],     //列表
+                        // [{ 'script': 'sub'}, { 'script': 'super' }],   // 上下标
+                        // [{ 'indent': '-1'}, { 'indent': '+1' }],     // 缩进
+                        // [{ 'direction': 'rtl' }],             // 文本方向
+                
+                        [{ 'size': ['small', false, 'large', 'huge'] }], // 字体大小
+                        [{ 'header': [1, 2, 3, 4, 5, 6, false] }],     //几级标题
+                
+                        [{ 'color': [] }, { 'background': [] }],     // 字体颜色,字体背景颜色
+                        // [{ 'font': [] }],     //字体
+                        [{ 'align': [] }],    //对齐方式
+                
+                        ['clean'],    //清除字体样式
+                        // ['image','video']    //上传图片、上传视频
+                        []    //上传图片、上传视频
+                    ], //工具栏设置
+                },
+                theme: 'snow',
+            },
+        }
+    },
+    methods: {
+        // 分类管理
+        batchManage() {
+            this.categoryManageVisible = true;
+            // 加载分类数据
+            this.http.post('/course-type/list', {}, res => {
+                if (res.code == "ok") {
+                    // 将后端返回的数据转换为前端需要的格式
+                    this.categoryList = res.data.map(item => ({
+                        label: item.typeName,
+                        value: item.id,
+                        coverImage: item.coverImage || ''
+                    }));
+                    
+                    // 同步更新下拉选项
+                    this.categoryOptions = [...this.categoryList];
+                } else {
+                    this.$message({
+                        message: res.msg || '获取分类列表失败',
+                        type: 'error'
+                    });
+                }
+            }, error => {
+                this.$message({
+                    message: error || '获取分类列表失败',
+                    type: 'error'
+                });
+            });
+        },
+
+        // 搜索课程列表
+        searchList() {
+            this.page = 1;
+            this.getList();
+        },
+
+        // 获取课程列表
+        getList() {
+            this.listLoading = true;
+
+            // 调用后端API获取课程列表
+            this.http.post('/course-info/list', {
+                page: this.page,
+                size: this.size,
+                courseType: this.categoryValue,
+                courseName: this.keyword,
+                courseInstructor: this.instructor
+            }, res => {
+                this.listLoading = false;
+                if (res.code == "ok") {
+                    this.list = res.data.records;
+                    this.total = res.data.total;
+                } else {
+                    this.$message({
+                        message: res.msg || '获取课程列表失败',
+                        type: 'error'
+                    });
+                }
+            }, error => {
+                this.listLoading = false;
+                this.$message({
+                    message: error || '获取课程列表失败',
+                    type: 'error'
+                });
+            });
+        },
+
+        // 分页相关
+        handleCurrentChange(val) {
+            this.page = val;
+            this.getList();
+        },
+        handleSizeChange(val) {
+            this.page = 1;
+            this.size = val;
+            this.getList();
+        },
+
+        // 删除课程
+        deleteItem(row) {
+            this.$confirm('确认删除该课程?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                // 调用后端API删除课程
+                this.http.post('/course-info/deleteCourse', {
+                    id: row.id
+                }, res => {
+                    if (res.code == "ok") {
+                        this.$message({
+                            type: 'success',
+                            message: '删除成功!'
+                        });
+                        this.getList();
+                    } else {
+                        this.$message({
+                            message: res.msg || '删除课程失败',
+                            type: 'error'
+                        });
+                    }
+                }, error => {
+                    this.$message({
+                        message: error || '删除课程失败',
+                        type: 'error'
+                    });
+                });
+            })
+        },
+
+        // 上架/下架课程
+        uploadItem(row) {
+            const action = row.courseStatus === 1 ? '下架' : '上架';
+            this.$confirm(`确认${action}该课程?`, '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                // 调用后端API上架/下架课程
+                this.http.post('/course-info/saveOrUpdate', {
+                    id: row.id,
+                    courseStatus: row.courseStatus === 1 ? 0 : 1 // 0: 下架, 1: 上架
+                }, res => {
+                    if (res.code == "ok") {
+                        row.courseStatus = row.courseStatus === 1 ? 0 : 1;
+                        this.$message({
+                            type: 'success',
+                            message: `${action}成功!`
+                        });
+                    } else {
+                        this.$message({
+                            message: res.msg || `${action}失败`,
+                            type: 'error'
+                        });
+                    }
+                }, error => {
+                    this.$message({
+                        message: error || `${action}失败`,
+                        type: 'error'
+                    });
+                });
+            })
+        },
+
+        // 保存分类封面
+        saveCategoryCover() {
+            if (!this.coverImageUrl) {
+                this.$message.warning('请先上传封面图片!');
+                return;
+            }
+            
+            // 关闭对话框
+            this.coverDialogVisible = false;
+            this.$message({
+                type: 'success',
+                message: '设置封面成功!'
+            });
+        },
+
+        // 处理封面图片变更
+        handleCoverChange(file) {
+            this.coverImageFile = file.raw;
+            if (this.coverImageFile) {
+                this.coverImageUrl = URL.createObjectURL(this.coverImageFile);
+                
+                // 获取当前分类的ID
+                if (this.currentCategoryIndex >= 0) {
+                    const categoryId = this.categoryList[this.currentCategoryIndex].value;
+                    
+                    // 立即上传封面图片
+                    this.listLoading = true;
+                    
+                    // 创建FormData对象用于上传文件
+                    const formData = new FormData();
+                    formData.append('id', categoryId);
+                    formData.append('coverImage', this.coverImageFile);
+                    
+                    // 调用上传图片的API
+                    this.http.uploadFile('/course-type/uploadCover', formData, res => {
+                        this.listLoading = false;
+                        if (res.code == "ok") {
+                            // 上传成功后,获取返回的图片URL
+                            const imageUrl = res.data && res.data.url ? res.data.url : this.coverImageUrl;
+                            
+                            // 更新本地数据
+                            this.categoryList[this.currentCategoryIndex].coverImage = imageUrl;
+                            
+                            // 同步更新到分类选项中
+                            const optionIndex = this.categoryOptions.findIndex(item => item.value === categoryId);
+                            if (optionIndex !== -1) {
+                                this.categoryOptions[optionIndex].coverImage = imageUrl;
+                            }
+                            
+                            this.$message({
+                                type: 'success',
+                                message: '封面图片上传成功!'
+                            });
+                        } else {
+                            this.$message({
+                                message: res.msg || '上传封面图片失败',
+                                type: 'error'
+                            });
+                        }
+                    }, error => {
+                        this.listLoading = false;
+                        this.$message({
+                            message: error || '上传封面图片失败',
+                            type: 'error'
+                        });
+                    });
+                }
+            }
+        },
+
+         // 设置分类封面
+         setCategoryCover(index, row) {
+            this.currentCategoryIndex = index;
+            this.coverImageUrl = row.coverImage || '';
+            this.coverDialogVisible = true;
+        },
+
+        // 处理封面图片上传前的验证
+        beforeCoverUpload(file) {
+            const isImage = file.type.indexOf('image/') === 0;
+            const isLt2M = file.size / 1024 / 1024 < 2;
+            
+            if (!isImage) {
+                this.$message.error('上传封面图片只能是图片格式!');
+            }
+            if (!isLt2M) {
+                this.$message.error('上传封面图片大小不能超过 2MB!');
+            }
+            
+            return isImage && isLt2M;
+        },
+
+        // 添加课程
+        addCourse(row) {
+            this.addDialogVisible = true;
+            if(!row) {
+                this.courseForm = {
+                    courseTypeId: '',
+                    courseName: '',
+                    courseDesc: '',
+                    coursePrice: 0,
+                    courseDuration: 0,
+                    coverImageUrl: '',
+                };
+                if (this.$refs.courseForm) {
+                    this.$refs.courseForm.resetFields();
+                }
+            } else {
+                console.log(row, '<==== 看看数据')
+                const { courseTypeId, courseName, courseDesc, coursePrice, courseDuration, coverImage, id } = row
+                this.courseForm = {
+                    id,
+                    courseTypeId,
+                    courseName,
+                    courseDesc,
+                    coursePrice,
+                    courseDuration,
+                    coverImageUrl: this.checkAndAddUpload(coverImage)
+                }
+            }
+        },
+
+        // 提交课程表单
+        submitCourseForm() {
+            this.$refs.courseForm.validate(valid => {
+                if (!valid) {
+                    return false;
+                }
+                
+                const formData = new FormData();
+                if(this.courseForm.id) {
+                    formData.append('id', this.courseForm.id);
+                }
+                formData.append('courseTypeId', this.courseForm.courseTypeId);
+                formData.append('courseName', this.courseForm.courseName);
+                formData.append('courseDesc', this.courseForm.courseDesc);
+                formData.append('coursePrice', this.courseForm.coursePrice);
+                formData.append('courseDuration', this.courseForm.courseDuration);
+                formData.append('coverImage', this.courseForm.coverImageUrl);
+
+                this.listLoading = true;
+                this.http.uploadFile('/course-info/saveOrUpdate', formData, res => {
+                    this.listLoading = false;
+                    if (res.code === "ok") {
+                        this.$message.success('添加课程成功');
+                        this.addDialogVisible = false;
+                        this.getList();
+                    } else {
+                        this.$message.error(res.msg || '添加课程失败');
+                    }
+                }, error => {
+                    this.listLoading = false;
+                    this.$message.error(error || '添加课程失败');
+                });
+            });
+        },
+
+        // 处理课程封面图片变更
+        handleCourseCoverChange(file) {
+            const row = file.raw
+            const formData = new FormData();
+            formData.append('multipartFile', row);
+            this.http.uploadFile('/common/uploadFile', formData, res => {
+              if (res.code == "ok") {
+                this.courseForm.coverImageUrl = this.checkAndAddUpload(res.data)
+              } else {
+                this.$message({
+                  message: res.msg || '图片上传失败',
+                  type: 'error'
+                });
+              }
+            })
+        },
+
+        checkAndAddUpload(str) {
+            if(!str) {
+                return '';
+            }
+            if (str.includes('/upload/')) {
+                return str;
+            } else {
+                return '/upload/' + str;
+            }
+        },
+
+        // 添加分类
+        addCategory() {
+            if (!this.newCategory.label || !this.newCategory.label.trim()) {
+                this.$message({
+                    type: 'warning',
+                    message: '请输入分类名称!'
+                });
+                return;
+            }
+            
+            // 调用后端API保存课程分类
+            this.http.post('/course-type/saveOrUpdate', {
+                typeName: this.newCategory.label
+            }, res => {
+                if (res.code == "ok") {
+                    // 生成唯一ID作为value,实际项目中应该使用后端返回的ID
+                    const categoryId = res.data && res.data.id ? res.data.id : 'category_' + Date.now();
+                    this.newCategory.value = categoryId;
+                    
+                    // 添加到分类列表
+                    this.categoryList.push({...this.newCategory});
+                    this.categoryOptions.push({...this.newCategory});
+                    
+                    // 清空输入
+                    this.newCategory.label = '';
+                    this.newCategory.value = '';
+                    
+                    this.$message({
+                        type: 'success',
+                        message: '添加分类成功!'
+                    });
+                } else {
+                    this.$message({
+                        message: res.msg || '添加分类失败',
+                        type: 'error'
+                    });
+                }
+            }, error => {
+                this.$message({
+                    message: error || '添加分类失败',
+                    type: 'error'
+                });
+            });
+        },
+        
+        // 删除分类
+        deleteCategory(index, row) {
+            this.$confirm('确认删除该分类?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                // 调用后端API删除课程分类
+                this.http.post('/course-type/delete', {
+                    id: row.value
+                }, res => {
+                    if (res.code == "ok") {
+                        // 从分类列表中删除
+                        this.categoryList.splice(index, 1);
+                        
+                        // 从下拉选项中删除
+                        const optionIndex = this.categoryOptions.findIndex(item => item.value === row.value);
+                        if (optionIndex !== -1) {
+                            this.categoryOptions.splice(optionIndex, 1);
+                        }
+                        
+                        this.$message({
+                            type: 'success',
+                            message: '删除分类成功!'
+                        });
+                    } else {
+                        this.$message({
+                            message: res.msg || '删除分类失败',
+                            type: 'error'
+                        });
+                    }
+                }, error => {
+                    this.$message({
+                        message: error || '删除分类失败',
+                        type: 'error'
+                    });
+                });
+            })
+        },
+    },
+
+    created() {
+        let height = window.innerHeight;
+        this.tableHeight = height - 195;
+        const that = this;
+        window.onresize = function temp() {
+            that.tableHeight = window.innerHeight - 195;
+        };
+
+        // 初始化分类列表
+        this.categoryList = [...this.categoryOptions];
+    },
+
+    mounted() {
+        // 获取课程分类数据
+        this.http.post('/course-type/list', {}, res => {
+            if (res.code == "ok") {
+                // 将后端返回的数据转换为前端需要的格式
+                this.categoryList = res.data.map(item => ({
+                    label: item.typeName,
+                    value: item.id,
+                    coverImage: item.coverImage || ''
+                }));
+
+                // 同步更新下拉选项
+                this.categoryOptions = [...this.categoryList];
+            } else {
+                this.$message({
+                    message: res.msg || '获取分类列表失败',
+                    type: 'error'
+                });
+            }
+        }, error => {
+            this.$message({
+                message: error || '获取分类列表失败',
+                type: 'error'
+            });
+        });
+
+        // 获取课程列表
+        this.getList();
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.rg_span {
+    display: inline-block;
+}
+
+.rg_span span {
+    text-align: right;
+    box-sizing: border-box;
+    padding-right: 10px;
+}
+
+.el-dialog__title {
+    display: inline-table;
+    margin-top: 20px;
+}
+
+.btns .el-button {
+    margin-left: 10px;
+    margin-bottom: 5px;
+}
+</style>
+<style>
+.course-cover-image {
+    width: 100px;
+    height: 100px;
+    object-fit: cover;
+    border-radius: 4px;
+}
+
+.otherForm .el-form-item {
+    float: left;
+    width: 50%;
+    margin: 0;
+}
+
+/* 视频播放器样式 */
+.video-player-container {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 100%;
+}
+
+.video-player {
+    max-width: 100%;
+    max-height: 500px;
+}
+
+.video-preview {
+    margin-top: 10px;
+    border: 1px solid #ebeef5;
+    border-radius: 4px;
+    padding: 10px;
+    background-color: #f9f9f9;
+}
+
+.video-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 10px;
+    padding-bottom: 8px;
+    border-bottom: 1px solid #ebeef5;
+}
+
+.video-title {
+    font-weight: bold;
+    color: #303133;
+}
+
+.delete-video-btn {
+    color: #f56c6c;
+    padding: 0;
+}
+
+.delete-video-btn:hover {
+    color: #f78989;
+}
+
+.video-name {
+    margin-top: 5px;
+    color: #606266;
+    font-size: 14px;
+}
+
+/* 编辑器样式 */
+.editor-container {
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    overflow: hidden;
+}
+
+.editor-toolbar {
+    background-color: #f5f7fa;
+    padding: 5px;
+    border-bottom: 1px solid #dcdfe6;
+    display: flex;
+    align-items: center;
+}
+
+.editor-btn {
+    background: none;
+    border: none;
+    padding: 5px 8px;
+    margin: 0 2px;
+    cursor: pointer;
+    border-radius: 3px;
+}
+
+.editor-btn:hover {
+    background-color: #e4e7ed;
+}
+
+.editor-separator {
+    width: 1px;
+    height: 16px;
+    background-color: #dcdfe6;
+    margin: 0 5px;
+}
+
+.editor-select {
+    height: 24px;
+    border: 1px solid #dcdfe6;
+    border-radius: 3px;
+    margin: 0 5px;
+}
+
+.editor-content {
+    width: 100%;
+    min-height: 120px;
+    padding: 10px;
+    border: none;
+    resize: vertical;
+    outline: none;
+    font-family: Arial, sans-serif;
+    font-size: 14px;
+}
+
+/* 上传链接样式 */
+.upload-item {
+    margin-bottom: 20px;
+}
+
+.upload-link {
+    color: #409eff;
+    cursor: pointer;
+    font-size: 14px;
+}
+
+.upload-link:hover {
+    text-decoration: underline;
+}
+
+/* 分类封面图片样式 */
+.category-cover-image {
+    width: 100px;
+    height: 56px;
+    object-fit: cover;
+    border-radius: 4px;
+}
+
+.cover-upload-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 20px 0;
+}
+
+.cover-uploader {
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+    width: 178px;
+    height: 100px;
+    margin-bottom: 10px;
+}
+
+.cover-uploader:hover {
+    border-color: #409EFF;
+}
+
+.cover-uploader-icon {
+    font-size: 28px;
+    color: #8c939d;
+    width: 178px;
+    height: 100px;
+    line-height: 100px;
+    text-align: center;
+}
+
+.cover-image {
+    width: 178px;
+    height: 100px;
+    display: block;
+    object-fit: cover;
+}
+
+.cover-tip {
+    color: #909399;
+    font-size: 12px;
+    margin-top: 10px;
+}
+
+/* 讲师照片上传样式 */
+.instructor-uploader {
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+    width: 100px;
+    height: 100px;
+    margin-bottom: 10px;
+}
+
+.instructor-uploader:hover {
+    border-color: #409EFF;
+}
+
+.instructor-uploader-icon {
+    font-size: 28px;
+    color: #8c939d;
+    width: 100px;
+    height: 100px;
+    line-height: 100px;
+    text-align: center;
+}
+
+.instructor-image {
+    width: 100px;
+    height: 100px;
+    display: block;
+    object-fit: cover;
+}
+
+.upload-tip {
+    color: #909399;
+    font-size: 12px;
+    margin-top: 5px;
+}
+
+.ql-snow .ql-picker-label::before {
+    position: relative;
+    top: -8px;
+}
+</style>

+ 310 - 13
fhKeeper/formulahousekeeper/course-pc/src/views/coursemanagement/list.vue

@@ -19,10 +19,7 @@
                         size="small"></el-input>
                 </el-form-item>
                 <el-form-item>
-                    <el-button type="primary" @click="addCourse" size="small">添加课程</el-button>
-                </el-form-item>
-                <el-form-item>
-                    <el-button type="primary" @click="addCourse" size="small">添加讲师</el-button>
+                    <el-button type="primary" @click="addCourse()" size="small">添加课程</el-button>
                 </el-form-item>
                 <el-form-item>
                     <el-button type="primary" @click="batchManage" size="small">分类管理</el-button>
@@ -53,11 +50,12 @@
                 </template>
             </el-table-column>
 
-            <el-table-column label="操作" width="250" class-name="btns" header-align="center" fixed="right">
+            <el-table-column label="操作" width="340" class-name="btns" header-align="center" fixed="right">
                 <template slot-scope="scope">
-                    <el-button size="small" type="danger" @click="deleteItem(scope.row)">删除</el-button>
-                    <el-button size="small" type="primary">编辑</el-button>
-                    <el-button size="small" :type="scope.row.courseStatus === 1 ? 'warning' : 'success'"
+                    <el-button size="mini" type="primary" @click="addVideo(scope.row)">添加视频</el-button>
+                    <el-button size="mini" type="primary" @click="addCourse(scope.row)">编辑</el-button>
+                    <el-button size="mini" type="danger" @click="deleteItem(scope.row)">删除</el-button>
+                    <el-button size="mini" :type="scope.row.courseStatus === 1 ? 'warning' : 'success'"
                         @click="uploadItem(scope.row)">{{ scope.row.courseStatus === 1 ? '下架' : '上架' }}</el-button>
                 </template>
             </el-table-column>
@@ -128,13 +126,112 @@
                 <el-button type="primary" @click="saveCategoryCover">确 定</el-button>
             </span>
         </el-dialog>
+
+        <!-- 添加课程 -->
+        <el-dialog :visible.sync="addDialogVisible" title="添加课程" width="800px" top="6.5vh">
+            <el-form :model="courseForm" :rules="courseRules" ref="courseForm" label-width="120px">
+                <el-form-item label="课程分类" prop="courseTypeId">
+                    <el-select v-model="courseForm.courseTypeId" placeholder="请选择" style="width:100%">
+                        <el-option v-for="item in categoryOptions" :key="item.value" 
+                            :label="item.label" :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="课程名称" prop="courseName">
+                    <el-input v-model="courseForm.courseName" placeholder="请输入课程名称"></el-input>
+                </el-form-item>
+                <el-form-item label="课程介绍" style="height: 200px;">
+                    <quill-editor style="height: 150px" ref="text" v-model="courseForm.courseDesc" class="myQuillEditor" :options="editorOption"/>
+                </el-form-item>
+                <el-form-item label="课程价格">
+                    <el-input-number v-model="courseForm.coursePrice" :min="0" :precision="2"></el-input-number>
+                </el-form-item>
+                <el-form-item label="课程时间(分钟)">
+                    <el-input-number v-model="courseForm.courseDuration" :min="0"></el-input-number>
+                </el-form-item>
+                <el-form-item label="课程封面">
+                    <el-upload
+                        class="cover-uploader"
+                        action="#"
+                        :show-file-list="false"
+                        :on-change="handleCourseCoverChange"
+                        :auto-upload="false"
+                        :before-upload="beforeCoverUpload">
+                        <img v-if="courseForm.coverImageUrl" :src="courseForm.coverImageUrl" class="cover-image" />
+                        <i v-else class="el-icon-plus cover-uploader-icon"></i>
+                    </el-upload>
+                    <div class="cover-tip">建议尺寸 16:9,大小不超过2MB</div>
+                </el-form-item>
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="addDialogVisible = false">取 消</el-button>
+                <el-button type="primary" @click="submitCourseForm">确 定</el-button>
+            </span>
+        </el-dialog>
+
+        <!-- 添加视频 -->
+        <el-dialog :visible.sync="addVideoVisable" width="900" top="6.5vh">
+            <div slot="title">
+                <div class="title-dios">
+                    <div>添加视频</div>
+                    <div style="margin-right: 30px;">
+                        <el-upload
+                            ref="uploadVideoRef"
+                            action="#"
+                            :disabled="addVideoLoading"
+                            :show-file-list="false"
+                            :limit="5"
+                            multiple
+                            :http-request="uploadVideo"
+                            accept="video/mp4,video/avi,video/x-msvideo">
+                            <el-button size="small" type="primary" :loading="addVideoLoading">上传视频</el-button>
+                        </el-upload>
+                    </div>
+                </div>
+            </div>
+            <div class="addVideoClass">
+                <addVideoCom ref="addVideoComRef" v-model="displayVideoList"></addVideoCom>
+            </div>
+            <span slot="footer" class="dialog-footer">
+                <el-button type="primary" @click="saveVideo()" :disabled="!displayVideoList.length || displayVideoList.some(video => video.videoError) || displayVideoList.some(video => video.videoLoading)" size="small" :loading="saveVideoLoading">保存视频</el-button>
+            </span>
+        </el-dialog>
     </section>
 </template>
 
 <script>
+// 富文本样式
+import 'quill/dist/quill.core.css'
+import 'quill/dist/quill.snow.css'
+import 'quill/dist/quill.bubble.css'
+// 导入富文本
+import { quillEditor } from 'vue-quill-editor'
+import addVideoCom from './addVideo.vue'
 export default {
+    components: {
+        quillEditor, // 富文本
+        addVideoCom, // 添加视频
+    },
     data() {
         return {
+            // 课程表单
+            courseForm: {
+                courseTypeId: '',
+                courseName: '',
+                courseDesc: '',
+                coursePrice: 0,
+                courseDuration: 0,
+                coverImageUrl: '',
+                coverImageFile: null
+            },
+            courseRules: {
+                courseTypeId: [
+                    { required: true, message: '请选择课程分类', trigger: 'change' }
+                ],
+                courseName: [
+                    { required: true, message: '请输入课程名称', trigger: 'blur' }
+                ]
+            },
             // 课程分类选择
             categoryValue: '',
             categoryOptions: [],
@@ -171,9 +268,103 @@ export default {
             
             // 用户信息
             user: sessionStorage.user ? JSON.parse(sessionStorage.user) : {},
+            editorOption: { // 富文本框里面的默认值
+                placeholder: '请输入内容...',
+                modules: {
+                    toolbar:[
+                        ['bold', 'italic', 'underline', 'strike'],    //加粗,斜体,下划线,删除线
+                        // ['blockquote', 'code-block'],     //引用,代码块
+                
+                        [{ 'header': 1 }, { 'header': 2 }],        // 标题,键值对的形式;1、2表示字体大小
+                        // [{ 'list': 'ordered'}, { 'list': 'bullet' }],     //列表
+                        // [{ 'script': 'sub'}, { 'script': 'super' }],   // 上下标
+                        // [{ 'indent': '-1'}, { 'indent': '+1' }],     // 缩进
+                        // [{ 'direction': 'rtl' }],             // 文本方向
+                
+                        [{ 'size': ['small', false, 'large', 'huge'] }], // 字体大小
+                        [{ 'header': [1, 2, 3, 4, 5, 6, false] }],     //几级标题
+                
+                        [{ 'color': [] }, { 'background': [] }],     // 字体颜色,字体背景颜色
+                        // [{ 'font': [] }],     //字体
+                        [{ 'align': [] }],    //对齐方式
+                
+                        ['clean'],    //清除字体样式
+                        // ['image','video']    //上传图片、上传视频
+                        []    //上传图片、上传视频
+                    ], //工具栏设置
+                },
+                theme: 'snow',
+            },
+
+            // 添加视频
+            addVideoRow: {},
+            addVideoVisable: false,
+            addVideoLoading: false,
+            videoList: [],
+            displayVideoList: [],
+            videiListTime: null,
+            saveVideoLoading: false,
         }
     },
     methods: {
+        saveVideo() {
+            console.log(this.displayVideoList, '<=== 看看数据')
+            return
+            this.addVideoVisable = false
+        },
+        uploadVideo(file) {
+            this.videoList.unshift(file.file)
+            if(this.videiListTime) {
+                clearTimeout(this.videiListTime)
+            }
+            this.videiListTime = setTimeout(() => {
+                this.batchUploadVideo()
+            }, 500)
+            // this.$refs.addVideoComRef.addNewRow()
+        },
+        batchUploadVideo() {
+            for(let i = 0; i < this.videoList.length; i++) {
+                const formData = new FormData()
+                formData.append('multipartFile', this.videoList[i])
+                this.displayVideoList.unshift({
+                    videoName: this.removeVideoSuffix(this.videoList[i].name),
+                    videoLoading: true,
+                    videoUrl: '',
+                    videoLecturerId: '',
+                    videoPreviewTime: 3,
+                    videoError: ''
+                })
+                this.http.uploadFile(`/common/uploadFile`, formData, res => {
+                    this.displayVideoList[i].videoUrl = res.data
+                    this.displayVideoList[i].videoLoading = false
+                }, err => {
+                    this.displayVideoList[i].videoLoading = false
+                    this.displayVideoList[i].videoError = '视频上传失败'
+                })
+            }
+
+            setTimeout(() => {
+                this.videoList = []
+                this.$refs.uploadVideoRef.clearFiles()
+            }, 1500)
+        },
+        // 去除视频名称后缀
+        removeVideoSuffix(str) { 
+            if (str.indexOf('.') > -1) {
+                return str.substring(0, str.lastIndexOf('.'))
+            } else {
+                return str
+            }
+        },
+        // 添加视频
+        addVideo(row) {
+            console.log(row, '<===== 单机的对象')
+            this.addVideoRow = row
+            this.videoList = []
+            this.displayVideoList = []
+            this.addVideoVisable = true
+        },
+
         // 分类管理
         batchManage() {
             this.categoryManageVisible = true;
@@ -226,16 +417,12 @@ export default {
                     this.list = res.data.records;
                     this.total = res.data.total;
                 } else {
-                    // 如果API调用失败,使用模拟数据(实际项目中可以移除这部分)
-                    this.useMockData();
                     this.$message({
                         message: res.msg || '获取课程列表失败',
                         type: 'error'
                     });
                 }
             }, error => {
-                // 如果API调用出错,使用模拟数据(实际项目中可以移除这部分)
-                this.useMockData();
                 this.listLoading = false;
                 this.$message({
                     message: error || '获取课程列表失败',
@@ -413,6 +600,99 @@ export default {
             return isImage && isLt2M;
         },
 
+        // 添加课程
+        addCourse(row) {
+            this.addDialogVisible = true;
+            if(!row) {
+                this.courseForm = {
+                    courseTypeId: '',
+                    courseName: '',
+                    courseDesc: '',
+                    coursePrice: 0,
+                    courseDuration: 0,
+                    coverImageUrl: '',
+                };
+                if (this.$refs.courseForm) {
+                    this.$refs.courseForm.resetFields();
+                }
+            } else {
+                console.log(row, '<==== 看看数据')
+                const { courseTypeId, courseName, courseDesc, coursePrice, courseDuration, coverImage, id } = row
+                this.courseForm = {
+                    id,
+                    courseTypeId,
+                    courseName,
+                    courseDesc,
+                    coursePrice,
+                    courseDuration,
+                    coverImageUrl: this.checkAndAddUpload(coverImage)
+                }
+            }
+        },
+
+        // 提交课程表单
+        submitCourseForm() {
+            this.$refs.courseForm.validate(valid => {
+                if (!valid) {
+                    return false;
+                }
+                
+                const formData = new FormData();
+                if(this.courseForm.id) {
+                    formData.append('id', this.courseForm.id);
+                }
+                formData.append('courseTypeId', this.courseForm.courseTypeId);
+                formData.append('courseName', this.courseForm.courseName);
+                formData.append('courseDesc', this.courseForm.courseDesc);
+                formData.append('coursePrice', this.courseForm.coursePrice);
+                formData.append('courseDuration', this.courseForm.courseDuration);
+                formData.append('coverImage', this.courseForm.coverImageUrl);
+
+                this.listLoading = true;
+                this.http.uploadFile('/course-info/saveOrUpdate', formData, res => {
+                    this.listLoading = false;
+                    if (res.code === "ok") {
+                        this.$message.success('添加课程成功');
+                        this.addDialogVisible = false;
+                        this.getList();
+                    } else {
+                        this.$message.error(res.msg || '添加课程失败');
+                    }
+                }, error => {
+                    this.listLoading = false;
+                    this.$message.error(error || '添加课程失败');
+                });
+            });
+        },
+
+        // 处理课程封面图片变更
+        handleCourseCoverChange(file) {
+            const row = file.raw
+            const formData = new FormData();
+            formData.append('multipartFile', row);
+            this.http.uploadFile('/common/uploadFile', formData, res => {
+              if (res.code == "ok") {
+                this.courseForm.coverImageUrl = this.checkAndAddUpload(res.data)
+              } else {
+                this.$message({
+                  message: res.msg || '图片上传失败',
+                  type: 'error'
+                });
+              }
+            })
+        },
+
+        checkAndAddUpload(str) {
+            if(!str) {
+                return '';
+            }
+            if (str.includes('/upload/')) {
+                return str;
+            } else {
+                return '/upload/' + str;
+            }
+        },
+
         // 添加分类
         addCategory() {
             if (!this.newCategory.label || !this.newCategory.label.trim()) {
@@ -544,6 +824,18 @@ export default {
 </script>
 
 <style lang="scss" scoped>
+.title-dios {
+    font-size: 20px;
+    color: #666;
+    display: flex;
+    align-items: center;
+    width: 100%;
+    justify-content: space-between;
+}
+.addVideoClass {
+    height: 65vh;
+    overflow-y: auto;
+}
 .rg_span {
     display: inline-block;
 }
@@ -785,4 +1077,9 @@ export default {
     font-size: 12px;
     margin-top: 5px;
 }
-</style>
+
+.ql-snow .ql-picker-label::before {
+    position: relative;
+    top: -8px;
+}
+</style>

+ 98 - 45
fhKeeper/formulahousekeeper/course-pc/src/views/lecturerManagement/index.vue

@@ -20,7 +20,9 @@
             <el-table-column prop="introduction" label="讲师介绍" min-width="300" align="center"></el-table-column>
             <el-table-column label="讲师照片" width="120" align="center">
                 <template slot-scope="scope">
-                    <img v-if="scope.row.photo" :src="scope.row.photo" class="lecturer-photo" />
+                    <div v-if="scope.row.photo" >
+                        <img :src="scope.row.photo" class="lecturer-photo" @click="previewImage(scope.row.photo)" />
+                      </div>
                     <span v-else>无照片</span>
                 </template>
             </el-table-column>
@@ -74,6 +76,12 @@
                 <el-button type="primary" @click="saveLecturer">确 定</el-button>
             </span>
         </el-dialog>
+
+        <!-- 图片预览 -->
+        <viewer ref="imageWrapper" :images="images" style="opacity: 1;position: absolute;top: -9999px;">
+            <img v-for="(image, index) in images" :src="image" :key="index" />
+        </viewer>
+  
     </section>
 </template>
 
@@ -112,11 +120,24 @@ export default {
                 name: [
                     { required: true, message: '请输入讲师名称', trigger: 'blur' }
                 ]
-            }
+            },
+
+            images: [],
         }
     },
     methods: {
+        previewImage(urls) {
+            this.images = [urls];
+            this.$nextTick(() => {
+                if (this.$refs.imageWrapper && this.$refs.imageWrapper.$viewer) {
+                    this.$refs.imageWrapper.$viewer.show();
+                }
+            });
+        },
         checkAndAddUpload(str) {
+          if(!str) {
+              return '';
+          }
           if (str.includes('/upload/')) {
             return str;
           } else {
@@ -133,17 +154,22 @@ export default {
         // 获取讲师列表
         getList() {
             this.listLoading = true;
-            // 这里应该是调用API获取讲师列表
-            // 模拟数据
-            setTimeout(() => {
-                this.list = [
-                    { id: 1, name: '张老师', introduction: '资深前端开发工程师', photo: '' },
-                    { id: 2, name: '李老师', introduction: '后端架构师', photo: '' },
-                    { id: 3, name: '王老师', introduction: '全栈开发工程师', photo: '' }
-                ];
-                this.total = 3;
+            this.http.post('/course-teacher/pageList', {
+                teacherName: this.keyword,
+                page: this.page,
+                size: this.size
+            }, res => {
+                this.list = res.data.records.map(item => ({
+                    id: item.id,
+                    name: item.teacherName,
+                    introduction: item.teacherDesc,
+                    photo: this.checkAndAddUpload(item.teacherImgUrl)
+                }));
+                this.total = res.data.total;
                 this.listLoading = false;
-            }, 500);
+            }, () => {
+                this.listLoading = false;
+            })
         },
 
         // 分页相关
@@ -195,12 +221,15 @@ export default {
                 cancelButtonText: '取消',
                 type: 'warning'
             }).then(() => {
-                // 这里应该是调用API删除讲师
-                this.$message({
-                    type: 'success',
-                    message: '删除成功!'
+                this.http.post('/course-teacher/delete', { id: row.id }, res => {
+                    if (res.data.code === 'ok') {
+                        this.$message({
+                            type: 'success',
+                            message: '删除成功!'
+                        });
+                        this.getList();
+                    }
                 });
-                this.getList();
             })
         },
 
@@ -216,13 +245,16 @@ export default {
                 cancelButtonText: '取消',
                 type: 'warning'
             }).then(() => {
-                // 这里应该是调用API批量删除讲师
-                this.$message({
-                    type: 'success',
-                    message: '批量删除成功!'
-                });
-                this.getList();
-                this.selectedIds = [];
+                this.http.post('/course-teacher/batchDelete', { ids: this.selectedIds }, res => {
+                    if (res.data.code === 'ok') {
+                        this.$message({
+                            type: 'success',
+                            message: '批量删除成功!'
+                        });
+                        this.getList();
+                        this.selectedIds = [];
+                    }
+                })
             })
         },
 
@@ -230,39 +262,53 @@ export default {
         saveLecturer() {
             this.$refs.lecturerForm.validate((valid) => {
                 if (valid) {
-                    // 这里应该是调用API保存讲师信息
-                    if (this.photoFile) {
-                        // 如果有上传照片,先上传照片
-                        // 模拟上传照片
-                        setTimeout(() => {
-                            this.lecturerForm.photo = this.photoUrl;
-                            this.saveLecturerInfo();
-                        }, 500);
-                    } else {
-                        this.saveLecturerInfo();
-                    }
+                    this.saveLecturerInfo();
                 }
             });
         },
 
         // 保存讲师信息
         saveLecturerInfo() {
-            // 模拟保存
-            setTimeout(() => {
-                this.$message({
-                    type: 'success',
-                    message: '保存成功!'
-                });
-                this.dialogVisible = false;
-                this.getList();
-            }, 500);
+            const params = {
+                teacherName: this.lecturerForm.name,
+                teacherDesc: this.lecturerForm.introduction,
+                teacherImgUrl: this.photoUrl
+            };
+            
+            if (this.currentLecturerId) {
+                params.id = this.currentLecturerId;
+            }
+
+            this.http.post('/course-teacher/save', params, res => {
+                if (res.code === 'ok') {
+                    this.$message({
+                        type: 'success',
+                        message: '保存成功!'
+                    });
+                    this.dialogVisible = false;
+                    this.getList();
+                }
+            })
         },
 
         // 处理照片变更
         handlePhotoChange(file) {
+            const isImage = file.raw.type.indexOf('image/') === 0;
+            const isLt2M = file.raw.size / 1024 / 1024 < 5;
+            
+            if (!isImage) {
+                this.$message.error('上传文件只能是图片格式!');
+                return;
+            }
+            if (!isLt2M) {
+                this.$message.error('上传图片大小不能超过 2MB!');
+                return;
+            }
+
             const row = file.raw
             const formData = new FormData();
             formData.append('multipartFile', row);
+            
             this.http.uploadFile('/common/uploadFile', formData, res => {
               if (res.code == "ok") {
                 this.photoUrl = this.checkAndAddUpload(res.data)
@@ -299,6 +345,12 @@ export default {
             that.tableHeight = window.innerHeight - 195;
         };
     },
+    beforeDestroy() {
+        if (this.viewer) {
+        this.viewer.destroy();
+        this.viewer = null;
+        }
+    },
 
     mounted() {
         this.getList();
@@ -320,7 +372,8 @@ export default {
     width: 80px;
     height: 80px;
     object-fit: cover;
-    border-radius: 50%;
+    border-radius: 10px;
+    cursor: pointer;
 }
 
 .lecturer-uploader {