构建多分校网校平台(二):视频课程技术详解

在当前数字化教育浪潮中,构建一个多分校网校平台不仅能有效整合教学资源,还能提升教学效率,实现教育资源的共享与优化。本文将深入探讨视频课程在网校平台中的技术实现,帮助教育机构构建高效、稳定的多分校网校平台。

1. 平台架构设计

一个高效的多分校网校平台需要合理的架构设计,通常包括以下几个部分:

  • 前端:负责用户交互,包括视频播放、互动功能等。
  • 后端:负责业务逻辑处理,包括视频管理、用户管理等。
  • 数据库:存储用户信息、课程信息、学习记录等。
  • 云服务:提供视频存储和分发服务。

2. 前端开发:

使用Vue.js构建现代化的前端应用。创建一个简单的视频信息展示页面:

<template>
  <div>
  <a-card :bordered="false" v-show="showList">

    <a-row>
      <a-col :span="12">
         <span style="font-weight: 600; font-style: normal; font-size: 20px; color: #000000D8">
           视频课程
         </span>
      </a-col>
      <a-col :span="12" >
        <a-button @click="addCourse" type="primary"  style="float: right">新建课程</a-button>
      </a-col>
    </a-row>
    <div >

    <!-- 专业科目 -->
    <MajorList @changeMajor="changeMajor"></MajorList>

    <a-divider orientation="left">
    </a-divider>
    <!-- 查询区域 -->
    <div class="table-page-search-wrapper" >
      <a-form layout="inline" @keyup.enter.native="searchQuery">
        <a-row :gutter="24">
          <a-col :xl="6" :lg="7" :md="8" :sm="24">
            <a-form-item label="课程名称">
              <j-input placeholder="请输入课程名称" v-model="queryParam.courseVideoName"></j-input>
            </a-form-item>
          </a-col>
<!--          <a-col :xl="6" :lg="7" :md="8" :sm="24">-->
<!--            <a-form-item label="学年">-->
<!--              <a-input placeholder="请输入学年" v-model="queryParam.schoolYear"></a-input>-->
<!--            </a-form-item>-->
<!--          </a-col>-->

          <a-col :xl="4" :lg="7" :md="8" :sm="24">
            <a-form-item label="学年">
              <a-date-picker
                mode="year"
                placeholder="请选择年份"
                format='YYYY'
                v-model="queryParam.yearValue"
                :open='yearShowOne'
                @openChange="openChangeOne"
                @panelChange="panelChangeOne"
                @change="change"
              />
            </a-form-item>
          </a-col>
          <template>
            <a-col :xl="6" :lg="7" :md="8" :sm="24">
              <a-form-item label="发布状态">
                <j-dict-select-tag placeholder="请选择发布状态" v-model="queryParam.postStatus" dictCode="post_status"/>
              </a-form-item>
            </a-col>
          </template>
          <a-col :xl="6" :lg="7" :md="8" :sm="24">
            <span style="overflow: hidden;" class="table-page-search-submitButtons">
              <a-button type="primary" @click="searchQuery1" icon="search">查询</a-button>
              <a-button  @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
              <a-button  @click="checkVideoAll" icon="reload" style="float: right">一键共用</a-button>
            </span>
          </a-col>
        </a-row>
      </a-form>
    </div>
    <!-- 查询区域-END -->

    <!-- table区域-begin -->
    <div>
<!--      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">-->
<!--        <i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>项-->
<!--        <a style="margin-left: 24px" @click="onClearSelected">清空</a>-->
<!--      </div>-->

      <a-table
        ref="table"
        size="middle"
        :scroll="{x:true}"
        bordered
        rowKey="id"
        :columns="columns"
        :dataSource="dataSource"
        :pagination="pagination"
        :loading="loading"
        class="j-table-force-nowrap"
        @change="handleTableChange">

        <template slot="htmlSlot" slot-scope="text">
          <div v-html="text"></div>
        </template>
        <template slot="courseType" slot-scope="text">
          <div v-if="text == 1">平台</div>
          <div v-if="text == 2">普为</div>
        </template>
        <template slot="imgSlot" slot-scope="text">
          <span v-if="!text" style="font-size: 12px;font-style: italic;">无图片</span>
          <img v-else :src="getImgView(text)" height="25px" alt="" style="max-width:80px;font-size: 12px;font-style: italic;"/>
        </template>
        <template slot=learningPhaseSlot slot-scope="text">
          <a-tag v-if = "text=='导学'" color="cyan" > {{text}}</a-tag>
          <a-tag v-else-if="text=='基础'" color="green" > {{text}}</a-tag>
          <a-tag v-else  color="blue">
            {{text}}
          </a-tag>
        </template>
        <template slot=postStatusSlot slot-scope="text">
          <a-tag v-if = "text=='已发布'" color="blue" > {{text}}</a-tag>
          <a-tag v-else = "text=='未发布'" color="gray" > {{text}}</a-tag>
        </template>
        <template slot="fileSlot" slot-scope="text">
          <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
          <a-button
            v-else
            :ghost="true"
            type="primary"
            icon="download"
            size="small"
            @click="downloadFile(text)">
            下载
          </a-button>
        </template>
        <span slot="action" slot-scope="text,record">
           <a @click="editCourse(record)" >管理</a>
          <a-divider type="vertical" />
           <a @click="updateCourse(record)" v-if="record.postStatus==0">发布</a>
          <a @click="updateCourse2(record)" v-if="record.postStatus==1">取消发布</a>
          <a-divider type="vertical" />
          <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
                  <a>删除</a>
          </a-popconfirm>
<!--          <a-divider type="vertical" />-->
<!--          <a-dropdown>-->
<!--            <a class="ant-dropdown-link">更多 <a-icon type="down" /></a>-->
<!--            <a-menu slot="overlay">-->
<!--              <a-menu-item>-->
<!--                <a @click="handleDetail(record)">详情</a>-->
<!--              </a-menu-item>-->
<!--              <a-menu-item>-->
<!--                <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">-->
<!--                  <a>删除</a>-->
<!--                </a-popconfirm>-->
<!--              </a-menu-item>-->
<!--            </a-menu>-->
<!--          </a-dropdown>-->
        </span>
      </a-table>
    </div>
    </div>

  </a-card>
    <wd-course-form ref="WdCourseFormRef" v-show="wdCourseFormVisible" :courseId="courseId" :majorId="majorId" :templateId="templateId"  @goback="goback"></wd-course-form>

    <a-modal v-model="visibleVideo"
             cancelText="关闭"
             :closable="!copySpin"
             :keyboard="!copySpin"
             :maskClosable="!copySpin"
             width="800px"
    >
      <template solt="title">
        <p style="font-size: x-large">共用内容</p>
        <p style="color: red">点击复制后程序会在后台进行运行,可自行刷新查看!</p>
        <a-divider />
      </template>
      <a-spin :spinning="copySpin">
        <div>
          <a-row>
            <a-col :span="24">
              <a-cascader @change="majorChange" style="width: 100%" v-model="sourceMajorId" :display-render="displayRender" :options="options" :show-search="{ filter }" placeholder="请选择" />
            </a-col>
            <a-col :span="24">
              <div style="height: 20px; width: 100%; float: left;">
                <div slot="header" >
                  <div style="align-items: center;width: 10%; float: left;">
                    <a-checkbox  :checked="checkAll" @change="onCheckAllChange"></a-checkbox>
                  </div>
                  <div style="width: 70%; text-align: center; float: left;">
                    课程名称
                  </div>
                  <div style="width: 20%; text-align: center; float: left;">
                    年份
                  </div>
                </div>
              </div>
              <div style="height: 400px; width: 100%;overflow: auto;float: left;">
                <a-list :data-source="videoListAll">
                  <a-list-item slot="renderItem" slot-scope="item, index">
                    <div style="align-items: center;width: 10%;">
                      <a-checkbox :value="item.id" :checked="checkAllList.indexOf(item.id) > -1" @change="listCheckbox"></a-checkbox>
                    </div>
                    <div style="text-align: center;width: 70%;">{{item.courseVideoName}}</div>
                    <div style="text-align: center;width: 20%;">{{item.schoolYear}}</div>
                  </a-list-item>
                  <div v-if="false" class="demo-loading-container">
                    <a-spin />
                  </div>
                </a-list>
              </div>
            </a-col>
          </a-row>
        </div>
      </a-spin>
      <template slot="footer">
        <a-button :disabled="copySpin" @click="copyEntity">复制</a-button>
        <a-button :disabled="copySpin" @click="sharCancel">关闭</a-button>
      </template>
    </a-modal>

  </div>
</template>

<script>
  import moment from 'moment'
  import '@/assets/less/TableExpand.less'
  import { mixinDevice } from '@/utils/mixin'
  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
  import MajorList from '@/components/MajorList'
  import WdCourseForm from './modules/WdCourseForm'
  import { httpAction, getAction, postAction } from '@/api/manage'

  export default {
    name: 'WdCourseVideoList',
    mixins:[JeecgListMixin, mixinDevice],

    components: {
      WdCourseForm,
      MajorList,
    },
    data () {
      let ellipsis = (v, l = 20) => (<j-ellipsis value={v} length={l}/>)
      return {
        checkAll: false,
        videoListAll: [],
        checkAllList: [],
        options: [],
        sourceMajorId: [],
        visibleVideo: false,
        copySpin: false,
        //重构
        showList:true,
        courseId: '',
        majorId:'',
        templateId:'',
        wdCourseFormVisible: false,
          //重构
        editabletabsValue:'',
        description: '视频课程管理页面',
        // 表头
        columns: [

          {
            title:'课程编码',
            align:"center",
            dataIndex: 'id'
          },
          {
            title:'课程名称',
            align:"center",
            dataIndex: 'courseVideoName',
            customRender: (t) => ellipsis(t)
          },
          {
            title:'课程类型',
            align:"center",
            dataIndex: 'courseType',
            scopedSlots: {customRender: 'courseType'}
          },
          {
            title:'课程图片',
            align:"center",
            dataIndex: 'courseVideoImgPath',
            scopedSlots: {customRender: 'imgSlot'}
          },
          {
            title:'开课时间',
            align:"center",
            dataIndex: 'startTime',
            customRender:function (text) {
              return !text?"":(text.length>10?text.substr(0,10):text)
            }
          },
          {
            title:'课时',
            align:"center",
            dataIndex: 'classHour'
          },
          {
            title:'学年',
            align:"center",
            dataIndex: 'schoolYear'
          },
          {
            title: '排序',
            align: "center",
            dataIndex: 'sort'
          },
          {
            title:'学习阶段',
            align:"center",
            dataIndex: 'learningPhase_dictText',
            scopedSlots: { customRender: 'learningPhaseSlot' },
          },
          {
            title: '标签',
            align: "center",
            dataIndex: 'labelNames',
          },
          {
            title:'发布状态',
            align:"center",
            dataIndex: 'postStatus_dictText',
              scopedSlots: { customRender: 'postStatusSlot' },
          },
          {
            title: '是否参与售卖',
            align: "center",
            dataIndex: 'isSale',
            customRender: function (text) {
              if (text==0){
                return '是'
              }else {
                return '否'
              }
            }
          },
            {
                title: '操作',
                dataIndex: 'action',
                align:"center",
                fixed:"right",
                width:147,
                scopedSlots: { customRender: 'action' }
            }
        ],
        url: {
          list: "/course/WdCourseVideo/wdCourseVideo/list",
          delete: "/course/WdCourseVideo/wdCourseVideo/delete",
          deleteBatch: "/course/WdCourseVideo/wdCourseVideo/deleteBatch",
          exportXlsUrl: "/course/WdCourseVideo/wdCourseVideo/exportXls",
          importExcelUrl: "course/WdCourseVideo/wdCourseVideo/importExcel",
          majorList: "/major/list",
          edit: "/course/WdCourseVideo/wdCourseVideo/edit",
          majorLists: '/major/listByParentId',
          findByCourseVideo: '/course/WdCourseVideo/wdCourseVideo/findByCourseVideo',
          copyCourseVideo: '/course/WdCourseVideo/wdCourseVideo/copyCourseVideo',
        },
        dictOptions:{},
        superFieldList:[],
        majorParentList:[],
        model:{},
          dataSource:[],
          yearShowOne: false,
          queryParam: {
              majorIds: '',
              courseVideoName: '',
              yearValue: null,// 设置值: moment(value, 'YYYY')
              schoolYear: '',
              postStatus: ''
          },
          pagination: {
              current: 1,
              pageSize: 10,
              total: 0,
              showTotal: (total, range) => range[0] + "-" + range[1] + " 共 " + total+" 条",
              showSizeChanger: true,
              pageSizeOptions: ['10', '20', '30'],
              showQuickJumper: true,
              onChange: (page, pageSize) => {
                  this.onChange(page, pageSize);
              },
              onShowSizeChange: (current, size) => {
                  this.onShowSizeChange(current, size);
              },
          },
      }
    },
    created() {
    this.searchQuery1();
    },

    computed: {
      importExcelUrl: function(){
        return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`;
      },
    },
    methods: {
      copyEntity(){
        this.copySpin = true;
        if(this.sourceMajorId <= 0){
          this.$message.error('请选择专业')
          this.copySpin = false;
          return;
        }
        if(this.checkAllList.length <= 0){
          this.$message.error('请选择课程')
          this.copySpin = false;
          return;
        }
        // copy数据
        console.log(this.sourceMajorId[1]);
        console.log(this.checkAllList);
        console.log(this.majorId);

        const data = {
          sourceMajorId: this.sourceMajorId[1],
          courseVideoList: this.checkAllList,
          targetMajorId: this.majorId
        }

        postAction(this.url.copyCourseVideo, data).then(res => {
          if (res.success) {
            this.copySpin = false;
            this.$message.success(res.message);
            this.visibleVideo = false;

            this.changeMajor(this.majorId);
          } else {
            this.copySpin = false;
            this.$message.warning(res.message);
          }
        })




      },
      sharCancel(){
        this.visibleVideo = false;
      },
      checkVideoAll(){
        // 一键共用课程
        this.loadingMajor();
        this.visibleVideo = true;
        this.videoListAll = [];
        this.sourceMajorId = [];
        this.checkAll = false;
        this.checkAllList = [];
      },
      displayRender({ labels }) {
        return labels.join('-');
      },
      majorChange(e){
        console.log(this.sourceMajorId[1]);
        this.findByCourseVideo(this.sourceMajorId[1]);
      },
      findByCourseVideo(majorId){
        getAction(this.url.findByCourseVideo+'?majorId='+majorId).then(res => {
          if(res.success) {
            this.videoListAll = res.result || [];
            console.log(this.videoListAll);
          } else {
            this.$message.error(res.message);
          }
        })
      },
      listCheckbox(e) {
        if(!e.target.checked) {
          if(this.checkAllList.indexOf(e.target.value) > -1) {
            this.checkAllList.splice(this.checkAllList.indexOf(e.target.value), 1)
          }
        } else {
          this.checkAllList.push(e.target.value)
        }
        if(this.checkAllList.length === this.videoListAll.length) {
          this.checkAll = true
        } else {
          this.checkAll = false
        }
      },
      onCheckAllChange(e) {
        this.checkAll = e.target.checked
        if(!this.checkAll) {
          this.checkAllList = []
        } else {
          let arr = []
          this.videoListAll.forEach(item => {
            arr.push(item.id)
          })
          this.checkAllList = arr
        }
      },
      loadingMajor() {
        getAction(this.url.majorLists).then(res => {
          if(res.success) {
            let options = [];
            res.result.forEach(e => {
              let parent = {
                value: e.id,
                label: e.majorName,
              };
              let children = [];
              e.children.forEach(e2 => {
                let major = {
                  value: e2.id,
                  label: e2.majorName,
                };
                children.push(major);
              });
              parent.children = children;
              options.push(parent);
            });
            this.options = options;
          } else {
            this.$message.error(res.message);
          }
        })
      },
      filter(inputValue, path) {
        return path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
      },
        getDataList(params) {
            this.tableLoading = true;
            getAction(this.url.list, Object.assign({
                pageNo: this.pagination.current,
                pageSize: this.pagination.pageSize
            }, params)).then((res) => {
                if(res.success) {
                    this.dataSource = res.result.records;
                    this.pagination.total = res.result.total;

                } else {
                    this.$message.error(res.message);
                }
            }).finally(() => {
                this.tableLoading = false;
            });
        },

        searchQuery1() {
            if(this.queryParam.yearValue != null && this.queryParam.yearValue != '') {
                this.queryParam.schoolYear = moment(this.queryParam.yearValue).format('YYYY');
            }
            this.dataSource = [];
            this.getDataList(this.queryParam);
        },
        onChange(page, pageSize) {

            this.pagination.current = page;
            this.pagination.pageSize = pageSize;
            if(this.queryParam.yearValue != null && this.queryParam.yearValue != '') {
                this.queryParam.schoolYear = moment(this.queryParam.yearValue).format('YYYY');
            }
            this.getDataList(this.queryParam);
        },
        onShowSizeChange(current, size) {

            this.pagination.current = current;
            this.pagination.pageSize = size;
            if(this.queryParam.yearValue != null && this.queryParam.yearValue != '') {
                this.queryParam.schoolYear = moment(this.queryParam.yearValue).format('YYYY');
            }
            this.getDataList(this.queryParam);
        },
      initDictConfig(){
      },
      getSuperFieldList(){
        let fieldList=[];
        fieldList.push({type:'string',value:'courseVideoCode',text:'课程编码',dictCode:''})
        fieldList.push({type:'string',value:'courseVideoName',text:'课程名称',dictCode:''})
        fieldList.push({type:'string',value:'courseVideoDescription',text:'课程描述',dictCode:''})
        fieldList.push({type:'string',value:'courseVideoImgPath',text:'课程图片',dictCode:''})
        fieldList.push({type:'date',value:'startTime',text:'开课时间'})
        fieldList.push({type:'BigDecimal',value:'classHour',text:'课时',dictCode:''})
        fieldList.push({type:'string',value:'schoolYear'

3. 后端开发:

使用springcloud构建后端API。创建一个简单的视频模型和相应的API视图:

@AutoLog(value = "视频课程-分页列表查询")
@ApiOperation(value="视频课程-分页列表查询", notes="视频课程-分页列表查询")
@GetMapping(value = "/list")
public Result<?> queryPageList(WdCourseVideo wdCourseVideo,
                         @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
                         @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
                         HttpServletRequest req) {
    QueryWrapper<WdCourseVideo> queryWrapper = QueryGenerator.initQueryWrapper(wdCourseVideo, req.getParameterMap());
    queryWrapper.orderByDesc("sort").orderByDesc("create_time").orderByDesc("id");
    Page<WdCourseVideo> page = new Page<WdCourseVideo>(pageNo, pageSize);
    IPage<WdCourseVideo> pageList = wdCourseVideoService.page(page, queryWrapper);
    for (WdCourseVideo record : page.getRecords()) {
       if(record.getCourseType() == 1){
          try{
             record.setClassHour(""+setClassHour(record.getId(), record.getTemplateId(), 1));
             wdCourseVideoService.updateById(record);
          }catch (Exception e){
             System.out.println("计算课时有误");
          }
       }
    }
    for (WdCourseVideo record : pageList.getRecords()) {
       //查询绑定的标签id
       record.setLabelIds(wdCourseLabelRelationService.selectCourseLabelIds(record.getId()));
       record.setLabelNames(wdCourseLabelRelationService.selectCourseLabelNames(record.getId()));
       //查询是否售卖
       record.setIsSale(1);
       WdCourseSale courseSale = wdCourseSaleService.getOne(new LambdaQueryWrapper<WdCourseSale>().eq(WdCourseSale::getCourseId, record.getId()));
       if (courseSale!=null){
          record.setIsSale(courseSale.getIsSale());
       }
    }
    return Result.OK(pageList);
}

4. 启动项目:

启动后端服务:

java -jar /opt/myapp/educatuion.jar

启动前端服务:

cd education
npm install
npm run serve

构建多分校网校平台是未来教育发展的重要方向。通过视频课程技术的应用,可以有效解决传统教学中的诸多问题,实现教育资源的优化配置和高效利用。希望本文的技术详解能为教育机构提供参考,助力打造高效、稳定的多分校网校平台,实现教育事业的进一步发展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值