项目实训:虚拟现实环境下的远程教育和智能评估系统(十二)

前端vue页面的编写

一 、课程收藏列表页面

<template>
  <div>
    <y-header />
    <div class="person_body container account_cont clearfix">
      <y-side :type="'wdsc'" />
      <div class="main_box">
        <ul class="tabs clearfix">
          <a class="tab on">我的收藏</a>
        </ul>
        <div class="main_cont">
          <div v-if="notdata" class="notdata">
            <i class="iconfont">&#xe6be;</i>暂时没有数据
          </div>
          <table v-else class="course_table table">
            <tbody>
              <tr v-for="(item, index) in pageObj.list" :key="index">
                <td v-if="item.courseResp">
                  <img :src="item.courseResp.courseLogo" :alt="item.courseResp.courseName" :height="80">
                  <div>
                    <div class="title">{{ item.courseResp.courseName }}</div>
                    <div v-if="item.courseResp.isFree === 1" style="margin: 0">【免费课】</div>
                    <br><br>
                    <div>购买人数:{{ item.courseResp.countBuy }}</div>
                    <br><br>
                    <div>学习人数:{{ item.courseResp.countStudy }}</div>
                  </div>
                </td>
                <td v-if="item.courseResp" style="float: right;margin-top: 10px">
                  <nuxt-link target="_blank" :to="{name: 'course-id', params: {id: item.courseResp.id}}" class="go_btn">马上学习</nuxt-link>
                </td>
              </tr>
            </tbody>
          </table>
          <d-page v-if="pageObj.totalPage > 1" :page="pageObj" @btnClick="getPage" />
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import YSide from '~/components/account/Side'
import YHeader from '~/components/common/Header'
import DPage from '~/components/common/Page'
import { userCourseCollectPage } from '@/api/user.js'

export default {
  components: {
    YHeader,
    YSide,
    DPage
  },
  data() {
    return {
      notdata: true,
      pageCurrent: 1,
      pageObj: {}
    }
  },
  head() {
    return {
      title: '我的收藏-' + this.$store.state.websiteInfo.websiteName
    }
  },
  mounted() {
    this.getList()
  },
  methods: {
    getPage(int) {
      this.pageCurrent = int
      this.getList()
    },
    getList() {
      userCourseCollectPage({ pageCurrent: this.pageCurrent, pageSize: 20 }).then(res => {
        if (res.totalCount > 0) {
          this.notdata = false
          this.pageObj = res
          console.log('res', res)
        }
      }).catch(err => {
        if (err.code >= 300 && err.code <= 400) {
          this.$store.dispatch('REDIRECT_LOGIN')
        }
      })
    }
  }
}
</script>
<style lang="scss">
@import '~/assets/css/account.scss';
</style>
  1. 组件结构

    • YHeader 组件:显示页面顶部的通用头部。
    • .person_body 包裹容器:整体布局容器,包含侧边栏、主内容区域。
    • YSide 组件:侧边栏组件,根据type属性显示特定类型的侧边栏。
    • .main_box:主内容区域的容器。
    • .tabs:选项卡区域,目前只有一个“我的收藏”选项卡。
    • .main_cont:主内容区域,包含课程列表或暂无数据的提示信息。
    • v-if="notdata":根据notdata的值决定显示暂无数据的提示信息。
    • v-else:如果有数据,则显示课程列表表格。
    • v-for="(item, index) in pageObj.list":遍历pageObj.list中的课程数据,显示每个课程的相关信息和链接。
  2. 表格数据展示

    • v-if="item.courseResp":确保课程数据存在时才显示该列。
    • :src="item.courseResp.courseLogo":显示课程的Logo。
    • {{ item.courseResp.courseName }}:显示课程名称。
    • v-if="item.courseResp.isFree === 1":如果课程是免费的,则显示免费课字样。
    • {{ item.courseResp.countBuy }}:显示购买人数。
    • {{ item.courseResp.countStudy }}:显示学习人数。
    • <nuxt-link>:使用Nuxt.js的链接组件,跳转到课程详情页。
  3. 分页组件

    • d-page 组件:显示分页控件,根据pageObj对象中的总页数来决定是否显示。

JavaScript部分(script)

  1. 导入组件和API

    • 导入了YSideYHeaderDPage组件以及一个名为userCourseCollectPage的API方法,用于获取用户收藏的课程列表数据。
  2. 数据和状态

    • notdata:控制是否显示暂无数据的提示信息。
    • pageCurrent:当前页码。
    • pageObj:包含从API返回的分页数据对象,如当前页数据列表、总页数等。
  3. 生命周期钩子

    • mounted:组件挂载后调用getList()方法获取初始数据。
  4. 方法

    • getPage(int):处理分页点击事件,更新pageCurrent并重新获取列表数据。
    • getList():调用userCourseCollectPage方法获取用户收藏的课程数据,并更新组件的状态(如是否显示暂无数据信息)。

样式部分(style)

  • 使用了SCSS预处理器,并导入了特定的账户样式文件

 二、我的课程页面设计

<template>
  <div>
    <y-header />
    <div class="person_body container account_cont clearfix">
      <y-side :type="'wdkc'" />
      <div class="main_box">
        <ul class="tabs clearfix">
          <a class="tab on">我的课程</a>
        </ul>
        <div class="main_cont">
          <div v-if="notdata" class="notdata">
            <i class="iconfont">&#xe6be;</i>暂时没有数据
          </div>
          <table v-else class="course_table table">
            <tbody>
              <tr v-for="(item, index) in pageObj.list" :key="item.periodName + index">
                <td v-if="item.courseResp">
                  <img :src="item.courseResp.courseLogo" :alt="item.courseResp.courseName" :height="80">
                  <div>
                    <div class="title">{{ item.courseResp.courseName }}</div>
                    <div v-if="item.courseResp.isFree === 1" style="margin: 0">【免费课】</div>
                    <br><br>
                    <div v-if="item.periodName">学习至:{{ item.periodName }}({{ item.periodProgress }}%)| {{ item.periodTime }}</div>
                    <br><br>
                    <div>总进度:{{ item.courseProgress ? item.courseProgress : 0 }}%</div>
                  </div>
                </td>
                <td v-if="item.courseResp" style="float: right; margin-top: 10px">
                  <nuxt-link :to="{ name: 'course-id', params: { id: item.courseResp.id }}" class="go_btn">继续学习</nuxt-link>
                </td>
                <td v-if="item.courseResp" style="float: right; margin-top: 10px">
                  <el-button plain type="primary" @click="studyRecord(item)">进度明细</el-button>
                </td>
              </tr>
            </tbody>
          </table>
          <d-page v-if="pageObj.totalPage > 1" :page="pageObj" @btnClick="getPage" />
        </div>
      </div>
    </div>
    <study :visible="study.visible" :info="study.info" @close="studyCallback" />
  </div>
</template>

<script>
import YSide from '~/components/account/Side'
import YHeader from '~/components/common/Header'
import DPage from '~/components/common/Page'
import { userCoursePage } from '@/api/user.js'
import Study from './study.vue'
import { userStudyePage } from '@/api/course.js'

export default {
  components: {
    Study,
    YHeader,
    YSide,
    DPage
  },
  data() {
    return {
      study: {
        visible: false,
        info: []
      },
      notdata: true,
      pageCurrent: 1,
      pageObj: {
        list: [],
        totalPage: 0
      }
    }
  },
  head() {
    return {
      title: '我的课程-' + this.$store.state.websiteInfo.websiteName
    }
  },
  mounted() {
    this.getStudyList()
  },
  methods: {
    getPage(int) {
      this.pageCurrent = int
      this.getStudyList()
    },
    getStudyList() {
      userCoursePage({ pageCurrent: this.pageCurrent, pageSize: 20 }).then(res => {
        if (res.totalCount > 0) {
          this.notdata = false
          this.pageObj = res
        } else {
          this.notdata = true
        }
      }).catch(err => {
        if (err.code >= 300 && err.code <= 400) {
          this.$store.dispatch('REDIRECT_LOGIN')
        }
      })
    },
    studyRecord(item) {
      userStudyePage({ userId: this.$store.state.userInfo.id, courseId: item.courseResp.id }).then((res) => {
        this.study.info = res.list || []
        this.study.visible = true
        console.log(this.study.info)
      })
    },
    studyCallback() {
      this.study.visible = false
    }
  }
}
</script>

<style lang="scss">
@import '~/assets/css/account.scss'
</style>

实现了一个简单的“我的课程”页面,包括课程列表的展示、分页、以及学习记录的查看

三、个人信息查看及编辑页面

<template>
  <div class="">
    <y-header />
    <div class="container account_cont clearfix">
      <y-side :type="'grxx'" />
      <div class="main_box">
        <ul class="tabs clearfix">
          <a class="tab on">基础信息</a>
        </ul>
        <div v-if="editStatus" class="main_cont form">
          <form action="" @submit="userInfoUpdate">
            <div class="form_group">
              <div class="label">手机号:</div>
              <div class="form_ctl">
                <div class="text">{{ obj.mobile }}</div>
              </div>
            </div>
            <div class="form_group">
              <div class="label">昵称:</div>
              <div class="form_ctl">
                <input v-model="obj.nickname" type="text" class="form_input" placeholder="请输入昵称">
              </div>
            </div>
            <div class="form_group">
              <div class="label">年龄:</div>
              <div class="form_ctl">
                <input v-model="obj.userAge" type="number" class="form_input" placeholder="请输入年龄">
              </div>
            </div>
            <div class="form_group">
              <div class="label">性别:</div>
              <div class="form_ctl form_ctl_radio">
                <input id="sex1" v-model="obj.userSex" type="radio" class="radiobox" value="1" name="sex">
                <label for="sex1">男</label>
                <input id="sex2" v-model="obj.userSex" type="radio" class="radiobox" value="2" name="sex">
                <label for="sex2">女</label>
                <input id="sex3" v-model="obj.userSex" type="radio" class="radiobox" value="3" name="sex">
                <label for="sex3">保密</label>
              </div>
            </div>
            <div class="form_group">
              <div class="label">头像:</div>
              <div class="form_ctl upload_ctl" style="float: none;">
                <input v-model="obj.userHead" type="hidden">
                <div class="preview" style="display: flex;align-items: center; width: 300px;">
                  <img v-if="obj.userHead" :src="obj.userHead" alt="" style="height: 100px; margin-left: 10px">
                  <i v-else class="iconfont">&#xe6b2;</i>
                  <d-upload style="margin-left: 50px" :btntxt="'选择头像'" @rtnUrl="setUrl" />
                </div>
              </div>
              <p class="tip" style="padding-left:110px;">* 图片尺寸为800x800,图片大小&lt;500KB,建议使用真人照片,便于品牌宣传效果</p>
            </div>
            <div class="form_group">
              <div class="label">学号:</div>
              <div class="form_ctl">
                <input v-model="obj.studentNumber" type="text" class="form_input" placeholder="请输入学号">
              </div>
            </div>
            <div class="form_group">
              <div class="label">学校:</div>
              <div class="form_ctl">
                <input v-model="obj.school" type="text" class="form_input" placeholder="请输入学校">
              </div>
            </div>

            <div class="form_group">
              <div class="label">&nbsp;</div>
              <div class="form_ctl">
                <button type="submit" class="submit_btn">保存</button>
              </div>
            </div>
          </form>
        </div>
        <div v-else class="main_cont form">
          <div class="form_group">
            <div class="label">手机号:</div>
            <div class="form_ctl">
              <div class="text">{{ obj.mobile }}</div>
            </div>
          </div>
          <div class="form_group">
            <div class="label">昵称:</div>
            <div class="form_ctl">
              <div class="text">{{ obj.nickname }}</div>
            </div>
          </div>
          <div class="form_group">
            <div class="label">年龄:</div>
            <div class="form_ctl">
              <div class="text">{{ obj.userAge }}</div>
            </div>
          </div>
          <div class="form_group">
            <div class="label">性别:</div>
            <div class="form_ctl">
              <div v-if="obj.userSex === 1" class="text">男</div>
              <div v-else-if="obj.userSex === 2" class="text">女</div>
              <div v-else-if="obj.userSex === 3" class="text">保密</div>
            </div>
          </div>
          <div class="form_group">
            <div class="label">头像:</div>
            <div class="form_ctl upload_ctl">
              <div class="preview">
                <img v-if="obj.userHead" :src="obj.userHead" alt="">
                <i v-else class="iconfont">&#xe6b2;</i>
              </div>
            </div>
          </div>
          <div class="form_group">
            <div class="label">学号</div>
            <div class="form_ctl">
              <div class="text">{{ obj.studentNumber }}</div>
            </div>
          </div>
          <div class="form_group">
            <div class="label">学校:</div>
            <div class="form_ctl">
              <div class="text">{{ obj.school }}</div>
            </div>
          </div>
          <div class="form_group">
            <div class="label">&nbsp;</div>
            <div class="form_ctl">
              <button href="javascript:" class="submit_btn" @click="editInfo">修改</button>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- <bottom /> -->
  </div>
</template>
<script>
import YSide from '~/components/account/Side'
import YHeader from '~/components/common/Header'
import DUpload from '~/components/common/Upload'
import { getUserInfo, usersUpdata } from '@/api/user.js'

export default {
  components: {
    YHeader,
    YSide,
    DUpload
  },
  data() {
    return {
      editStatus: false,
      region1: [],
      region2: [],
      obj: {}
    }
  },
  head() {
    return {
      title: '个人信息-' + this.$store.state.websiteInfo.websiteName
    }
  },
  mounted() {
    this.userInfo()
  },
  methods: {
    userInfo() {
      getUserInfo().then(res => {
        this.obj = res || {}
      }).catch(err => {
        if (err.code >= 300 && err.code <= 400) {
          this.$store.dispatch('REDIRECT_LOGIN')
        }
      })
    },
    editInfo() {
      this.editStatus = true
    },
    userInfoUpdate(e) {
      e.preventDefault()
      usersUpdata(this.obj).then(res => {
        this.$msgBox({
          content: '信息修改成功',
          isShowCancelBtn: false
        }).then(async(val) => {
          window.location.reload()
        }).catch(() => {
          window.location.reload()
        })
      })
    },
    setUrl(res) {
      this.obj.userHead = res.url
    }
  }
}
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
@import '~/assets/css/account.scss';

.upload_ctl {
  .preview {
    width: 100px;
    height: 100px;
    text-align: center;
    margin-bottom: 10px;

    i {
      line-height: 120px;
      font-size: 100px;
      color: #ddd;
    }
  }
}
</style>

四、课程订单查看界面

  <div>
    <y-header />
    <div class="container account_cont clearfix">
      <y-side :type="'wddd'" />
      <div class="main_box">
        <ul class="tabs clearfix">
          <a class="tab" :class="{on: num == 0}" @click="clicktab(0)">所有订单</a>
          <a class="tab" :class="{on: num == 1}" @click="clicktab(1)">待支付订单</a>
          <a class="tab" :class="{on: num == 2}" @click="clicktab(2)">已完成订单</a>
        </ul>
        <div v-if="notdata" class="notdata">
          <i class="iconfont">&#xe6be;</i>暂时没有数据
        </div>
        <div v-if="!notdata" class="person_info">
          <div v-for="(item, index) in pageObj.list" :key="index" class="order_content">
            <div class="order_title clearfix">
              <span class="order_num">订单号:{{ item.orderNo }}</span>
              <span class="time">{{ item.gmtCreate }}</span>
            </div>
            <div class="order_body clearfix">
              <div class="body_left clearfix">
                <nuxt-link target="_blank" :to="{name: 'course-id', params: {id: item.courseId}}">
                  <div v-if="item.courseLogo" class="img_box fl">
                    <img :src="item.courseLogo" :alt="item.courseName">
                  </div>
                  <p class="fl">
                    {{ item.courseName }}<br><br>
                    原价:<span style="text-decoration-line: line-through; margin-right: 20px">¥{{ item.rulingPrice }}</span>实付:¥{{ item.coursePrice }}
                  </p>
                </nuxt-link>
              </div>
              <ul class="body_right clearfix">
                <li>
                  <br>
                  <span v-if="item.orderStatus == 1">待支付</span>
                  <span v-if="item.orderStatus == 2"><nuxt-link target="_blank" :to="{name: 'course-id', params: {id: item.courseId}}" class="go_btn">马上学习</nuxt-link></span>
                  <span v-if="item.orderStatus == 3">支付失败</span>
                  <span v-if="item.orderStatus == 4">已关闭</span>
                </li>
                <li v-if="item.orderStatus == 1 || item.orderStatus == 3">
                  <a href="javascript:" class="go_btn go_pay" @click="continuePay(item)">继续支付</a>
                  <a href="javascript:" class="cancel" @click="closeOrder(item.orderNo)">关闭订单</a>
                </li>
              </ul>
            </div>
          </div>
          <d-page v-if="pageObj.totalPage > 1 && !notdata" :page="pageObj" @btnClick="getPage" />
        </div>
      </div>
    </div>
    <d-paymodal v-if="showPay" class="" :data="payData" @hidefun="showPay = false" />
    <!-- <bottom /> -->
  </div>
</template>
<script>
import YSide from '~/components/account/Side'
import YHeader from '~/components/common/Header'
import DPaymodal from '@/components/common/Order'
import { cancelOrder, orderPage } from '@/api/user.js'
import DPage from '~/components/common/Page'

export default {
  components: {
    YHeader,
    YSide,
    DPage,
    DPaymodal
  },
  data() {
    return {
      num: 0,
      showPay: false,
      payData: null,
      notdata: true,
      obj: {
        orderStatus: 0,
        pageCurrent: 1,
        pageSize: 20
      },
      pageObj: {
        pageCurrent: '',
        pageSize: '',
        totalCount: '',
        totalPage: ''
      }
    }
  },
  head() {
    return {
      title: '我的订单-' + this.$store.state.websiteInfo.websiteName
    }
  },
  mounted() {
    this.obj = {
      orderStatus: '',
      pageCurrent: 1,
      pageSize: 10
    }
    this.getOrderList()
  },
  methods: {
    continuePay(item) {
      this.payData = item
      this.showPay = true
    },
    clicktab(int) {
      this.num = int
      if (int === 0) {
        this.obj.orderStatus = ''
      } else {
        this.obj.orderStatus = int
      }
      this.obj.pageCurrent = 1
      this.getOrderList()
    },
    getPage: function(int) {
      this.obj.pageCurrent = int
      this.getOrderList()
    },
    getOrderList() {
      orderPage(this.obj).then(res => {
        if (res.totalCount > 0) {
          this.notdata = false
          this.pageObj = res
        }
      }).catch(err => {
        if (err.code >= 300 && err.code <= 400) {
          this.$store.dispatch('REDIRECT_LOGIN')
        }
      })
    },
    closeOrder(orderNo) {
      cancelOrder({ orderNo: orderNo }).then(res => {
        this.$msgBox({
          content: '关闭成功',
          isShowCancelBtn: false,
          edit: false
        }).then(async(val) => {
          this.getOrderList()
        }).catch(() => {
          this.getOrderList()
        })
      })
    }
  }
}
</script>

五、学习记录信息。

<template>
  <el-dialog :visible="visible" :append-to-body="true" :title="title" width="900px" @close="closeDialog">
    <el-table :data="info" row-key="id" :tree-props="{ children: 'userStudyPeriodPageRespList' }" default-expand-all>
      <el-table-column label="章节名称" prop="chapterName" width="150">
        <template #default="scope">
          <span>{{ scope.row.chapterName }}</span>
          <span>{{ scope.row.periodName }}</span>
        </template>
      </el-table-column>
      <el-table-column label="学习时间" prop="gmtCreate" width="200">
        <template #default="scope">
          <span v-if="scope.row.progress > 0">{{ scope.row.gmtCreate }}</span>
        </template>
      </el-table-column>
      <el-table-column label="学习进度" prop="courseProgress" width="200">
        <template #default="scope">
          <el-progress v-if="scope.row.progress" :percentage="scope.row.progress" :stroke-width="25" :text-inside="true" />
        </template>
      </el-table-column>
      <!-- <el-table-column label="眼动数据上传" width="150">
        <template #default="scope">
        <uploader-btn v-if="scope.row.progress" :disabled="scope.row.eyeData" icon="" :plain="false" btn-text="上传" :userStudyId="scope.row.progressId" mode="async" class="mgl10"/>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="200">
        <template #default="scope">
          <el-button v-if="scope.row.progress" :disabled="!scope.row.report" @click="viewReport(scope.row.report)" type="primary">查看报告</el-button>
        </template>
      </el-table-column> -->
      <el-table-column label="状态" width="140">
        <template #default="scope">
          <div v-if="scope.row.periodName">
            <span v-if="!scope.row.progress">未观看视频</span>
            <span v-if="scope.row.progress&&scope.row.report">报告已生成</span>
            <span v-if="scope.row.progress&&!scope.row.report&&scope.row.eyeData">报告已上传</span>
            <span v-if="scope.row.progress&&!scope.row.report&&!scope.row.eyeData">未上传眼动数据</span>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="更多操作" width="170">
        <template #default="scope">
          <el-dropdown v-if="scope.row.progress">
            <el-button>更多操作<i class="el-icon-arrow-down" /></el-button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item>
                <uploader-btn v-if="scope.row.progress" :disabled="scope.row.eyeData" icon="" :plain="false" btn-text="上传眼动数据" :userStudyId="scope.row.progressId" mode="async" class="mgl10"/>
              </el-dropdown-item>
              <el-dropdown-item>
                <el-button v-if="scope.row.progress" :disabled="!scope.row.report" @click="viewReport(scope.row.report)" type="primary" size="small">查看报告</el-button>
              </el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>
</template>

<script>
import { defineComponent, ref, watch } from 'vue'
import UploaderBtn from '@/components/upload/UploaderBtn.vue'

export default defineComponent({
  components: {
    UploaderBtn
  },
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    title: {
      type: String,
      default: '学习记录'
    },
    info: {
      type: Array,
      default: () => []
    }
  },
  emits: ['close'],
  setup(props, { emit }) {
    const dialogVisible = ref(props.visible)

    watch(() => props.visible, (newValue) => {
      dialogVisible.value = newValue
    })

    const closeDialog = () => {
      emit('close')
    }

    const viewReport = (link) => {
      if (link) {
        window.open(link, '_blank')
      } else {
        console.warn('报告不存在')
      }
    }

    return {
      dialogVisible,
      closeDialog,
      viewReport
    }
  }
})
</script>

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值