|
|
@@ -0,0 +1,336 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
|
|
|
+ <el-form-item label="课程/章节" prop="keywords">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.keywords"
|
|
|
+ placeholder="请输入课程名称/章节名称"
|
|
|
+ clearable
|
|
|
+ size="small"
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="课程分类" prop="cateId">
|
|
|
+ <el-select
|
|
|
+ v-model="queryParams.cateId"
|
|
|
+ placeholder="请选择课程分类"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ size="small"
|
|
|
+ @change="onRootCateChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in rootCategoryOptions"
|
|
|
+ :key="item.cateId"
|
|
|
+ :label="item.cateName"
|
|
|
+ :value="item.cateId"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="子分类" prop="subCateId">
|
|
|
+ <el-select
|
|
|
+ v-model="queryParams.subCateId"
|
|
|
+ placeholder="请选择子分类"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ size="small"
|
|
|
+ :disabled="!queryParams.cateId"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in subCategoryOptions"
|
|
|
+ :key="item.cateId"
|
|
|
+ :label="item.cateName"
|
|
|
+ :value="item.cateId"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="筛选时间" prop="dateRange">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="dateRange"
|
|
|
+ type="daterange"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="yyyy-MM-dd"
|
|
|
+ size="small"
|
|
|
+ clearable
|
|
|
+ @change="onDateRangeChange"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
|
|
+ <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-tabs v-model="activeTab" @tab-click="onTabClick">
|
|
|
+ <el-tab-pane label="课程数据" name="course">
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
+ <el-col :span="1.5">
|
|
|
+ <el-button
|
|
|
+ type="warning"
|
|
|
+ plain
|
|
|
+ icon="el-icon-download"
|
|
|
+ size="mini"
|
|
|
+ :loading="exportLoading"
|
|
|
+ @click="handleExport"
|
|
|
+ v-hasPermi="['course:publicCourseWatchStat:export']"
|
|
|
+ >导出</el-button>
|
|
|
+ </el-col>
|
|
|
+ <right-toolbar :showSearch.sync="showSearch" @queryTable="getCourseList" />
|
|
|
+ </el-row>
|
|
|
+ <el-table v-loading="courseLoading" border :data="courseList">
|
|
|
+ <el-table-column label="时间" align="center" prop="statDate" width="110">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ formatDay(scope.row.statDate) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="课程ID" align="center" prop="courseId" width="90" />
|
|
|
+ <el-table-column label="课程名称" align="center" prop="courseName" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column label="课程分类" align="center" prop="rootCateName" min-width="100" show-overflow-tooltip />
|
|
|
+ <el-table-column label="子分类" align="center" prop="subCateName" min-width="90" show-overflow-tooltip />
|
|
|
+ <el-table-column label="曝光位置" align="center" prop="exposurePositionLabel" width="130" />
|
|
|
+ <el-table-column label="曝光次数" align="center" prop="exposurePv" width="90" />
|
|
|
+ <el-table-column label="曝光人数" align="center" prop="exposureUv" width="90" />
|
|
|
+ <el-table-column label="点击次数" align="center" prop="clickPv" width="90" />
|
|
|
+ <el-table-column label="点击人数" align="center" prop="clickUv" width="90" />
|
|
|
+ <el-table-column label="点击率" align="center" width="90">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ scope.row.clickRate != null ? scope.row.clickRate + '%' : '0%' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="课程观看人数" align="center" prop="watchUv" width="110" />
|
|
|
+ <el-table-column label="课程完课人数" align="center" prop="finishUv" width="110" />
|
|
|
+ </el-table>
|
|
|
+ <pagination
|
|
|
+ v-show="courseTotal > 0"
|
|
|
+ :total="courseTotal"
|
|
|
+ :page.sync="queryParams.pageNum"
|
|
|
+ :limit.sync="queryParams.pageSize"
|
|
|
+ @pagination="getCourseList"
|
|
|
+ />
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <el-tab-pane label="目录数据" name="catalog">
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
+ <el-col :span="1.5">
|
|
|
+ <el-button
|
|
|
+ type="warning"
|
|
|
+ plain
|
|
|
+ icon="el-icon-download"
|
|
|
+ size="mini"
|
|
|
+ :loading="exportLoading"
|
|
|
+ @click="handleExport"
|
|
|
+ v-hasPermi="['course:publicCourseWatchStat:export']"
|
|
|
+ >导出</el-button>
|
|
|
+ </el-col>
|
|
|
+ <right-toolbar :showSearch.sync="showSearch" @queryTable="getCatalogList" />
|
|
|
+ </el-row>
|
|
|
+ <el-table v-loading="catalogLoading" border :data="catalogList">
|
|
|
+ <el-table-column label="目录ID" align="center" prop="videoId" width="90" />
|
|
|
+ <el-table-column label="目录名称" align="center" prop="catalogName" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column label="课程名称" align="center" prop="courseName" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column label="课程分类" align="center" prop="catePath" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column label="浏览量(PV)" align="center" prop="pv" width="100" />
|
|
|
+ <el-table-column label="观看人数(UV)" align="center" prop="uv" width="110" />
|
|
|
+ <el-table-column label="完课人数" align="center" prop="finishUv" width="90" />
|
|
|
+ <el-table-column label="评论数" align="center" prop="commentCount" width="80" />
|
|
|
+ <el-table-column label="完课率" align="center" width="90">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ scope.row.finishRate != null ? scope.row.finishRate + '%' : '0%' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="平均学习时长" align="center" prop="avgWatchDuration" width="120" />
|
|
|
+ <el-table-column label="看课奖励类型" align="center" prop="rewardType" min-width="100" show-overflow-tooltip />
|
|
|
+ <el-table-column label="答题人数" align="center" prop="answerUv" width="90" />
|
|
|
+ <el-table-column label="领取积分人数" align="center" prop="integralReceiveUv" width="110" />
|
|
|
+ <el-table-column label="分享私聊数" align="center" prop="sharePrivateCount" width="100" />
|
|
|
+ <el-table-column label="分享朋友圈数" align="center" prop="shareTimelineCount" width="110" />
|
|
|
+ <el-table-column label="操作" align="center" width="100" fixed="right">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button type="text" size="mini" @click="handleUserData(scope.row)">用户数据</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <pagination
|
|
|
+ v-show="catalogTotal > 0"
|
|
|
+ :total="catalogTotal"
|
|
|
+ :page.sync="queryParams.pageNum"
|
|
|
+ :limit.sync="queryParams.pageSize"
|
|
|
+ @pagination="getCatalogList"
|
|
|
+ />
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {
|
|
|
+ listPublicCourseWatchStatCourseDay,
|
|
|
+ listPublicCourseWatchStatCatalog,
|
|
|
+ exportPublicCourseWatchStatCourseDay,
|
|
|
+ exportPublicCourseWatchStatCatalog
|
|
|
+} from '@/api/course/publicCourseWatchStat'
|
|
|
+import { listUserCourseCategory } from '@/api/course/userCourseCategory'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'PublicCourseWatchLog',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ showSearch: true,
|
|
|
+ activeTab: 'course',
|
|
|
+ exportLoading: false,
|
|
|
+ dateRange: [],
|
|
|
+ categoryFlat: [],
|
|
|
+ queryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ keywords: null,
|
|
|
+ cateId: null,
|
|
|
+ subCateId: null,
|
|
|
+ beginDate: null,
|
|
|
+ endDate: null
|
|
|
+ },
|
|
|
+ courseLoading: false,
|
|
|
+ courseList: [],
|
|
|
+ courseTotal: 0,
|
|
|
+ catalogLoading: false,
|
|
|
+ catalogList: [],
|
|
|
+ catalogTotal: 0
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ rootCategoryOptions() {
|
|
|
+ return this.categoryFlat.filter(c => c.pid === 0)
|
|
|
+ },
|
|
|
+ subCategoryOptions() {
|
|
|
+ if (!this.queryParams.cateId) {
|
|
|
+ return []
|
|
|
+ }
|
|
|
+ return this.categoryFlat.filter(c => c.pid === this.queryParams.cateId)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.initDateRange()
|
|
|
+ this.loadCategories()
|
|
|
+ this.getCourseList()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ initDateRange() {
|
|
|
+ const end = new Date()
|
|
|
+ const start = new Date()
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
|
|
|
+ const fmt = d => this.parseTime(d, '{y}-{m}-{d}')
|
|
|
+ this.dateRange = [fmt(start), fmt(end)]
|
|
|
+ this.queryParams.beginDate = this.dateRange[0]
|
|
|
+ this.queryParams.endDate = this.dateRange[1]
|
|
|
+ },
|
|
|
+ onDateRangeChange(val) {
|
|
|
+ if (val && val.length === 2) {
|
|
|
+ this.queryParams.beginDate = val[0]
|
|
|
+ this.queryParams.endDate = val[1]
|
|
|
+ } else {
|
|
|
+ this.queryParams.beginDate = null
|
|
|
+ this.queryParams.endDate = null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ loadCategories() {
|
|
|
+ listUserCourseCategory({ cateType: 1 }).then(res => {
|
|
|
+ this.categoryFlat = res.data || []
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onRootCateChange() {
|
|
|
+ this.queryParams.subCateId = null
|
|
|
+ },
|
|
|
+ /** tab-click 触发时 v-model 可能尚未更新,必须用 tab.name */
|
|
|
+ onTabClick(tab) {
|
|
|
+ const name = tab.name
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
+ if (name === 'course') {
|
|
|
+ this.getCourseList()
|
|
|
+ } else {
|
|
|
+ this.getCatalogList()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ formatDay(val) {
|
|
|
+ if (!val) {
|
|
|
+ return ''
|
|
|
+ }
|
|
|
+ if (typeof val === 'string') {
|
|
|
+ return val.length >= 10 ? val.substring(0, 10) : val
|
|
|
+ }
|
|
|
+ return this.parseTime(val, '{y}-{m}-{d}')
|
|
|
+ },
|
|
|
+ buildQuery() {
|
|
|
+ return { ...this.queryParams }
|
|
|
+ },
|
|
|
+ buildExportQuery() {
|
|
|
+ const q = { ...this.queryParams }
|
|
|
+ delete q.pageNum
|
|
|
+ delete q.pageSize
|
|
|
+ return q
|
|
|
+ },
|
|
|
+ handleQuery() {
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
+ if (this.activeTab === 'course') {
|
|
|
+ this.getCourseList()
|
|
|
+ } else {
|
|
|
+ this.getCatalogList()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ resetQuery() {
|
|
|
+ this.resetForm('queryForm')
|
|
|
+ this.queryParams.cateId = null
|
|
|
+ this.queryParams.subCateId = null
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
+ this.queryParams.pageSize = 10
|
|
|
+ this.initDateRange()
|
|
|
+ this.handleQuery()
|
|
|
+ },
|
|
|
+ getCourseList() {
|
|
|
+ this.courseLoading = true
|
|
|
+ listPublicCourseWatchStatCourseDay(this.buildQuery()).then(res => {
|
|
|
+ this.courseList = res.rows
|
|
|
+ this.courseTotal = res.total
|
|
|
+ this.courseLoading = false
|
|
|
+ }).catch(() => {
|
|
|
+ this.courseLoading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ getCatalogList() {
|
|
|
+ this.catalogLoading = true
|
|
|
+ listPublicCourseWatchStatCatalog(this.buildQuery()).then(res => {
|
|
|
+ this.catalogList = res.rows
|
|
|
+ this.catalogTotal = res.total
|
|
|
+ this.catalogLoading = false
|
|
|
+ }).catch(() => {
|
|
|
+ this.catalogLoading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ handleExport() {
|
|
|
+ const tabLabel = this.activeTab === 'course' ? '课程数据' : '目录数据'
|
|
|
+ this.$confirm(`是否确认导出当前筛选条件下的「${tabLabel}」?`, '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ this.exportLoading = true
|
|
|
+ const q = this.buildExportQuery()
|
|
|
+ const req = this.activeTab === 'course'
|
|
|
+ ? exportPublicCourseWatchStatCourseDay(q)
|
|
|
+ : exportPublicCourseWatchStatCatalog(q)
|
|
|
+ return req
|
|
|
+ }).then(res => {
|
|
|
+ if (res && res.msg) {
|
|
|
+ this.download(res.msg)
|
|
|
+ }
|
|
|
+ this.exportLoading = false
|
|
|
+ }).catch(() => {
|
|
|
+ this.exportLoading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ handleUserData() {
|
|
|
+ this.$modal.msg('用户数据功能开发中')
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|