|
@@ -0,0 +1,481 @@
|
|
|
+<template>
|
|
|
+ <div class="tv-list">
|
|
|
+ <div class="content">
|
|
|
+ <div class="itemHead">
|
|
|
+ <van-row>
|
|
|
+ <van-col span="3" class="td"> 排名 </van-col>
|
|
|
+ <van-col span="5" class="td"> 频道 </van-col>
|
|
|
+ <van-col span="5" class="td"> 市场份额 </van-col>
|
|
|
+ <van-col
|
|
|
+ span="7"
|
|
|
+ class="td td2"
|
|
|
+ :style="width > 667 ? 'line-height: 2.5em' : 'line-height: 1.5em'"
|
|
|
+ >
|
|
|
+ 人均使用时长(分钟)
|
|
|
+ </van-col>
|
|
|
+ <van-col span="4" class="td">
|
|
|
+ 点击<img
|
|
|
+ src="../../../assets/image/shou.gif"
|
|
|
+ width="30px"
|
|
|
+ alt=""
|
|
|
+ style="vertical-align: middle"
|
|
|
+ />
|
|
|
+ </van-col>
|
|
|
+ </van-row>
|
|
|
+ </div>
|
|
|
+ <div v-for="(v, o) in platfromData || []" :key="v.name">
|
|
|
+ <div
|
|
|
+ style="border-bottom: 1px dashed #eee; margin: 11px 0 0 0"
|
|
|
+ v-if="o === 20"
|
|
|
+ ></div>
|
|
|
+ <van-row class="item">
|
|
|
+ <van-col
|
|
|
+ span="3"
|
|
|
+ :class="{ td: true }"
|
|
|
+ :style="{ color: color[o] || '#000', fontWeight: 600 }"
|
|
|
+ >
|
|
|
+ {{ v.rank }}
|
|
|
+ </van-col>
|
|
|
+ <van-col span="5" :class="{ td: true }">
|
|
|
+ {{ v.channelname }}
|
|
|
+ </van-col>
|
|
|
+ <van-col span="5" :class="{ td: true }" style="color: #e1a74f">
|
|
|
+ <van-icon
|
|
|
+ v-if="v.occrate_change !== '相等'"
|
|
|
+ name="down"
|
|
|
+ :color="v.occrate_change === '上升' ? '#f00' : '#0f0'"
|
|
|
+ :class="{ rise: v.occrate_change === '上升' }"
|
|
|
+ />
|
|
|
+ {{ formatNum(v.occrate, 2) }}%
|
|
|
+ </van-col>
|
|
|
+ <van-col span="7" :class="{ td: true }" style="color: #e1a74f">
|
|
|
+ <van-icon
|
|
|
+ v-if="v.user_duration_change !== '相等'"
|
|
|
+ name="down"
|
|
|
+ :color="v.user_duration_change === '上升' ? '#f00' : '#0f0'"
|
|
|
+ :class="{ rise: v.user_duration_change === '上升' }"
|
|
|
+ />
|
|
|
+ {{ timeFormat(v.user_duration) }}
|
|
|
+ </van-col>
|
|
|
+ <van-col
|
|
|
+ span="4"
|
|
|
+ class="td"
|
|
|
+ style="cursor: pointer"
|
|
|
+ @click="() => showPopup(o)"
|
|
|
+ >
|
|
|
+ 更多
|
|
|
+ <van-icon name="arrow" />
|
|
|
+ </van-col>
|
|
|
+ </van-row>
|
|
|
+ </div>
|
|
|
+ <van-popup
|
|
|
+ :style="{ height: '100%', width: '100%' }"
|
|
|
+ position="right"
|
|
|
+ v-model="show"
|
|
|
+ >
|
|
|
+ <div class="main">
|
|
|
+ <van-cell
|
|
|
+ title-style="flex: 3"
|
|
|
+ :title="ratios.schedulename"
|
|
|
+ :label="ratios.channelname"
|
|
|
+ >
|
|
|
+ <div @click="show = false" style="color: #5470c6; cursor: pointer">
|
|
|
+ 返回
|
|
|
+ <van-icon name="share-o" />
|
|
|
+ </div>
|
|
|
+ </van-cell>
|
|
|
+ <van-row>
|
|
|
+ <van-col span="5" class="td"> 收视率: </van-col>
|
|
|
+ <van-col span="7" class="td">
|
|
|
+ {{ formatNum(ratios.watchrate, 2) }}%
|
|
|
+ </van-col>
|
|
|
+ <van-col span="7" class="td"> 市占率: </van-col>
|
|
|
+ <van-col span="5" class="td"
|
|
|
+ >{{ formatNum(ratios.occrate, 2) }}%
|
|
|
+ </van-col>
|
|
|
+
|
|
|
+ <van-col span="5" class="td"> 到达率: </van-col>
|
|
|
+ <van-col span="7" class="td"
|
|
|
+ >{{ formatNum(ratios.reachrate, 2) }}%
|
|
|
+ </van-col>
|
|
|
+ <van-col span="7" class="td"> 收视次数: </van-col>
|
|
|
+ <van-col span="5" class="td">
|
|
|
+ <counto
|
|
|
+ :startVal="0"
|
|
|
+ :endVal="ratios.hitcount || 0"
|
|
|
+ :duration="1000"
|
|
|
+ ></counto>
|
|
|
+ <span v-if="ratios.hitcountP">.</span>
|
|
|
+ <counto
|
|
|
+ :startVal="0"
|
|
|
+ :endVal="ratios.hitcountP || 0"
|
|
|
+ :duration="1000"
|
|
|
+ ></counto
|
|
|
+ >{{ ratios.hitcountWei }}
|
|
|
+ </van-col>
|
|
|
+
|
|
|
+ <van-col span="5" class="td"> 忠诚度: </van-col>
|
|
|
+ <van-col span="7" class="td"
|
|
|
+ >{{ formatNum(ratios.loyalty, 2) }}%
|
|
|
+ </van-col>
|
|
|
+ <van-col span="7" class="td"> 用户数: </van-col>
|
|
|
+ <van-col span="5" class="td">
|
|
|
+ <counto
|
|
|
+ :startVal="0"
|
|
|
+ :endVal="ratios.usrcount || 0"
|
|
|
+ :duration="1000"
|
|
|
+ ></counto>
|
|
|
+ <span v-if="ratios.usrcountP">.</span>
|
|
|
+ <counto
|
|
|
+ :startVal="0"
|
|
|
+ :endVal="ratios.usrcountP || 0"
|
|
|
+ :duration="1000"
|
|
|
+ ></counto
|
|
|
+ >{{ ratios.usrcountWei }}
|
|
|
+ </van-col>
|
|
|
+
|
|
|
+ <van-col span="5" class="td"> 收视时长: </van-col>
|
|
|
+ <van-col span="7" class="td">
|
|
|
+ <counto
|
|
|
+ :startVal="0"
|
|
|
+ :endVal="ratios.timecount || 0"
|
|
|
+ :duration="1000"
|
|
|
+ ></counto>
|
|
|
+ <span v-if="ratios.timecountP">.</span>
|
|
|
+ <counto
|
|
|
+ :startVal="0"
|
|
|
+ :endVal="ratios.timecountP || 0"
|
|
|
+ :duration="1000"
|
|
|
+ ></counto
|
|
|
+ >{{ ratios.timecountWei }}
|
|
|
+ </van-col>
|
|
|
+ <van-col span="7" class="td"> 人均收视时长: </van-col>
|
|
|
+ <van-col span="5" class="td"
|
|
|
+ >{{ timeFormat(ratios.user_duration) }}分钟
|
|
|
+ </van-col>
|
|
|
+ </van-row>
|
|
|
+ <div ref="pie"></div>
|
|
|
+ <div ref="day"></div>
|
|
|
+ </div>
|
|
|
+ </van-popup>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+// @ is an alias to /src
|
|
|
+import counto from "@/components/counto/vue-countTo.vue";
|
|
|
+import {
|
|
|
+ Col as vanCol,
|
|
|
+ Row as vanRow,
|
|
|
+ Icon as vanIcon,
|
|
|
+ Popup as vanPopup,
|
|
|
+ Cell as vanCell,
|
|
|
+} from "vant";
|
|
|
+import "vant/lib/cell/style/index";
|
|
|
+import "vant/lib/popup/style/index";
|
|
|
+import "vant/lib/icon/style/index";
|
|
|
+import "vant/lib/col/style/index";
|
|
|
+import "vant/lib/row/style/index";
|
|
|
+// import {} from "../utils/tool";
|
|
|
+import Dayjs from "dayjs";
|
|
|
+import { jsonZhou } from "../../../api/index";
|
|
|
+
|
|
|
+import echarts from "../../../utils/echarts";
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: "tv-list",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ show: false,
|
|
|
+ charts: undefined,
|
|
|
+ ratios: {},
|
|
|
+ platfromData: [],
|
|
|
+ color: ["#ff0036", "#ff9b00", "#ffcc00"],
|
|
|
+ echarts: undefined,
|
|
|
+ dayEcharts: undefined,
|
|
|
+ width: document.body.offsetWidth || 0,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ watch: {},
|
|
|
+ mounted() {
|
|
|
+ let D = new Date();
|
|
|
+ const lasetDay = new Dayjs(D.getTime() - D.getDay() * 86400000);
|
|
|
+ const firstDay = new Dayjs(lasetDay - 6 * 86400000);
|
|
|
+ jsonZhou({
|
|
|
+ start: firstDay.format("YYYYMMDD"),
|
|
|
+ end: lasetDay.format("YYYYMMDD"),
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ this.platfromData = res || [];
|
|
|
+ this.$emit("start", {
|
|
|
+ status: 200,
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ this.$emit("start", {
|
|
|
+ status: err,
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ window.onresize = () => {
|
|
|
+ const doc = document.body;
|
|
|
+ const width = doc.offsetWidth - 32;
|
|
|
+ let height = (width / 4) * 3;
|
|
|
+ this.width = document.body.offsetWidth || 0;
|
|
|
+ this.dayEcharts &&
|
|
|
+ this.dayEcharts.resize({
|
|
|
+ width,
|
|
|
+ height: width,
|
|
|
+ });
|
|
|
+ this.echarts &&
|
|
|
+ this.echarts.resize({
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ });
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {},
|
|
|
+ methods: {
|
|
|
+ formatNum(num, w) {
|
|
|
+ if (isNaN(num)) return 0;
|
|
|
+ return Number(num).toFixed(w || 4) - 0;
|
|
|
+ },
|
|
|
+ formatType(num, W) {
|
|
|
+ let N = this.formatNum(num, 2);
|
|
|
+ let wei = "";
|
|
|
+ if (N >= 100000000) {
|
|
|
+ N = (N / 100000000).toFixed(W || 2);
|
|
|
+ wei = "亿";
|
|
|
+ } else if (N >= 10000) {
|
|
|
+ N = (N / 10000).toFixed(W || 2);
|
|
|
+ wei = "万";
|
|
|
+ }
|
|
|
+ N = N.toString().split(".");
|
|
|
+ return {
|
|
|
+ N: (N[0] || 0) * 1,
|
|
|
+ P: (N[1] || 0) * 1,
|
|
|
+ wei,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ timeFormat(t) {
|
|
|
+ const Time = t || 0;
|
|
|
+ let out = Time.toFixed(1) - 0;
|
|
|
+ return out;
|
|
|
+ },
|
|
|
+ showPopup(i) {
|
|
|
+ this.show = true;
|
|
|
+ const reaios = this.platfromData[i] || {};
|
|
|
+ let hitcount = this.formatType(reaios.hitcount, 2);
|
|
|
+ reaios.hitcount = hitcount.N;
|
|
|
+ reaios.hitcountP = hitcount.P;
|
|
|
+ reaios.hitcountWei = hitcount.wei;
|
|
|
+ let usrcount = this.formatType(reaios.usrcount, 2);
|
|
|
+ reaios.usrcount = usrcount.N;
|
|
|
+ reaios.usrcountP = usrcount.P;
|
|
|
+ reaios.usrcountWei = usrcount.wei;
|
|
|
+ let timecount = this.formatType(reaios.timecount / 60, 2);
|
|
|
+ reaios.timecount = timecount.N;
|
|
|
+ reaios.timecountP = timecount.P;
|
|
|
+ reaios.timecountWei = timecount.wei + "小时";
|
|
|
+ this.ratios = reaios;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.upEcharts();
|
|
|
+ this.dayEchartsFun();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ dayEchartsFun() {
|
|
|
+ this.dayEcharts && this.dayEcharts.clear && this.dayEcharts.clear();
|
|
|
+ if (!this.dayEcharts) {
|
|
|
+ const doc = document.body;
|
|
|
+ const width = doc.offsetWidth - 32;
|
|
|
+ let height = (width / 4) * 3;
|
|
|
+ if (height > 400) height = 400;
|
|
|
+ this.dayEcharts = echarts.init(
|
|
|
+ this.$refs.day,
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ const key = [],
|
|
|
+ value = [];
|
|
|
+ this.ratios.detail_list.map(v => {
|
|
|
+ let val = v.usrcount;
|
|
|
+ key.push(v.dt);
|
|
|
+ value.push(val);
|
|
|
+ });
|
|
|
+ this.dayEcharts.setOption({
|
|
|
+ title: {
|
|
|
+ text: "用户趋势",
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 14,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ show: true,
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ boundaryGap: false,
|
|
|
+ data: key,
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ right: "15px",
|
|
|
+ left: "40px",
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ axisLabel: {
|
|
|
+ formatter: e => {
|
|
|
+ const T = this.formatType(e);
|
|
|
+ let out = T.N;
|
|
|
+ if (T.P) out += "." + T.P;
|
|
|
+ out += T.wei;
|
|
|
+ return out;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: "line",
|
|
|
+ data: value,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ },
|
|
|
+ upEcharts() {
|
|
|
+ this.echarts && this.echarts.clear && this.echarts.clear();
|
|
|
+ if (!this.echarts) {
|
|
|
+ const doc = document.body;
|
|
|
+ const width = doc.offsetWidth - 32;
|
|
|
+ let height = width;
|
|
|
+ if (height > 667) height = 667;
|
|
|
+ this.echarts = echarts.init(
|
|
|
+ this.$refs.pie,
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ const key = [],
|
|
|
+ value = [];
|
|
|
+ let max = 0;
|
|
|
+ this.ratios.duration_list.map(v => {
|
|
|
+ let val = (v.value * 100).toFixed(2) - 0;
|
|
|
+ key.push(v.range);
|
|
|
+ value.push(val);
|
|
|
+ max < val && (max = val);
|
|
|
+ });
|
|
|
+ max += max * 0.2;
|
|
|
+ max > 100 && (max = 100);
|
|
|
+ this.echarts.setOption({
|
|
|
+ title: {
|
|
|
+ text: "观看时长用户分布",
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 14,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ radar: {
|
|
|
+ shape: "circle",
|
|
|
+ center: ["50%", "50%"],
|
|
|
+ radius: 100,
|
|
|
+ indicator: key.map((v, i) => {
|
|
|
+ let m = value[i] / max,
|
|
|
+ val = max;
|
|
|
+ m <= 0.5 ? (val = 0.5 * max) : "";
|
|
|
+ return {
|
|
|
+ name: v,
|
|
|
+ max: val,
|
|
|
+ };
|
|
|
+ }),
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: "radar",
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ value,
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ formatter: function (params) {
|
|
|
+ return params.value + "%";
|
|
|
+ },
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: "#F9713C",
|
|
|
+ },
|
|
|
+ areaStyle: {
|
|
|
+ opacity: 0.1,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ },
|
|
|
+ },
|
|
|
+ beforeUnmount() {},
|
|
|
+ components: {
|
|
|
+ counto,
|
|
|
+ vanCol,
|
|
|
+ vanRow,
|
|
|
+ vanIcon,
|
|
|
+ vanPopup,
|
|
|
+ vanCell,
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.tv-list {
|
|
|
+ background-color: #fff;
|
|
|
+ font-size: 15px;
|
|
|
+ padding: 5px;
|
|
|
+ height: 100%;
|
|
|
+ overflow-y: scroll;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+.td {
|
|
|
+ text-align: center;
|
|
|
+ line-height: 2.5em;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ vertical-align: middle;
|
|
|
+}
|
|
|
+.td2 {
|
|
|
+ line-height: 1.5em;
|
|
|
+ white-space: initial;
|
|
|
+}
|
|
|
+.item {
|
|
|
+ overflow: hidden;
|
|
|
+ border-radius: 3px;
|
|
|
+ margin-top: 11px;
|
|
|
+ background-color: #f5f6f8;
|
|
|
+}
|
|
|
+.itemHead {
|
|
|
+ margin-top: 0;
|
|
|
+ background-color: #f5f6f8;
|
|
|
+}
|
|
|
+.main {
|
|
|
+ padding: 0.5em;
|
|
|
+}
|
|
|
+.content {
|
|
|
+ margin: 0 auto;
|
|
|
+ max-width: 667px;
|
|
|
+}
|
|
|
+.rise {
|
|
|
+ /* 上升 */
|
|
|
+ transform: rotate(180deg);
|
|
|
+}
|
|
|
+</style>
|
|
|
+<style>
|
|
|
+.tv-list .van-tab {
|
|
|
+ font-size: 16px;
|
|
|
+}
|
|
|
+</style>
|