|
@@ -0,0 +1,458 @@
|
|
|
+<template>
|
|
|
+ <div class="Jugou">
|
|
|
+ <el-loading></el-loading>
|
|
|
+ <el-card>
|
|
|
+ <div style="padding: 0 100px">
|
|
|
+ <div style="position: relative">
|
|
|
+ <video
|
|
|
+ controls
|
|
|
+ autoplay
|
|
|
+ ref="video"
|
|
|
+ @timeupdate="timeupdate"
|
|
|
+ @error="videoerror"
|
|
|
+ @canplay="videoload"
|
|
|
+ :src="videoUrl"
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ v-show="videoerrorStart"
|
|
|
+ class="videoerror"
|
|
|
+ src="../../assets/img/videoerror.png"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div style="position: relative">
|
|
|
+ <div
|
|
|
+ class="chart"
|
|
|
+ ref="chart"
|
|
|
+ style="width: 100%; min-height: 300px"
|
|
|
+ ></div>
|
|
|
+ <div class="lineinfobackground" :style="'left:' + leftNum + 'px'">
|
|
|
+ <div style="position: relative; width: 100%; height: 100%">
|
|
|
+ <div class="line"></div>
|
|
|
+ <el-row style="padding: 10px 0 0 20px">
|
|
|
+ <el-col :span="6">
|
|
|
+ <span class="icon" style="background-color: #e05c5c"></span>
|
|
|
+ 流入
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <span class="icon" style="background-color: #6ab581"></span>
|
|
|
+ 流出
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <span class="iconLine"></span>
|
|
|
+ 收视率%
|
|
|
+ <div style="float: right; padding: 0 5px 0 0">
|
|
|
+ {{ tof(nowItem.audienceRating, 4) }}%
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <div style="padding: 5px 20px">
|
|
|
+ 当前时间:{{
|
|
|
+ nowItem.date ? nowItem.date.split(' ')[1] || '' : ''
|
|
|
+ }}
|
|
|
+ <div style="float: right">
|
|
|
+ 人数: {{ formateNum(nowItem.uv) }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div style="padding: 0 20px">
|
|
|
+ 流入排行
|
|
|
+ <div>
|
|
|
+ <div
|
|
|
+ style="font-size: 12px; line-height: 22px"
|
|
|
+ v-for="(item, i) in left"
|
|
|
+ :key="i"
|
|
|
+ v-text="item"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div style="padding: 0 20px">
|
|
|
+ 流出排行
|
|
|
+ <div>
|
|
|
+ <div
|
|
|
+ style="font-size: 12px; line-height: 22px"
|
|
|
+ v-for="(item, i) in right"
|
|
|
+ :key="i"
|
|
|
+ v-text="item"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+// @ is an alias to /src
|
|
|
+import { defaultAjax } from '@/api/index';
|
|
|
+import config from '@/config/index';
|
|
|
+
|
|
|
+import { markRaw } from 'vue';
|
|
|
+const ElLoading = require('element-plus/lib/el-loading/index');
|
|
|
+import 'element-plus/lib/theme-chalk/el-loading.css';
|
|
|
+
|
|
|
+import * as echarts from 'echarts';
|
|
|
+
|
|
|
+let sec = 0,
|
|
|
+ timeout = undefined,
|
|
|
+ isplay = false,
|
|
|
+ isClick = false,
|
|
|
+ stepNum = 6.8,
|
|
|
+ page = 0;
|
|
|
+export default {
|
|
|
+ name: 'Channel',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ videoerrorStart: false,
|
|
|
+ leftNum: 50,
|
|
|
+ videoUrl: '',
|
|
|
+ myChart: undefined,
|
|
|
+ orgLi: [],
|
|
|
+ nowItem: {},
|
|
|
+ left: [],
|
|
|
+ right: [],
|
|
|
+ load: undefined,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.list = [
|
|
|
+ {
|
|
|
+ channelId: 1,
|
|
|
+ channelName: '陕西卫视',
|
|
|
+ videoUrl: '/video/20230814/1.mp4',
|
|
|
+ dataUrl:
|
|
|
+ 'https://smcic-bigdata.oss-cn-chengdu.aliyuncs.com/live-flow/channel/20230814/channel-1.json',
|
|
|
+ dt: '2023-08-14',
|
|
|
+ channelStatus: 0,
|
|
|
+ isDefault: 1,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ this.getData();
|
|
|
+ },
|
|
|
+ computed: {},
|
|
|
+ methods: {
|
|
|
+ getData() {
|
|
|
+ if (!this.load) this.load = ElLoading.default.service();
|
|
|
+ let item = this.list[0];
|
|
|
+ this.videoUrl = config.base.Intranet + '/minio' + item.videoUrl;
|
|
|
+ defaultAjax({
|
|
|
+ url: item.dataUrl,
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ this.load.close();
|
|
|
+ let org = res || {},
|
|
|
+ li = [];
|
|
|
+ Object.keys(org).map(v => {
|
|
|
+ org[v].date = v;
|
|
|
+ li.push(org[v]);
|
|
|
+ });
|
|
|
+ if (!li.length) return;
|
|
|
+ this.orgLi = li;
|
|
|
+ this.nowItem = li[0];
|
|
|
+ this.left = li[0].lrTop || [];
|
|
|
+ this.right = li[0].lcTop || [];
|
|
|
+ return this.chart();
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.load.close();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ tof(num, t) {
|
|
|
+ return Number(num * 100).toFixed(t || 2);
|
|
|
+ },
|
|
|
+ chart() {
|
|
|
+ if (!this.myChart) {
|
|
|
+ this.myChart = markRaw(echarts.init(this.$refs.chart));
|
|
|
+ this.myChart.resize({
|
|
|
+ height: 300,
|
|
|
+ });
|
|
|
+ this.myChart.on('datazoom', () => {
|
|
|
+ if (isClick) return;
|
|
|
+ isClick = true;
|
|
|
+ let funisplay = !this.$refs.video.paused;
|
|
|
+ if (timeout) clearTimeout(timeout), (timeout = undefined);
|
|
|
+ this.$refs.video.pause();
|
|
|
+ this.leftNum = 50;
|
|
|
+ timeout = setTimeout(() => {
|
|
|
+ clearTimeout(timeout);
|
|
|
+ timeout = undefined;
|
|
|
+ isClick = false;
|
|
|
+ // 拖拽后的事件
|
|
|
+ isplay = funisplay;
|
|
|
+ this.leftNum = 50;
|
|
|
+ this.$refs.video.currentTime =
|
|
|
+ this.myChart.getOption().dataZoom[0].startValue;
|
|
|
+ if (isplay) this.$refs.video.play();
|
|
|
+ else this.$refs.video.pause();
|
|
|
+ }, 200);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ let timeAll = [],
|
|
|
+ Irdata = [],
|
|
|
+ Icdata = [],
|
|
|
+ audienceRating = [];
|
|
|
+ this.orgLi.map(v => {
|
|
|
+ v.date && timeAll.push(v.date.split(' ')[1] || '');
|
|
|
+ Icdata.push(-1 * v.lc);
|
|
|
+ Irdata.push(v.lr);
|
|
|
+ audienceRating.push(v.audienceRating * 100);
|
|
|
+ });
|
|
|
+ this.myChart.setOption({
|
|
|
+ dataZoom: [
|
|
|
+ {
|
|
|
+ maxSpan: 10,
|
|
|
+ minSpan: 5,
|
|
|
+ type: 'slider',
|
|
|
+ brushSelect: false,
|
|
|
+ // zoomLock: false,
|
|
|
+ show: true,
|
|
|
+ realtime: true,
|
|
|
+ start: stepNum * page,
|
|
|
+ end: stepNum * (page + 1),
|
|
|
+ zoomOnMouseWheel: false,
|
|
|
+ height: '30',
|
|
|
+ top: '85%',
|
|
|
+ handleIcon:
|
|
|
+ 'path://M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
|
|
|
+ handleSize: 15,
|
|
|
+ textStyle: {
|
|
|
+ color: '#c5c6cc',
|
|
|
+ },
|
|
|
+ dataBackground: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#fff',
|
|
|
+ },
|
|
|
+ areaStyle: {
|
|
|
+ color: '#8b51a8',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ xAxis: {
|
|
|
+ data: timeAll,
|
|
|
+ position: 'top',
|
|
|
+ silent: false,
|
|
|
+ axisLabel: {
|
|
|
+ interval: 'auto',
|
|
|
+ color: '#000',
|
|
|
+ rotate: '-90',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ scale: false,
|
|
|
+ nameGap: 6,
|
|
|
+ inverse: false,
|
|
|
+ name: '流入流出',
|
|
|
+ nameLocation: 'start',
|
|
|
+ splitArea: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#000',
|
|
|
+ formatter: function (value) {
|
|
|
+ let v = value;
|
|
|
+ if (v < 0) v *= -1;
|
|
|
+ return v;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ nameTextStyle: {
|
|
|
+ color: '#000',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ scale: true,
|
|
|
+ nameGap: 6,
|
|
|
+ type: 'value',
|
|
|
+ name: '收视率%',
|
|
|
+ nameLocation: 'start',
|
|
|
+ splitLine: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#000',
|
|
|
+ },
|
|
|
+ nameTextStyle: {
|
|
|
+ color: '#000',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ grid: {
|
|
|
+ left: 50,
|
|
|
+ right: 50,
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '流入',
|
|
|
+ type: 'bar',
|
|
|
+ yAxisIndex: 0,
|
|
|
+ stack: 'one',
|
|
|
+ data: Irdata,
|
|
|
+ sampling: 'lttb',
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series',
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#df5a5a',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '流出',
|
|
|
+ type: 'bar',
|
|
|
+ yAxisIndex: 0,
|
|
|
+ sampling: 'lttb',
|
|
|
+ stack: 'one',
|
|
|
+ data: Icdata,
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series',
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#6ab581',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '收视率%',
|
|
|
+ type: 'line',
|
|
|
+ yAxisIndex: 1,
|
|
|
+ smooth: true,
|
|
|
+ showSymbol: false,
|
|
|
+ data: audienceRating,
|
|
|
+ lineStyle: {
|
|
|
+ width: 1,
|
|
|
+ color: '#50abfd',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ },
|
|
|
+ timeupdate(v) {
|
|
|
+ if (sec === Math.floor(v.srcElement.currentTime)) return;
|
|
|
+ sec = Math.floor(v.srcElement.currentTime);
|
|
|
+ let width = this.$refs.chart.offsetWidth - 100;
|
|
|
+ let item = this.orgLi[sec] || {};
|
|
|
+ let s = this.myChart.getOption().dataZoom[0].startValue,
|
|
|
+ e = this.myChart.getOption().dataZoom[0].endValue;
|
|
|
+ let iW = width / (e - s),
|
|
|
+ cheng = sec - s < 0 ? 0 : sec - s;
|
|
|
+ let step = iW * cheng > width ? 0 : iW * cheng;
|
|
|
+ if (!item.lrTop) return;
|
|
|
+ this.nowItem = item;
|
|
|
+ this.left = item.lrTop || [];
|
|
|
+ this.right = item.lcTop || [];
|
|
|
+ this.leftNum = 50 + step;
|
|
|
+ if (iW * cheng <= width) return;
|
|
|
+ page++;
|
|
|
+ this.myChart.setOption({
|
|
|
+ dataZoom: {
|
|
|
+ start: stepNum * page,
|
|
|
+ end: stepNum * (page + 1),
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
+ videoerror() {
|
|
|
+ this.videoerrorStart = true;
|
|
|
+ },
|
|
|
+ videoload() {
|
|
|
+ this.videoerrorStart = false;
|
|
|
+ },
|
|
|
+ formateNum(n) {
|
|
|
+ let num = n;
|
|
|
+ if (isNaN(n)) num = '0';
|
|
|
+ else if (n >= 100000000)
|
|
|
+ num =
|
|
|
+ ((n / 100000000).toFixed(2) - 0 + '').replace(
|
|
|
+ /\B(?=(?:\d{3})+\b)/g,
|
|
|
+ ','
|
|
|
+ ) + '亿';
|
|
|
+ else if (n >= 10000)
|
|
|
+ num =
|
|
|
+ ((n / 10000).toFixed(2) - 0 + '').replace(
|
|
|
+ /\B(?=(?:\d{3})+\b)/g,
|
|
|
+ ','
|
|
|
+ ) + '万';
|
|
|
+ else num = (num + '').replace(/\B(?=(?:\d{3})+\b)/g, ',');
|
|
|
+ return num;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ components: {},
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+.Jugou {
|
|
|
+ min-width: 1149px;
|
|
|
+ padding: 10px 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.Jugou video {
|
|
|
+ width: 100%;
|
|
|
+ height: 500px;
|
|
|
+ background-color: #000;
|
|
|
+}
|
|
|
+.Jugou .lineinfobackground {
|
|
|
+ font-size: 12px;
|
|
|
+ border-radius: 9px;
|
|
|
+ position: absolute;
|
|
|
+ width: 345px;
|
|
|
+ height: 200px;
|
|
|
+ top: 30px;
|
|
|
+ left: 50px;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ transform: translateX(-50%);
|
|
|
+ color: #fff;
|
|
|
+ /* opacity: 0.5; */
|
|
|
+}
|
|
|
+.Jugou .lineinfobackground .line {
|
|
|
+ position: absolute;
|
|
|
+ width: 2px;
|
|
|
+ height: 120%;
|
|
|
+ margin-top: -5%;
|
|
|
+ margin-left: 50%;
|
|
|
+ background-color: #888;
|
|
|
+}
|
|
|
+.Jugou .lineinfobackground .icon {
|
|
|
+ display: inline-block;
|
|
|
+ width: 30px;
|
|
|
+ height: 10px;
|
|
|
+ border-radius: 10px;
|
|
|
+}
|
|
|
+.Jugou .lineinfobackground .iconLine {
|
|
|
+ background-color: #50abfd;
|
|
|
+ display: inline-block;
|
|
|
+ width: 30px;
|
|
|
+ height: 2px;
|
|
|
+ vertical-align: middle;
|
|
|
+ border-radius: 10px;
|
|
|
+}
|
|
|
+.Jugou .videoerror {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+}
|
|
|
+video::-webkit-media-controls-timeline {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+</style>
|