فهرست منبع

提交线下研修班报名页面

Lijy 4 روز پیش
والد
کامیت
0bc50154a9

+ 11 - 0
fhKeeper/formulahousekeeper/course-pc/src/routes.js

@@ -11,6 +11,7 @@ import courselist from './views/coursemanagement/list';
 import lecturerList from './views/lecturerManagement/index.vue'
 import offlineTraining from './views/offlineTraining/offlineTraining.vue'
 import examCertification from './views/examCertification/examCertification.vue'
+import offlineRegistration from './views/offlineRegistration/registration.vue'
 
 Vue.use(Router)
 
@@ -79,6 +80,16 @@ export const allRouters = [
             { path: '/offline-training', component: offlineTraining, name: '线下研修班' }
         ]
     },
+    {
+        path: '/offline-registration',
+        component: Home,
+        name: '线下研修班报名',
+        iconCls: 'iconfont firerock-iconkehu',
+        leaf: true,
+        children: [
+            { path: '/offline-registration', component: offlineRegistration, name: '线下研修班报名' }
+        ]
+    },
     {
         path: '*',
         hidden: true,

+ 505 - 0
fhKeeper/formulahousekeeper/course-pc/src/views/offlineRegistration/registration.vue

@@ -0,0 +1,505 @@
+<template>
+  <section>
+    <!-- 工具条 -->
+    <el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
+      <el-tabs v-model="activeTab">
+        <el-tab-pane label="预报名信息" name="pre-registration"></el-tab-pane>
+        <el-tab-pane label="报名课程" name="course-registration">
+          <div class="registerForCourses">
+            <el-form :inline="true" @submit.native.prevent>
+              <el-form-item label="姓名">
+                <el-input v-model="listQuery.name" placeholder="请输入姓名" clearable @change="handleFilter" size="small"></el-input>
+              </el-form-item>
+              <el-form-item label="手机号">
+                <el-input v-model="listQuery.phone" placeholder="请输入手机号" clearable @change="handleFilter" size="small"></el-input>
+              </el-form-item>
+            </el-form>
+
+            <div>
+              <el-button type="primary" size="small" @click="showAddDialog">添加报名</el-button>
+              <el-button type="primary" size="small" @click="batchManage">线下课程管理</el-button>
+            </div>
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+    </el-col>
+
+    <!-- 预报名列表 -->
+    <transition name="fade" mode="out-in">
+      <el-table 
+        v-if="activeTab === 'pre-registration'"
+        :data="preRegistrationList" 
+        highlight-current-row 
+        v-loading="listLoading"
+        :height="tableHeight"
+        style="width: 100%;">
+        <el-table-column label="序号" prop="id" width="80" align="center"></el-table-column>
+        <el-table-column label="姓名" prop="name" align="center"></el-table-column>
+        <el-table-column label="手机号" prop="phone" align="center"></el-table-column>
+        <el-table-column label="操作" width="180" align="center" fixed="right">
+          <template slot-scope="scope">
+            <el-button size="small" type="primary" @click="handleEdit(scope.row)">编辑</el-button>
+            <el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </transition>
+
+    <!-- 报名课程列表 -->
+    <transition name="fade" mode="out-in">
+      <div>
+        <el-table 
+          v-if="activeTab === 'course-registration'"
+          :data="courseRegistrationList" 
+          highlight-current-row 
+          v-loading="listLoading"
+          :height="tableHeight - 58"
+          style="width: 100%;">
+          <el-table-column label="序号" prop="id" width="80" align="center"></el-table-column>
+          <el-table-column label="姓名" prop="name" align="center"></el-table-column>
+          <el-table-column label="手机号" prop="phone" align="center"></el-table-column>
+          <el-table-column label="报名课程" prop="course" align="center"></el-table-column>
+          <el-table-column label="操作" width="180" align="center" fixed="right">
+            <template slot-scope="scope">
+              <el-button size="small" type="primary" @click="handleEdit(scope.row)">编辑</el-button>
+              <el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </transition>
+
+    <!-- 添加报名弹窗 -->
+    <el-dialog title="线下研修班报名" :visible.sync="dialogVisible" width="30%">
+      <el-form :model="registrationForm" label-width="80px">
+        <el-form-item label="姓名">
+          <el-input v-model="registrationForm.name"></el-input>
+        </el-form-item>
+        <el-form-item label="手机">
+          <el-input v-model="registrationForm.phone"></el-input>
+        </el-form-item>
+        <el-form-item label="报名课程">
+          <el-select v-model="registrationForm.course" placeholder="请选择课程" style="width: 100%">
+            <el-option 
+              v-for="item in courseOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value">
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitRegistration">确定</el-button>
+      </span>
+    </el-dialog>
+
+    <!-- 分页 -->
+    <el-col :span="24" class="toolbar">
+      <el-pagination
+        :current-page="listQuery.page"
+        :page-size="listQuery.limit"
+        :total="total"
+        layout="total, prev, pager, next, jumper"
+        @current-change="handleCurrentChange"
+        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>
+  </section>
+</template>
+
+<script>
+export default {
+  name: 'OfflineRegistration',
+  data() {
+    return {
+      activeTab: 'pre-registration',
+      listQuery: {
+        page: 1,
+        limit: 10,
+        name: '',
+        phone: ''
+      },
+      total: 0,
+      listLoading: false,
+      tableHeight: 0,
+      preRegistrationList: [],
+      courseRegistrationList: [],
+      dialogVisible: false,
+      registrationForm: {
+        name: '',
+        phone: '',
+        course: ''
+      },
+      courseOptions: [
+        { value: 'Vue高级课程', label: 'Vue高级课程' },
+        { value: 'React入门', label: 'React入门' },
+        { value: 'JavaScript基础', label: 'JavaScript基础' },
+        { value: 'Node.js实战', label: 'Node.js实战' }
+      ],
+      // 分类管理相关
+      categoryManageVisible: false,
+      categoryList: [],
+      newCategory: {
+        label: '',
+        value: '',
+        coverImage: ''
+      },
+      // 分类封面设置
+      coverDialogVisible: false,
+      currentCategoryIndex: -1,
+      coverImageUrl: '',
+      coverImageFile: null
+    }
+  },
+  created() {
+    let height = window.innerHeight
+    this.tableHeight = height - 195
+    const that = this
+    window.onresize = function temp() {
+      that.tableHeight = window.innerHeight - 195
+      console.log(that.tableHeight, '<==== that.tableHeight')
+    }
+  },
+  methods: {
+    getList() {
+      this.listLoading = true
+      // 模拟数据
+      setTimeout(() => {
+        this.preRegistrationList = [
+          { id: 1, name: '张三', phone: '13800138000' },
+          { id: 2, name: '李四', phone: '13800138001' }
+        ]
+        this.courseRegistrationList = [
+          { id: 1, name: '张三', phone: '13800138000', course: 'Vue高级课程' },
+          { id: 2, name: '李四', phone: '13800138001', course: 'React入门' }
+        ]
+        this.total = 20
+        this.listLoading = false
+      }, 500)
+    },
+    handleFilter() {
+      this.listQuery.page = 1
+      this.getList()
+    },
+    handleCurrentChange(val) {
+      this.listQuery.page = val
+      this.getList()
+    },
+    handleEdit(row) {
+      console.log(row)
+    },
+    handleDelete(row) {
+      this.$confirm('确认删除该记录吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$message({
+          type: 'success',
+          message: '删除成功!'
+        });
+      });
+    },
+    showAddDialog() {
+      this.dialogVisible = true
+    },
+    submitRegistration() {
+      // 这里添加提交逻辑
+      this.courseRegistrationList.push({
+        id: this.courseRegistrationList.length + 1,
+        name: this.registrationForm.name,
+        phone: this.registrationForm.phone,
+        course: this.registrationForm.course
+      });
+      this.$message({
+        type: 'success',
+        message: '报名成功!'
+      });
+      this.dialogVisible = false;
+      this.resetForm();
+    },
+    resetForm() {
+      this.registrationForm = {
+        name: '',
+        phone: '',
+        course: ''
+      }
+    },
+    // 分类管理
+    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.courseOptions = [...this.categoryList];
+        } else {
+          this.$message({
+            message: res.msg || '获取分类列表失败',
+            type: 'error'
+          });
+        }
+      }, error => {
+        this.$message({
+          message: error || '获取分类列表失败',
+          type: 'error'
+        });
+      });
+    },
+    // 设置分类封面
+    setCategoryCover(index, row) {
+      this.currentCategoryIndex = index;
+      this.coverImageUrl = row.coverImage || '';
+      this.coverDialogVisible = true;
+    },
+    // 处理封面图片变更
+    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.courseOptions.findIndex(item => item.value === categoryId);
+              if (optionIndex !== -1) {
+                this.courseOptions[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'
+            });
+          });
+        }
+      }
+    },
+    // 处理封面图片上传前的验证
+    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;
+    },
+    // 保存分类封面
+    saveCategoryCover() {
+      if (!this.coverImageUrl) {
+        this.$message.warning('请先上传封面图片!');
+        return;
+      }
+      
+      // 关闭对话框
+      this.coverDialogVisible = false;
+      this.$message({
+        type: 'success',
+        message: '设置封面成功!'
+      });
+    },
+    // 添加分类
+    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.courseOptions.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.courseOptions.findIndex(item => item.value === row.value);
+            if (optionIndex !== -1) {
+              this.courseOptions.splice(optionIndex, 1);
+            }
+            
+            this.$message({
+              type: 'success',
+              message: '删除分类成功!'
+            });
+          } else {
+            this.$message({
+              message: res.msg || '删除分类失败',
+              type: 'error'
+            });
+          }
+        }, error => {
+          this.$message({
+            message: error || '删除分类失败',
+            type: 'error'
+          });
+        });
+      });
+    },
+  },
+  mounted() {
+    this.getList()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.registerForCourses {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.toolbar {
+  padding-bottom: 10px;
+}
+
+.fade-enter-active, .fade-leave-active {
+  transition: opacity .3s ease;
+}
+.fade-enter, .fade-leave-to {
+  opacity: 0;
+}
+</style>