yjwang hai 1 semana
pai
achega
4f786ccd5a
Modificáronse 1 ficheiros con 336 adicións e 0 borrados
  1. 336 0
      src/views/course/publiccourse/courseWatchLog/index.vue

+ 336 - 0
src/views/course/publiccourse/courseWatchLog/index.vue

@@ -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>