liyongli 3 سال پیش
والد
کامیت
0bb22ada1e

+ 38 - 1
src/router/index.js

@@ -77,6 +77,42 @@ const routes = [
             path: "/country/flow", // 频道实时流入流出
             component: ()=> import("../views/Flow/Flow.vue")
         },
+        {
+            path: "/country/program", // 节目点分钟
+            component: ()=> import("../views/Program/Program.vue")
+        },
+        {
+            path: "/country/compete", // 节目竞争一览
+            component: ()=> import("../views/Compete/Compete.vue")
+        },
+        {
+            path: "/country/programDetail", // 具体节目剧目查询
+            component: ()=> import("../views/ProgramDetail/ProgramDetail.vue")
+        },
+        {
+            path: "/country/programDetail", // 具体节目剧目排行
+            component: ()=> import("../views/ProgramDetail/ProgramDetail.vue")
+        },
+        {
+            path: "/country/programDetail", // 抽象节目剧目查询
+            component: ()=> import("../views/ProgramDetail/ProgramDetail.vue")
+        },
+        {
+            path: "/country/programDetail", // 抽象节目剧目排行
+            component: ()=> import("../views/ProgramDetail/ProgramDetail.vue")
+        },
+        {
+            path: "/country/flow", // 单期节目流入流出
+            component: ()=> import("../views/Flow/Flow.vue")
+        },
+        {
+            path: "/country/abstract", // 抽象节目剧目分地区收视(全国)
+            component: ()=> import("../views/Abstract/Abstract.vue")
+        },
+        {
+            path: "/country/overlap", // 节目剧目重叠度
+            component: ()=> import("../views/Overlap/Overlap.vue")
+        },
         {
             path: "/country/scheduling", // 电视剧排播信息及表现(全国)
             component: ()=> import("../views/Scheduling/Scheduling.vue")
@@ -84,7 +120,8 @@ const routes = [
     ],
   },
 ];
-
+// 实时/单期 节目流入流出 共计2个  可以使用同一模块
+// 具体/抽象 节目剧目 查询/排行  共计4个,可以使用一个模块
 const router = createRouter({
   history: createWebHashHistory(process.env.BASE_URL),
   routes,

+ 288 - 0
src/views/Abstract/Abstract.vue

@@ -0,0 +1,288 @@
+<template>
+  <div class="SingleDay">
+    <el-breadcrumb separator-class="el-icon-arrow-right">
+      <el-breadcrumb-item>传统媒体</el-breadcrumb-item>
+      <el-breadcrumb-item>抽象节目剧目分地区收视(全国)</el-breadcrumb-item>
+    </el-breadcrumb>
+    <el-card class="box-card">
+      <el-form
+        ref="form"
+        :model="form"
+        size="small"
+        :inline="true"
+        label-width="120px"
+        class="demo-form-inline"
+      >
+        <el-form-item label="日期">
+          <el-date-picker
+            v-model="form.date"
+            type="daterange"
+            :disabled-date="time => disabledDate(time)"
+            range-separator="-"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="频道">
+          <el-select
+            v-model="form.channel"
+            placeholder="请选择频道"
+            multiple
+            collapse-tags
+            @change="channelSelect"
+          >
+            <el-option
+              v-for="item in channelList"
+              :key="item.label"
+              :label="item.label"
+              :value="item.label"
+              :disabled="item.disabled"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="区域">
+          <el-select
+            v-model="form.region"
+            placeholder="请选择区域"
+            @change="regionSelect"
+          >
+            <el-option
+              v-for="item in region"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item style="float: right">
+          <el-button type="primary" @click="onSubmit">查询</el-button>
+          <el-button type="primary" @click="onExport">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <br />
+    <el-card class="box-card">
+      <line-charts
+        :list="tableDate"
+        xName="date"
+        :keys="chartKeys"
+      ></line-charts>
+      <el-table
+        :data="tableDate"
+        :header-cell-style="{ backgroundColor: '#f4f5f7', color: '#606266' }"
+        style="width: 100%"
+      >
+        <el-table-column
+          align="center"
+          show-overflow-tooltip
+          width="400px"
+          prop="date"
+          label="29省"
+          class-name="nowrap"
+        />
+        <el-table-column
+          v-for="(item, i) in tableKeys"
+          :key="'table' + i"
+          align="center"
+          :prop="item.key"
+          :formatter="matrer"
+          :label="item.name"
+        >
+          <el-table-column
+            v-for="(li, o) in item.children"
+            :key="i + '-' + o"
+            :prop="li.key"
+            :label="li.name"
+            :formatter="matrer"
+          />
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+// import {  } from "@/api/index";
+
+import lineCharts from "@/views/Country/components/lineCharts";
+
+import config from "@/config/index";
+export default {
+  name: "Channel",
+  data() {
+    return {
+      form: {
+        channel: 0,
+        date: [
+          new Date(new Date() - 86400000),
+          new Date(new Date() - 86400000),
+        ],
+        region: 9,
+      },
+      tableData: [
+        {
+          abs_epg: "丝路新周刊 [陕西卫视]",
+          area_c_1: "陕西",
+          channel_0: "陕西卫视",
+          indicators_market_ratings: 0.01007562931012467,
+          indicators_market_ratings_rank: 1,
+          indicators_tv_ratings: 0.0015932668968739211,
+          indicators_tv_ratings_rank: 1,
+          is_live: "首播/重播",
+          week: "星期六",
+        },
+      ],
+    };
+  },
+  mounted() {},
+  computed: {
+    channelList() {
+      return config.channelNameList;
+    },
+    region() {
+      let reg = config.region || [],
+        li = [];
+      for (let i = 0; i < reg.length; i++) {
+        const v = reg[i];
+        if (v.label === "行业分城") {
+          li = v.options || [];
+          break;
+        }
+      }
+      return li;
+    },
+    tableDate() {
+      const obj = {};
+
+      const b = [
+        {
+          key: "indicators_tv_ratings",
+          name: "直播关注度",
+        },
+        {
+          key: "indicators_market_ratings",
+          name: "市占率",
+        },
+      ];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const v = this.tableData[i];
+        if (typeof obj[v.area_c_1] !== "object") obj[v.area_c_1] = {};
+        obj[v.area_c_1][v.area_c_1] = v;
+      }
+      let li = [],
+        keys = Object.keys(obj),
+        dataLen = Object.keys(obj[keys[0]]);
+      for (let i = 0; i < dataLen.length; i++) {
+        let p = {};
+        for (let o = 0; o < keys.length; o++) {
+          const v = obj[keys[o]][dataLen[i]];
+          b.map(k => {
+            p[keys[o] + "_" + k.key] = v[k.key];
+          });
+        }
+        li.push({
+          date: dataLen[i],
+          ...p,
+        });
+      }
+      console.log(li)
+      return li;
+    },
+    chartKeys() {
+      const obj = {},
+        li = [];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const b = [
+          {
+            key: "indicators_tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            key: "indicators_market_ratings",
+            name: "市占率",
+          },
+        ];
+        const v = this.tableData[i];
+        if (obj[v.area_c_1] === true) continue;
+        obj[v.area_c_1] = true;
+        li.push(
+          ...b.map(k => {
+            k.key = v.area_c_1 + "_" + k.key;
+            k.name = v.area_c_1 + "_" + k.name;
+            return k;
+          })
+        );
+      }
+      return li;
+    },
+    tableKeys() {
+      const obj = {},
+        li = [];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const b = [
+          {
+            key: "indicators_tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            key: "indicators_market_ratings",
+            name: "市占率",
+          },
+        ];
+        const v = this.tableData[i];
+        if (obj[v.area_c_1] === true) continue;
+        obj[v.area_c_1] = true;
+        li.push({
+          name: v.area_c_1,
+          children: b.map(k => {
+            k.key = v.area_c_1 + "_" + k.key;
+            return k;
+          }),
+        });
+      }
+      console.log(li)
+      return li;
+    },
+  },
+  methods: {
+    onExport() {},
+    disabledDate(time) {
+      return time.getTime() > Date.now() - 86400000;
+    },
+    onSubmit() {},
+    matrer(row, column, cellValue) {
+      return (cellValue * 100).toFixed(4) - 0 + "%";
+    },
+    channelSelect(a) {
+      this.selectAll(a, "channel", 0);
+    },
+    regionSelect(a) {
+      this.selectAll(a, "region", -1);
+    },
+    selectAll(a, key, val) {
+      if (!a.length) return;
+      let select = a[a.length - 1];
+      if (select === val) this.form[key] = [val];
+      else
+        this.form[key] = (a.join(",") + ",")
+          .replace(val + ",", "")
+          .replace(/,$/, "")
+          .split(",");
+    },
+  },
+  components: { lineCharts },
+};
+</script>
+
+<style>
+.SingleDay {
+  margin: 10px 15px;
+}
+.SingleDay .nowrap .cell {
+  white-space: nowrap;
+}
+</style>

+ 536 - 0
src/views/Compete/Compete.vue

@@ -0,0 +1,536 @@
+<template>
+  <div class="Compete">
+    <el-breadcrumb separator-class="el-icon-arrow-right">
+      <el-breadcrumb-item>传统媒体</el-breadcrumb-item>
+      <el-breadcrumb-item>节目竞争一览</el-breadcrumb-item>
+    </el-breadcrumb>
+    <el-card class="box-card">
+      <el-form
+        ref="form"
+        :model="form"
+        size="small"
+        :inline="true"
+        label-width="120px"
+        class="demo-form-inline"
+      >
+        <el-form-item label="节目">
+          <el-select
+            v-model="form.program"
+            class="m-2"
+            placeholder="节目名称"
+            multiple
+            filterable
+            remote
+            reserve-keyword
+            :remote-method="remoteMethod"
+            :loading="loading"
+          >
+            <el-option
+              v-for="item in options"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="节目选择">
+          <el-cascader
+            v-model="form.egp"
+            collapse-tags
+            :options="region"
+            :props="{ multiple: true, children: 'options' }"
+          ></el-cascader>
+        </el-form-item>
+        <el-form-item label="区域">
+          <el-select
+            v-model="form.region"
+            placeholder="请选择区域"
+            @change="regionSelect"
+          >
+            <el-option
+              v-for="item in region"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item style="float: right">
+          <el-button type="primary" @click="onSubmit">查询</el-button>
+          <el-button type="primary" @click="onExport">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <br />
+    <el-card class="box-card">
+      <el-radio-group v-model="radio">
+        <el-radio :label="0">同时段频道 </el-radio>
+        <el-radio :label="1">同时段节目 </el-radio>
+        <el-radio :label="2">同时段多节目 </el-radio>
+      </el-radio-group>
+
+      <el-row style="margin: 1em 0;" v-if="radio === 2" :gutter="20">
+        <el-col :span="6">
+          <el-card>
+            <div class="title">
+              直播关注度
+            </div>
+            <div class="body">{{ format(total.tv_ratings) }}%</div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card>
+            <div class="title">
+              市占率
+            </div>
+            <div class="body">{{ format(total.market_ratings) }}%</div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card>
+            <div class="title">
+              平均到达率
+            </div>
+            <div class="body">{{ format(total.arrive) }}%</div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card>
+            <div class="title">
+              平均忠诚度
+            </div>
+            <div class="body">{{ format(total.loyal) }}%</div>
+          </el-card>
+        </el-col>
+      </el-row>
+      <el-select
+        v-model="col"
+        @change="change"
+        class="m-2"
+        placeholder="请选择"
+        multiple
+        collapse-tags
+        v-if="radio === 2"
+        style="float: right;"
+      >
+        <el-option
+          v-for="item in cols"
+          :key="item.type"
+          :label="item.name"
+          :value="item.type"
+        >
+        </el-option>
+      </el-select>
+      <el-table :data="tableList" style="width: 100%">
+        <el-table-column
+          :prop="item.type"
+          :label="item.name"
+          :type="item.colType"
+          v-for="(item, i) in columns[radio] || []"
+          :key="'col' + i"
+        />
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+// import {  } from "@/api/index";
+
+// import lineCharts from "@/views/Country/components/lineCharts";
+
+import config from "@/config/index";
+const three = [
+  {
+    type: "epg_name",
+    name: "节目",
+  },
+  {
+    type: "tv_name",
+    name: "频道",
+  },
+  {
+    type: "start_time:end_time",
+    name: "播出时间",
+  },
+];
+export default {
+  name: "Channel",
+  data() {
+    return {
+      form: {
+        program: "",
+        region: 9,
+        epg: -1,
+      },
+      options: [],
+      list: [],
+      loading: false,
+      radio: 0,
+      col: [],
+      cols: [
+        {
+          type: "all_sametime-同时段全频道排名",
+          name: "同时段全频道排名",
+        },
+        {
+          type: "all_sametime_province_weishi-同时段省卫频道排名",
+          name: "同时段省卫频道排名",
+        },
+        {
+          type: "all_sametime_weishi-同时段卫视频道排名",
+          name: "同时段卫视频道排名",
+        },
+        {
+          type: "all_sametime_weishi_cctv1-同时段综合频道排名",
+          name: "同时段综合频道排名",
+        },
+        {
+          type: "all_sametime_jiemu-同时段全频道节目排名",
+          name: "同时段全频道节目排名",
+        },
+        {
+          type: "all_in_day-全天全频道节目排名",
+          name: "全天全频道节目排名",
+        },
+        {
+          type: "all_weishi-全天卫视频道节目排名",
+          name: "全天卫视频道节目排名",
+        },
+        {
+          type: "tv_ratings-直播关注度",
+          name: "直播关注度",
+        },
+        {
+          type: "market_ratings-市占率",
+          name: "市占率",
+        },
+        {
+          type: "arrive-到达率",
+          name: "到达率",
+        },
+        {
+          type: "loyal-忠诚度",
+          name: "忠诚度",
+        },
+        {
+          type: "column_remain-上期节目留存",
+          name: "上期节目留存",
+        },
+        {
+          type: "traction-用户牵引",
+          name: "用户牵引",
+        },
+        {
+          type: "tv_contribution-直播关注度贡献",
+          name: "直播关注度贡献",
+        },
+      ],
+      tableList: [
+        {
+          market_ratings: 0.103766,
+          tv_name: "CCTV-8",
+          time_range: "04:02~04:32",
+          epgs: "《破冰行动 8 9 10 11 12 13》、《星推荐》",
+          rank: 1,
+          tv_ratings: 0.000401,
+          id: 13,
+          type: "央视",
+        },
+        {
+          market_ratings: 0.081786,
+          tv_name: "CCTV-新闻",
+          time_range: "04:02~04:32",
+          epgs: "《新闻直播间》",
+          rank: 2,
+          tv_ratings: 0.000316,
+          id: 18,
+          type: "央视",
+        },
+      ],
+      columns: [
+        [
+          {
+            colType: "index",
+            name: "排名",
+          },
+          {
+            type: "tv_name",
+            name: "频道",
+          },
+          {
+            type: "time_range",
+            name: "时段",
+          },
+          {
+            type: "tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            type: "market_ratings",
+            name: "市占率",
+          },
+          {
+            type: "epgs",
+            name: "节目信息",
+          },
+        ],
+        [
+          {
+            colType: "index",
+            name: "排名",
+          },
+          {
+            type: "epg_name",
+            name: "节目",
+          },
+          {
+            type: "tv_name",
+            name: "频道",
+          },
+          {
+            type: "start_time:end_time",
+            name: "播出时间",
+          },
+          {
+            type: "tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            type: "market_ratings",
+            name: "市占率",
+          },
+          {
+            type: "arrive",
+            name: "到达率",
+          },
+          {
+            type: "loyal",
+            name: "忠诚度",
+          },
+          {
+            type: "column_remain",
+            name: "节目留存",
+          },
+        ],
+        [
+          {
+            type: "epg_name",
+            name: "节目",
+          },
+          {
+            type: "tv_name",
+            name: "频道",
+          },
+          {
+            type: "start_time:end_time",
+            name: "播出时间",
+          },
+        ],
+      ],
+      total: {
+        arrive: 0.00003,
+        loyal: 0.412556,
+        market_ratings: 0.003147,
+        tv_ratings: 0.000012,
+      },
+    };
+  },
+  mounted() {},
+  computed: {
+    region() {
+      let reg = config.region || [],
+        li = [];
+      for (let i = 0; i < reg.length; i++) {
+        const v = reg[i];
+        if (v.label === "行业分城") {
+          li = v.options || [];
+          break;
+        }
+      }
+      return li;
+    },
+    tableDate() {
+      const obj = {};
+
+      const b = [
+        {
+          key: "indicators_tv_ratings",
+          name: "直播关注度",
+        },
+        {
+          key: "indicators_market_ratings",
+          name: "市占率",
+        },
+      ];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const v = this.tableData[i];
+        if (typeof obj[v.epg] !== "object") obj[v.epg] = {};
+        obj[v.epg][v.minute] = v;
+      }
+      let li = [],
+        keys = Object.keys(obj),
+        dataLen = [];
+      for (let i = 0; i < keys.length; i++) {
+        const k = keys[i];
+        dataLen.push(...Object.keys(obj[k]));
+      }
+      for (let i = 0; i < dataLen.length; i++) {
+        let p = {};
+        for (let o = 0; o < keys.length; o++) {
+          const v = obj[keys[o]][dataLen[i]];
+          if (!v) continue;
+
+          b.map(k => {
+            p[keys[o] + "_" + k.key] = v[k.key] || 0;
+          });
+        }
+        li.push({
+          date: dataLen[i],
+          ...p,
+        });
+      }
+      return li;
+    },
+    chartKeys() {
+      const obj = {},
+        li = [];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const b = [
+          {
+            key: "indicators_tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            key: "indicators_market_ratings",
+            name: "市占率",
+          },
+        ];
+        const v = this.tableData[i];
+        if (obj[v.epg] === true) continue;
+        obj[v.epg] = true;
+        li.push(
+          ...b.map(k => {
+            k.key = v.epg + "_" + k.key;
+            k.name = v.epg + "_" + k.name;
+            return k;
+          })
+        );
+      }
+      return li;
+    },
+    tableKeys() {
+      const obj = {},
+        li = [];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const b = [
+          {
+            key: "indicators_tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            key: "indicators_market_ratings",
+            name: "市占率",
+          },
+        ];
+        const v = this.tableData[i];
+        if (obj[v.epg] === true) continue;
+        obj[v.epg] = true;
+        li.push({
+          name: v.epg,
+          children: b.map(k => {
+            k.key = v.epg + "_" + k.key;
+            return k;
+          }),
+        });
+      }
+      return li;
+    },
+  },
+  methods: {
+    onExport() {},
+    onSubmit() {},
+    matrer(row, column, cellValue) {
+      let val = cellValue || 0;
+      return (val * 100).toFixed(4) - 0 + "%";
+    },
+    regionSelect(a) {
+      this.selectAll(a, "region", -1);
+    },
+    selectAll(a, key, val) {
+      if (!a.length) return;
+      let select = a[a.length - 1];
+      if (select === val) this.form[key] = [val];
+      else
+        this.form[key] = (a.join(",") + ",")
+          .replace(val + ",", "")
+          .replace(/,$/, "")
+          .split(",");
+    },
+    change(v) {
+      let ori = v || [];
+      let l = JSON.parse(JSON.stringify(three));
+      for (let i = 0; i < ori.length; i++) {
+        const ol = ori[i];
+        let li = (ol || "").split("-");
+        let o = {
+          type: li[0],
+          name: li[1],
+        };
+        l.push(o);
+      }
+      this.columns[2] = l;
+    },
+    format(v) {
+      return (v * 100).toFixed(4);
+    },
+    remoteMethod(query) {
+      if (query) {
+        this.loading = true;
+        setTimeout(() => {
+          this.loading = false;
+          this.options = this.list.filter(item => {
+            return item.label.toLowerCase().indexOf(query.toLowerCase()) > -1;
+          });
+        }, 200);
+      } else {
+        this.options = [];
+      }
+    },
+  },
+  components: {},
+};
+</script>
+
+<style>
+.Compete {
+  margin: 10px 15px;
+}
+.Compete .nowrap .cell {
+  white-space: nowrap;
+}
+.Compete .nowrap .cell {
+  white-space: nowrap;
+}
+.Compete .title {
+  box-sizing: border-box;
+  font-size: 14px;
+  color: #787b82;
+  padding: auto 1em;
+}
+.Compete .body {
+  width: 100%;
+  height: 108px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+  flex-wrap: nowrap;
+  font-size: 30px;
+  color: #53507b;
+  font-weight: 700;
+}
+</style>

+ 73 - 0
src/views/Country/components/pieCharts.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="countryLineChart" ref="countryLineChart"></div>
+</template>
+
+<script>
+// import * as echarts from "echarts";
+// import config from "@/config/index";
+import * as echarts from "echarts/core";
+import { GaugeChart } from "echarts/charts";
+import { TooltipComponent } from "echarts/components";
+import { CanvasRenderer } from "echarts/renderers";
+echarts.use([CanvasRenderer, TooltipComponent, GaugeChart]);
+
+let chart = undefined;
+export default {
+  name: "countryLineChart",
+  props: ["list"],
+  data: function() {
+    return {
+      leftList: [],
+      rightList: [],
+      chart: undefined,
+    };
+  },
+  filters: {},
+  methods: {
+    createChart() {
+      if (!this.$refs.countryLineChart) return;
+      chart = echarts.init(this.$refs.countryLineChart);
+      chart.resize({
+        height: (this.$refs.countryLineChart.offsetWidth * 6) / 16,
+      });
+      var option = {
+        tooltip: {
+          formatter: "{a} <br/>{b} : {c}%",
+        },
+        series: this.list.map((v, i) => {
+          let y = i < 5 ? 25 : 75;
+          return {
+            progress: {
+              show: true,
+            },
+            detail: {
+              valueAnimation: true,
+              formatter: "{value}",
+            },
+            type: "gauge",
+            center: [((10 + i * 20) % 100) + "%", y + "%"],
+            radius: "40%",
+            data: [
+              {
+                value: (v.score * 100).toFixed(2) - 0,
+                name: v.name,
+              },
+            ],
+          };
+        }),
+      };
+
+      option && chart.setOption(option);
+    },
+  },
+  mounted() {
+    console.log(this.list);
+    this.createChart();
+  },
+  beforeUnmount: function() {
+    chart.dispose();
+  },
+  components: {},
+};
+</script>
+<style></style>

+ 417 - 0
src/views/Overlap/Overlap.vue

@@ -0,0 +1,417 @@
+<template>
+  <div class="Overlap">
+    <el-breadcrumb separator-class="el-icon-arrow-right">
+      <el-breadcrumb-item>传统媒体</el-breadcrumb-item>
+      <el-breadcrumb-item>具体节目剧目查询</el-breadcrumb-item>
+    </el-breadcrumb>
+    <el-card class="box-card">
+      <el-form
+        ref="form"
+        :model="form"
+        size="small"
+        :inline="true"
+        label-width="120px"
+        class="demo-form-inline"
+      >
+        <el-form-item label="日期">
+          <el-date-picker
+            v-model="form.date"
+            type="daterange"
+            :disabled-date="time => disabledDate(time)"
+            range-separator="-"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="时间">
+          <el-time-picker
+            v-model="form.rangeTime"
+            is-range
+            format="HH:mm"
+            range-separator="-"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+          >
+          </el-time-picker>
+        </el-form-item>
+        <el-form-item label="频道组">
+          <el-select
+            v-model="form.channelGroup"
+            placeholder="请选择频道组"
+            @change="channelSelect"
+          >
+            <el-option
+              v-for="item in channelList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+              :disabled="item.disabled"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="区域">
+          <el-cascader
+            v-model="form.region"
+            :options="region"
+            :props="{ children: 'options' }"
+          ></el-cascader>
+        </el-form-item>
+        <el-form-item style="float: right">
+          <el-button type="primary" @click="onSubmit">查询</el-button>
+          <el-button type="primary" @click="onExport">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <br />
+    <el-card class="box-card">
+      <template #header>
+        <div class="card-header">
+          <span>与本节目相关重叠度</span>
+          <el-radio-group v-model="radio">
+            <el-radio label="all_left">全部</el-radio>
+            <el-radio label="same_left">同类型</el-radio>
+          </el-radio-group>
+        </div>
+      </template>
+      <pie-charts :list="ori[radio]" />
+    </el-card>
+    <br />
+    <el-card class="box-card">
+      <template #header>
+        <div class="card-header">
+          <span>与其它节目相关重叠度</span>
+          <el-radio-group v-model="radio1">
+            <el-radio label="all_right">全部</el-radio>
+            <el-radio label="same_right">同类型</el-radio>
+          </el-radio-group>
+        </div>
+      </template>
+      <pie-charts :list="ori[radio1]" />
+    </el-card>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+// import {  } from "@/api/index";
+
+import pieCharts from "@/views/Country/components/pieCharts";
+import config from "@/config/index";
+export default {
+  name: "Channel",
+  data() {
+    return {
+      form: {
+        rangeTime: [],
+        channelGroup: 0,
+        date: [
+          new Date(new Date() - 86400000),
+          new Date(new Date() - 86400000),
+        ],
+        region: -1,
+      },
+      radio: 'all_left',
+      radio1: 'all_right',
+      ori: {
+        same_right: [
+          {
+            score: 0.208093763821318,
+            name: "警察故事",
+            channel: "陕西卫视",
+            id: 46076,
+          },
+          {
+            score: 0.144510582010582,
+            name: "奔腾年代",
+            channel: "宁夏卫视",
+            id: 1330438,
+          },
+          {
+            score: 0.13609825952853052,
+            name: "觉醒年代",
+            channel: "西藏卫视",
+            id: 1334504,
+          },
+          {
+            score: 0.1331103256753345,
+            name: "那年花开月正圆",
+            channel: "陕西卫视",
+            id: 1322842,
+          },
+          {
+            score: 0.1277181392788329,
+            name: "壮士出川",
+            channel: "陕西卫视",
+            id: 472088,
+          },
+          {
+            score: 0.12530191026860865,
+            name: "历史转折中的邓小平",
+            channel: "云南卫视",
+            id: 863290,
+          },
+          {
+            score: 0.12511619727156245,
+            name: "蓝军出击",
+            channel: "新疆卫视",
+            id: 1333395,
+          },
+          {
+            score: 0.12110658430805625,
+            name: "巡回检察组",
+            channel: "山西卫视",
+            id: 1334058,
+          },
+          {
+            score: 0.12048021595510407,
+            name: "我的父亲我的兵",
+            channel: "广西卫视",
+            id: 1324778,
+          },
+          {
+            score: 0.11879487724692912,
+            name: "亮剑",
+            channel: "甘肃卫视",
+            id: 12710,
+          },
+        ],
+        all_right: [
+          {
+            score: 0.2782906884726985,
+            name: "晚间天气预报",
+            channel: "陕西卫视",
+            id: 20275,
+          },
+          {
+            score: 0.27806414662084766,
+            name: "对话书记",
+            channel: "陕西卫视",
+            id: 1331128,
+          },
+          {
+            score: 0.2709613966068355,
+            name: "时代楷模发布厅",
+            channel: "陕西卫视",
+            id: 594511,
+          },
+          {
+            score: 0.2593436645396536,
+            name: "新闻联播",
+            channel: "陕西卫视",
+            id: 10231,
+          },
+          {
+            score: 0.25076297049847407,
+            name: "今日点击",
+            channel: "陕西卫视",
+            id: 12653,
+          },
+          {
+            score: 0.22789425706472197,
+            name: "追赶超越谱新篇",
+            channel: "陕西卫视",
+            id: 1335443,
+          },
+          {
+            score: 0.21213982050070854,
+            name: "敢教日月换新天",
+            channel: "陕西卫视",
+            id: 1335271,
+          },
+          {
+            score: 0.21202879102110528,
+            name: "华山论鉴",
+            channel: "陕西卫视",
+            id: 1315528,
+          },
+          {
+            score: 0.208093763821318,
+            name: "警察故事",
+            channel: "陕西卫视",
+            id: 46076,
+          },
+          {
+            score: 0.20121092649957759,
+            name: "好管家",
+            channel: "陕西卫视",
+            id: 1328878,
+          },
+        ],
+        same_left: [
+          {
+            score: 0.39318401171497985,
+            name: "逐梦蓝天",
+            channel: "CCTV-1",
+            id: 1335585,
+          },
+          {
+            score: 0.36323093819682495,
+            name: "烽火抗大",
+            channel: "CCTV-8",
+            id: 1335526,
+          },
+          {
+            score: 0.29344027689952407,
+            name: "对你的爱很美",
+            channel: "湖南卫视",
+            id: 1335471,
+          },
+          {
+            score: 0.2789962392252138,
+            name: "大决战",
+            channel: "CCTV-8",
+            id: 1335329,
+          },
+          {
+            score: 0.27453655939028854,
+            name: "我是真的爱你",
+            channel: "北京卫视",
+            id: 1335518,
+          },
+          {
+            score: 0.25666455885778944,
+            name: "盛夏晚晴天",
+            channel: "湖南卫视",
+            id: 44807,
+          },
+          {
+            score: 0.2462142643192332,
+            name: "刑警之海外行动",
+            channel: "贵州卫视",
+            id: 1334460,
+          },
+          {
+            score: 0.2458814523912537,
+            name: "妈妈在等你",
+            channel: "江西卫视",
+            id: 1334818,
+          },
+          {
+            score: 0.24172130329150998,
+            name: "亮剑",
+            channel: "甘肃卫视",
+            id: 12710,
+          },
+          {
+            score: 0.23110460278896397,
+            name: "我是真的爱你",
+            channel: "东方卫视",
+            id: 1335518,
+          },
+        ],
+        all_left: [
+          {
+            score: 0.39318401171497985,
+            name: "逐梦蓝天",
+            channel: "CCTV-1",
+            id: 1335585,
+          },
+          {
+            score: 0.36323093819682495,
+            name: "烽火抗大",
+            channel: "CCTV-8",
+            id: 1335526,
+          },
+          {
+            score: 0.35228142576629945,
+            name: "新闻直播间",
+            channel: "CCTV-新闻",
+            id: 12160,
+          },
+          {
+            score: 0.333344427064266,
+            name: "中国新闻",
+            channel: "CCTV-4",
+            id: 11221,
+          },
+          {
+            score: 0.33171364861716646,
+            name: "新闻联播",
+            channel: "CCTV-1",
+            id: 10231,
+          },
+          {
+            score: 0.31690351782207876,
+            name: "东京奥运会体操比赛",
+            channel: "CCTV-5",
+            id: 1335563,
+          },
+          {
+            score: 0.29344027689952407,
+            name: "对你的爱很美",
+            channel: "湖南卫视",
+            id: 1335471,
+          },
+          {
+            score: 0.2839218557593104,
+            name: "寰宇视野",
+            channel: "CCTV-纪录",
+            id: 11160,
+          },
+          {
+            score: 0.2789962392252138,
+            name: "大决战",
+            channel: "CCTV-8",
+            id: 1335329,
+          },
+          {
+            score: 0.27453655939028854,
+            name: "我是真的爱你",
+            channel: "北京卫视",
+            id: 1335518,
+          },
+        ],
+      },
+    };
+  },
+  mounted() {},
+  computed: {
+    channelList() {
+      return config.channelList;
+    },
+    region() {
+      return config.region;
+    },
+  },
+  methods: {
+    onExport() {},
+    disabledDate(time) {
+      return time.getTime() > Date.now() - 86400000;
+    },
+    onSubmit() {},
+    channelSelect(a) {
+      this.selectAll(a, "channelGroup", 0);
+    },
+    regionSelect(a) {
+      this.selectAll(a, "region", -1);
+    },
+    selectAll(a, key, val) {
+      if (!a.length) return;
+      let select = a[a.length - 1];
+      if (select === val) this.form[key] = [val];
+      else
+        this.form[key] = (a.join(",") + ",")
+          .replace(val + ",", "")
+          .replace(/,$/, "")
+          .split(",");
+    },
+  },
+  components: {pieCharts},
+};
+</script>
+
+<style>
+.Overlap {
+  margin: 10px 15px;
+}
+.Overlap .nowrap .cell {
+  white-space: nowrap;
+}
+.Overlap .card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>

+ 346 - 0
src/views/Program/Program.vue

@@ -0,0 +1,346 @@
+<template>
+  <div class="SingleDay">
+    <el-breadcrumb separator-class="el-icon-arrow-right">
+      <el-breadcrumb-item>传统媒体</el-breadcrumb-item>
+      <el-breadcrumb-item>节目点分钟</el-breadcrumb-item>
+    </el-breadcrumb>
+    <el-card class="box-card">
+      <el-form
+        ref="form"
+        :model="form"
+        size="small"
+        :inline="true"
+        label-width="120px"
+        class="demo-form-inline"
+      >
+        <el-form-item label="日期">
+          <el-date-picker
+            v-model="form.date"
+            type="daterange"
+            :disabled-date="time => disabledDate(time)"
+            range-separator="-"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="节目选择">
+            <el-cascader
+            v-model="form.egp"
+            collapse-tags
+            :options="region"
+            :props="{ multiple: true,children: 'options' }"
+          ></el-cascader>
+        </el-form-item>
+        <el-form-item label="区域">
+          <el-select
+            v-model="form.region"
+            placeholder="请选择区域"
+            @change="regionSelect"
+          >
+            <el-option
+              v-for="item in region"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item style="float: right">
+          <el-button type="primary" @click="onSubmit">查询</el-button>
+          <el-button type="primary" @click="onExport">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <br />
+    <el-card class="box-card">
+      <line-charts
+        :list="tableDate"
+        xName="date"
+        :keys="chartKeys"
+      ></line-charts>
+      <el-table
+        :data="tableDate"
+        :header-cell-style="{ backgroundColor: '#f4f5f7', color: '#606266' }"
+        style="width: 100%"
+      >
+        <el-table-column
+          align="center"
+          show-overflow-tooltip
+          width="400px"
+          prop="date"
+          label="日期"
+          class-name="nowrap"
+        />
+        <el-table-column
+          v-for="(item, i) in tableKeys"
+          :key="'table' + i"
+          align="center"
+          :prop="item.key"
+          :formatter="matrer"
+          :label="item.name"
+        >
+          <el-table-column
+            v-for="(li, o) in item.children"
+            :key="i + '-' + o"
+            :prop="li.key"
+            :label="li.name"
+            :formatter="matrer"
+          />
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+// import {  } from "@/api/index";
+
+import lineCharts from "@/views/Country/components/lineCharts";
+
+import config from "@/config/index";
+export default {
+  name: "Channel",
+  data() {
+    return {
+      form: {
+        date: [
+          new Date(new Date() - 86400000),
+          new Date(new Date() - 86400000),
+        ],
+        region: 9,
+        epg: -1
+      },
+      tableData: [
+        {
+          abs_epg: "好管家",
+          ca_types: "生活服务",
+          channel_0: "陕西卫视",
+          day: "2022-01-11",
+          default: "34城",
+          epg: "好管家 - 陕西卫视 - 2022-01-11 21:50~22:26",
+          epg_id: 64969089,
+          indicators_market_ratings: 0.002729,
+          indicators_tv_ratings: 0.00045,
+          is_live: "首播",
+          m: 1310,
+          minute: "21:50",
+          month: "2022-01",
+          start_time: 1641909000000,
+          tv_id: "48",
+          week: "星期二",
+          weekrange: "01-10 ~ 01-16",
+          year: "2022",
+        },
+        {
+          abs_epg: "好管家",
+          ca_types: "生活服务",
+          channel_0: "陕西卫视",
+          day: "2022-01-11",
+          default: "34城",
+          epg: "好管家 - 陕西卫视 - 2022-01-11 21:50~22:26",
+          epg_id: 64969089,
+          indicators_market_ratings: 0.002558,
+          indicators_tv_ratings: 0.000419,
+          is_live: "首播",
+          m: 1311,
+          minute: "21:51",
+          month: "2022-01",
+          start_time: 1641909000000,
+          tv_id: "48",
+          week: "星期二",
+          weekrange: "01-10 ~ 01-16",
+          year: "2022",
+        },
+        {
+          abs_epg: "新闻+",
+          ca_types: "新闻",
+          channel_0: "吉林卫视",
+          day: "2017-08-20",
+          default: "34城",
+          epg: "新闻+ - 吉林卫视 - 2017-08-20 23:06~23:27",
+          epg_id: 42434466,
+          indicators_market_ratings: 0.002738,
+          indicators_tv_ratings: 0.000272,
+          is_live: "首播",
+          m: 1386,
+          minute: "23:06",
+          month: "2017-08",
+          start_time: 1503241560000,
+          tv_id: "41",
+          week: "星期日",
+          weekrange: "08-14 ~ 08-20",
+          year: "2017",
+        },
+        {
+          abs_epg: "新闻+",
+          ca_types: "新闻",
+          channel_0: "吉林卫视",
+          day: "2017-08-20",
+          default: "34城",
+          epg: "新闻+ - 吉林卫视 - 2017-08-20 23:06~23:27",
+          epg_id: 42434466,
+          indicators_market_ratings: 0.002485,
+          indicators_tv_ratings: 0.000244,
+          is_live: "首播",
+          m: 1387,
+          minute: "23:07",
+          month: "2017-08",
+          start_time: 1503241560000,
+          tv_id: "41",
+          week: "星期日",
+          weekrange: "08-14 ~ 08-20",
+          year: "2017",
+        },
+      ],
+    };
+  },
+  mounted() {},
+  computed: {
+    region() {
+      let reg = config.region || [],
+        li = [];
+      for (let i = 0; i < reg.length; i++) {
+        const v = reg[i];
+        if (v.label === "行业分城") {
+          li = v.options || [];
+          break;
+        }
+      }
+      return li;
+    },
+    tableDate() {
+      const obj = {};
+
+      const b = [
+        {
+          key: "indicators_tv_ratings",
+          name: "直播关注度",
+        },
+        {
+          key: "indicators_market_ratings",
+          name: "市占率",
+        },
+      ];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const v = this.tableData[i];
+        if (typeof obj[v.epg] !== "object") obj[v.epg] = {};
+        obj[v.epg][v.minute] = v;
+      }
+      let li = [],
+        keys = Object.keys(obj),
+        dataLen = [];
+      for (let i = 0; i < keys.length; i++) {
+        const k = keys[i];
+        dataLen.push(...Object.keys(obj[k]));
+      }
+      for (let i = 0; i < dataLen.length; i++) {
+        let p = {};
+        for (let o = 0; o < keys.length; o++) {
+          const v = obj[keys[o]][dataLen[i]];
+          if (!v) continue;
+
+          b.map(k => {
+            p[keys[o] + "_" + k.key] = v[k.key] || 0;
+          });
+        }
+        li.push({
+          date: dataLen[i],
+          ...p,
+        });
+      }
+      return li;
+    },
+    chartKeys() {
+      const obj = {},
+        li = [];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const b = [
+          {
+            key: "indicators_tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            key: "indicators_market_ratings",
+            name: "市占率",
+          },
+        ];
+        const v = this.tableData[i];
+        if (obj[v.epg] === true) continue;
+        obj[v.epg] = true;
+        li.push(
+          ...b.map(k => {
+            k.key = v.epg + "_" + k.key;
+            k.name = v.epg + "_" + k.name;
+            return k;
+          })
+        );
+      }
+      return li;
+    },
+    tableKeys() {
+      const obj = {},
+        li = [];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const b = [
+          {
+            key: "indicators_tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            key: "indicators_market_ratings",
+            name: "市占率",
+          },
+        ];
+        const v = this.tableData[i];
+        if (obj[v.epg] === true) continue;
+        obj[v.epg] = true;
+        li.push({
+          name: v.epg,
+          children: b.map(k => {
+            k.key = v.epg + "_" + k.key;
+            return k;
+          }),
+        });
+      }
+      return li;
+    },
+  },
+  methods: {
+    onExport() {},
+    disabledDate(time) {
+      return time.getTime() > Date.now() - 86400000;
+    },
+    onSubmit() {},
+    matrer(row, column, cellValue) {
+      let val = cellValue || 0;
+      return (val * 100).toFixed(4) - 0 + "%";
+    },
+    regionSelect(a) {
+      this.selectAll(a, "region", -1);
+    },
+    selectAll(a, key, val) {
+      if (!a.length) return;
+      let select = a[a.length - 1];
+      if (select === val) this.form[key] = [val];
+      else
+        this.form[key] = (a.join(",") + ",")
+          .replace(val + ",", "")
+          .replace(/,$/, "")
+          .split(",");
+    },
+  },
+  components: { lineCharts },
+};
+</script>
+
+<style>
+.SingleDay {
+  margin: 10px 15px;
+}
+.SingleDay .nowrap .cell {
+  white-space: nowrap;
+}
+</style>

+ 245 - 0
src/views/ProgramDetail/ProgramDetail.vue

@@ -0,0 +1,245 @@
+<template>
+  <div class="ProgramDetail">
+    <el-breadcrumb separator-class="el-icon-arrow-right">
+      <el-breadcrumb-item>传统媒体</el-breadcrumb-item>
+      <el-breadcrumb-item>具体节目剧目查询</el-breadcrumb-item>
+    </el-breadcrumb>
+    <el-card class="box-card">
+      <el-form
+        ref="form"
+        :model="form"
+        size="small"
+        :inline="true"
+        label-width="120px"
+        class="demo-form-inline"
+      >
+        <el-form-item label="日期">
+          <el-date-picker
+            v-model="form.date"
+            type="daterange"
+            :disabled-date="time => disabledDate(time)"
+            range-separator="-"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="时间">
+          <el-time-picker
+            v-model="form.rangeTime"
+            is-range
+            format="HH:mm"
+            range-separator="-"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+          >
+          </el-time-picker>
+        </el-form-item>
+        <el-form-item label="频道组">
+          <el-select
+            v-model="form.channelGroup"
+            placeholder="请选择频道组"
+            @change="channelSelect"
+          >
+            <el-option
+              v-for="item in channelList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+              :disabled="item.disabled"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="区域">
+          <el-cascader
+            v-model="form.region"
+            :options="region"
+            :props="{ children: 'options' }"
+          ></el-cascader>
+        </el-form-item>
+        <el-form-item style="float: right">
+          <el-button type="primary" @click="onSubmit">查询</el-button>
+          <el-button type="primary" @click="onExport">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <br />
+    <el-card class="box-card">
+      <line-charts
+        :list="tableData"
+        xName="abs_epg"
+        :keys="chartKeys"
+      ></line-charts>
+      <el-table
+        :data="tableData"
+        :header-cell-style="{ backgroundColor: '#f4f5f7', color: '#606266' }"
+        style="width: 100%"
+      >
+        <el-table-column
+          align="center"
+          show-overflow-tooltip
+          width="400px"
+          prop="abs_epg"
+          label="省级卫视"
+          class-name="nowrap"
+        />
+        <el-table-column
+          v-for="(item, i) in chartKeys"
+          :key="'table' + i"
+          align="center"
+          :prop="item.key"
+          :formatter="matrer"
+          :label="item.name"
+        />
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+// import {  } from "@/api/index";
+
+import lineCharts from "@/views/Country/components/lineCharts";
+
+import config from "@/config/index";
+export default {
+  name: "Channel",
+  data() {
+    return {
+      chartKeys: [
+        {
+          key: "indicators_tv_ratings",
+          name: "直播关注度排行",
+        },
+        {
+          key: "indicators_tv_ratings",
+          name: "直播关注度",
+        },
+        {
+          key: "indicators_market_ratings",
+          name: "市占率",
+        },
+        {
+          key: "indicators_arrive",
+          name: "到达率",
+        },
+        {
+          key: "indicators_loyal",
+          name: "忠诚度",
+        },
+        {
+          key: "indicators_loyal",
+          name: "上期节目留存",
+        },
+        {
+          key: "indicators_loyal",
+          name: "用户牵引",
+        },
+      ],
+      form: {
+        rangeTime: [],
+        channelGroup: 0,
+        date: [
+          new Date(new Date() - 86400000),
+          new Date(new Date() - 86400000),
+        ],
+        region: -1,
+      },
+      tableData: [
+        {
+          abs_epg: "华山论鉴",
+          ca_types: "生活服务",
+          channel_0: "陕西卫视",
+          day: "2021-01-03",
+          default: "陕西",
+          epg: "华山论鉴 - 陕西卫视 - 2021-01-03 21:12~22:05",
+          epg_id: 60049237,
+          indicators_arrive: 0.007165,
+          indicators_column_remain: 0.027886,
+          indicators_loyal: 0.323005,
+          indicators_market_ratings: 0.014964,
+          indicators_traction: 0.455901,
+          indicators_tv_ratings: 0.002313,
+          is_live: "首播",
+          month: "2021-01",
+          start_time: 1609679520000,
+          tv_id: "48",
+          week: "星期日",
+          weekrange: "12-28 ~ 01-03",
+          year: "2021",
+        },
+        {
+          abs_epg: "华山论鉴",
+          ca_types: "生活服务",
+          channel_0: "陕西卫视",
+          day: "2021-01-10",
+          default: "陕西",
+          epg: "华山论鉴 - 陕西卫视 - 2021-01-10 21:10~22:05",
+          epg_id: 60144132,
+          indicators_arrive: 0.008802,
+          indicators_column_remain: 0.035565,
+          indicators_loyal: 0.312986,
+          indicators_market_ratings: 0.016561,
+          indicators_traction: 0.488494,
+          indicators_tv_ratings: 0.002752,
+          is_live: "首播",
+          month: "2021-01",
+          start_time: 1610284200000,
+          tv_id: "48",
+          week: "星期日",
+          weekrange: "01-04 ~ 01-10",
+          year: "2021",
+        },
+      ],
+    };
+  },
+  mounted() {},
+  computed: {
+    channelList() {
+      return config.channelList;
+    },
+    region() {
+      console.log(config.region);
+      return config.region;
+    },
+  },
+  methods: {
+    onExport() {},
+    disabledDate(time) {
+      return time.getTime() > Date.now() - 86400000;
+    },
+    onSubmit() {},
+    matrer(row, column, cellValue) {
+      return (cellValue * 100).toFixed(4) - 0 + "%";
+    },
+    channelSelect(a) {
+      this.selectAll(a, "channelGroup", 0);
+    },
+    regionSelect(a) {
+      this.selectAll(a, "region", -1);
+    },
+    selectAll(a, key, val) {
+      if (!a.length) return;
+      let select = a[a.length - 1];
+      if (select === val) this.form[key] = [val];
+      else
+        this.form[key] = (a.join(",") + ",")
+          .replace(val + ",", "")
+          .replace(/,$/, "")
+          .split(",");
+    },
+  },
+  components: { lineCharts },
+};
+</script>
+
+<style>
+.ProgramDetail {
+  margin: 10px 15px;
+}
+.ProgramDetail .nowrap .cell {
+  white-space: nowrap;
+}
+</style>

+ 427 - 102
src/views/Scheduling/Scheduling.vue

@@ -1,8 +1,8 @@
 <template>
-  <div class="SingleDay">
+  <div class="Compete">
     <el-breadcrumb separator-class="el-icon-arrow-right">
       <el-breadcrumb-item>传统媒体</el-breadcrumb-item>
-      <el-breadcrumb-item>频道全天节目</el-breadcrumb-item>
+      <el-breadcrumb-item>节目竞争一览</el-breadcrumb-item>
     </el-breadcrumb>
     <el-card class="box-card">
       <el-form
@@ -13,51 +13,50 @@
         label-width="120px"
         class="demo-form-inline"
       >
-        <el-form-item label="日期">
-          <el-date-picker
-            v-model="form.date"
-            type="daterange"
-            :disabled-date="time => disabledDate(time)"
-            range-separator="-"
-            start-placeholder="开始日期"
-            end-placeholder="结束日期"
-          >
-          </el-date-picker>
-        </el-form-item>
-        <el-form-item label="时间">
-          <el-time-picker
-            v-model="form.rangeTime"
-            is-range
-            format="HH:mm"
-            range-separator="-"
-            start-placeholder="开始时间"
-            end-placeholder="结束时间"
-          >
-          </el-time-picker>
-        </el-form-item>
-        <el-form-item label="频道组">
+        <el-form-item label="节目">
           <el-select
-            v-model="form.channelGroup"
-            placeholder="请选择频道组"
-            @change="channelSelect"
+            v-model="form.program"
+            class="m-2"
+            placeholder="节目名称"
+            multiple
+            filterable
+            remote
+            reserve-keyword
+            :remote-method="remoteMethod"
+            :loading="loading"
           >
             <el-option
-              v-for="item in channelList"
+              v-for="item in options"
               :key="item.value"
               :label="item.label"
               :value="item.value"
-              :disabled="item.disabled"
             >
             </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="区域">
+        <el-form-item label="节目选择">
           <el-cascader
-            v-model="form.region"
+            v-model="form.egp"
+            collapse-tags
             :options="region"
-            :props="{children: 'options'}"
+            :props="{ multiple: true, children: 'options' }"
           ></el-cascader>
         </el-form-item>
+        <el-form-item label="区域">
+          <el-select
+            v-model="form.region"
+            placeholder="请选择区域"
+            @change="regionSelect"
+          >
+            <el-option
+              v-for="item in region"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
         <el-form-item style="float: right">
           <el-button type="primary" @click="onSubmit">查询</el-button>
           <el-button type="primary" @click="onExport">导出</el-button>
@@ -66,31 +65,71 @@
     </el-card>
     <br />
     <el-card class="box-card">
-      <line-charts
-        :list="tableData"
-        xName="channel_2"
-        :keys="chartKeys"
-      ></line-charts>
-      <el-table
-        :data="tableData"
-        :header-cell-style="{ backgroundColor: '#f4f5f7', color: '#606266' }"
-        style="width: 100%"
+      <el-radio-group v-model="radio">
+        <el-radio :label="0">同时段频道 </el-radio>
+        <el-radio :label="1">同时段节目 </el-radio>
+        <el-radio :label="2">同时段多节目 </el-radio>
+      </el-radio-group>
+
+      <el-row style="margin: 1em 0;" v-if="radio === 2" :gutter="20">
+        <el-col :span="6">
+          <el-card>
+            <div class="title">
+              直播关注度
+            </div>
+            <div class="body">{{ format(total.tv_ratings) }}%</div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card>
+            <div class="title">
+              市占率
+            </div>
+            <div class="body">{{ format(total.market_ratings) }}%</div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card>
+            <div class="title">
+              平均到达率
+            </div>
+            <div class="body">{{ format(total.arrive) }}%</div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card>
+            <div class="title">
+              平均忠诚度
+            </div>
+            <div class="body">{{ format(total.loyal) }}%</div>
+          </el-card>
+        </el-col>
+      </el-row>
+      <el-select
+        v-model="col"
+        @change="change"
+        class="m-2"
+        placeholder="请选择"
+        multiple
+        collapse-tags
+        v-if="radio === 2"
+        style="float: right;"
       >
+        <el-option
+          v-for="item in cols"
+          :key="item.type"
+          :label="item.name"
+          :value="item.type"
+        >
+        </el-option>
+      </el-select>
+      <el-table :data="tableList" style="width: 100%">
         <el-table-column
-          align="center"
-          show-overflow-tooltip
-          width="400px"
-          prop="channel_2"
-          label="省级卫视"
-          class-name="nowrap"
-        />
-        <el-table-column
-          v-for="(item, i) in chartKeys"
-          :key="'table' + i"
-          align="center"
-          :prop="item.key"
-          :formatter="matrer"
+          :prop="item.type"
           :label="item.name"
+          :type="item.colType"
+          v-for="(item, i) in columns[radio] || []"
+          :key="'col' + i"
         />
       </el-table>
     </el-card>
@@ -101,87 +140,322 @@
 // @ is an alias to /src
 // import {  } from "@/api/index";
 
-import lineCharts from "@/views/Country/components/lineCharts";
+// import lineCharts from "@/views/Country/components/lineCharts";
 
 import config from "@/config/index";
+const three = [
+  {
+    type: "epg_name",
+    name: "节目",
+  },
+  {
+    type: "tv_name",
+    name: "频道",
+  },
+  {
+    type: "start_time:end_time",
+    name: "播出时间",
+  },
+];
 export default {
   name: "Channel",
   data() {
     return {
-      chartKeys: [
+      form: {
+        program: "",
+        region: 9,
+        epg: -1,
+      },
+      options: [],
+      list: [],
+      loading: false,
+      radio: 0,
+      col: [],
+      cols: [
         {
-          key: "indicators_tv_ratings",
-          name: "直播关注度排名",
+          type: "all_sametime-同时段全频道排名",
+          name: "同时段全频道排名",
         },
         {
-          key: "indicators_tv_ratings",
+          type: "all_sametime_province_weishi-同时段省卫频道排名",
+          name: "同时段省卫频道排名",
+        },
+        {
+          type: "all_sametime_weishi-同时段卫视频道排名",
+          name: "同时段卫视频道排名",
+        },
+        {
+          type: "all_sametime_weishi_cctv1-同时段综合频道排名",
+          name: "同时段综合频道排名",
+        },
+        {
+          type: "all_sametime_jiemu-同时段全频道节目排名",
+          name: "同时段全频道节目排名",
+        },
+        {
+          type: "all_in_day-全天全频道节目排名",
+          name: "全天全频道节目排名",
+        },
+        {
+          type: "all_weishi-全天卫视频道节目排名",
+          name: "全天卫视频道节目排名",
+        },
+        {
+          type: "tv_ratings-直播关注度",
           name: "直播关注度",
         },
         {
-          key: "indicators_market_ratings",
+          type: "market_ratings-市占率",
           name: "市占率",
         },
         {
-          key: "indicators_arrive",
+          type: "arrive-到达率",
           name: "到达率",
         },
         {
-          key: "indicators_loyal",
+          type: "loyal-忠诚度",
           name: "忠诚度",
         },
+        {
+          type: "column_remain-上期节目留存",
+          name: "上期节目留存",
+        },
+        {
+          type: "traction-用户牵引",
+          name: "用户牵引",
+        },
+        {
+          type: "tv_contribution-直播关注度贡献",
+          name: "直播关注度贡献",
+        },
       ],
-      form: {
-        rangeTime: [],
-        channelGroup: 0,
-        date: [
-          new Date(new Date() - 86400000),
-          new Date(new Date() - 86400000),
-        ],
-        region: -1,
-      },
-      tableData: [
+      tableList: [
         {
-          channel_2: "广东卫视",
-          indicators_arrive: "",
-          indicators_loyal: "",
-          indicators_market_ratings: 0.022008,
-          indicators_market_ratings_rank: 1,
-          indicators_tv_ratings: 0.003919,
-          indicators_tv_ratings_rank: 1,
+          market_ratings: 0.103766,
+          tv_name: "CCTV-8",
+          time_range: "04:02~04:32",
+          epgs: "《破冰行动 8 9 10 11 12 13》、《星推荐》",
+          rank: 1,
+          tv_ratings: 0.000401,
+          id: 13,
+          type: "央视",
         },
         {
-          channel_2: "湖南卫视",
-          indicators_arrive: "",
-          indicators_loyal: "",
-          indicators_market_ratings: 0.019602,
-          indicators_market_ratings_rank: 2,
-          indicators_tv_ratings: 0.00349,
-          indicators_tv_ratings_rank: 2,
+          market_ratings: 0.081786,
+          tv_name: "CCTV-新闻",
+          time_range: "04:02~04:32",
+          epgs: "《新闻直播间》",
+          rank: 2,
+          tv_ratings: 0.000316,
+          id: 18,
+          type: "央视",
         },
       ],
+      columns: [
+        [
+          {
+            colType: "index",
+            name: "排名",
+          },
+          {
+            type: "tv_name",
+            name: "频道",
+          },
+          {
+            type: "time_range",
+            name: "时段",
+          },
+          {
+            type: "tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            type: "market_ratings",
+            name: "市占率",
+          },
+          {
+            type: "epgs",
+            name: "节目信息",
+          },
+        ],
+        [
+          {
+            colType: "index",
+            name: "排名",
+          },
+          {
+            type: "epg_name",
+            name: "节目",
+          },
+          {
+            type: "tv_name",
+            name: "频道",
+          },
+          {
+            type: "start_time:end_time",
+            name: "播出时间",
+          },
+          {
+            type: "tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            type: "market_ratings",
+            name: "市占率",
+          },
+          {
+            type: "arrive",
+            name: "到达率",
+          },
+          {
+            type: "loyal",
+            name: "忠诚度",
+          },
+          {
+            type: "column_remain",
+            name: "节目留存",
+          },
+        ],
+        [
+          {
+            type: "epg_name",
+            name: "节目",
+          },
+          {
+            type: "tv_name",
+            name: "频道",
+          },
+          {
+            type: "start_time:end_time",
+            name: "播出时间",
+          },
+        ],
+      ],
+      total: {
+        arrive: 0.00003,
+        loyal: 0.412556,
+        market_ratings: 0.003147,
+        tv_ratings: 0.000012,
+      },
     };
   },
   mounted() {},
   computed: {
-    channelList() {
-      return config.channelList;
-    },
     region() {
-        console.log(config.region)
-      return config.region;
+      let reg = config.region || [],
+        li = [];
+      for (let i = 0; i < reg.length; i++) {
+        const v = reg[i];
+        if (v.label === "行业分城") {
+          li = v.options || [];
+          break;
+        }
+      }
+      return li;
+    },
+    tableDate() {
+      const obj = {};
+
+      const b = [
+        {
+          key: "indicators_tv_ratings",
+          name: "直播关注度",
+        },
+        {
+          key: "indicators_market_ratings",
+          name: "市占率",
+        },
+      ];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const v = this.tableData[i];
+        if (typeof obj[v.epg] !== "object") obj[v.epg] = {};
+        obj[v.epg][v.minute] = v;
+      }
+      let li = [],
+        keys = Object.keys(obj),
+        dataLen = [];
+      for (let i = 0; i < keys.length; i++) {
+        const k = keys[i];
+        dataLen.push(...Object.keys(obj[k]));
+      }
+      for (let i = 0; i < dataLen.length; i++) {
+        let p = {};
+        for (let o = 0; o < keys.length; o++) {
+          const v = obj[keys[o]][dataLen[i]];
+          if (!v) continue;
+
+          b.map(k => {
+            p[keys[o] + "_" + k.key] = v[k.key] || 0;
+          });
+        }
+        li.push({
+          date: dataLen[i],
+          ...p,
+        });
+      }
+      return li;
+    },
+    chartKeys() {
+      const obj = {},
+        li = [];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const b = [
+          {
+            key: "indicators_tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            key: "indicators_market_ratings",
+            name: "市占率",
+          },
+        ];
+        const v = this.tableData[i];
+        if (obj[v.epg] === true) continue;
+        obj[v.epg] = true;
+        li.push(
+          ...b.map(k => {
+            k.key = v.epg + "_" + k.key;
+            k.name = v.epg + "_" + k.name;
+            return k;
+          })
+        );
+      }
+      return li;
+    },
+    tableKeys() {
+      const obj = {},
+        li = [];
+      for (let i = 0; i < this.tableData.length; i++) {
+        const b = [
+          {
+            key: "indicators_tv_ratings",
+            name: "直播关注度",
+          },
+          {
+            key: "indicators_market_ratings",
+            name: "市占率",
+          },
+        ];
+        const v = this.tableData[i];
+        if (obj[v.epg] === true) continue;
+        obj[v.epg] = true;
+        li.push({
+          name: v.epg,
+          children: b.map(k => {
+            k.key = v.epg + "_" + k.key;
+            return k;
+          }),
+        });
+      }
+      return li;
     },
   },
   methods: {
     onExport() {},
-    disabledDate(time) {
-      return time.getTime() > Date.now() - 86400000;
-    },
     onSubmit() {},
     matrer(row, column, cellValue) {
-      return (cellValue * 100).toFixed(4) - 0 + "%";
-    },
-    channelSelect(a) {
-      this.selectAll(a, "channelGroup", 0);
+      let val = cellValue || 0;
+      return (val * 100).toFixed(4) - 0 + "%";
     },
     regionSelect(a) {
       this.selectAll(a, "region", -1);
@@ -196,16 +470,67 @@ export default {
           .replace(/,$/, "")
           .split(",");
     },
+    change(v) {
+      let ori = v || [];
+      let l = JSON.parse(JSON.stringify(three));
+      for (let i = 0; i < ori.length; i++) {
+        const ol = ori[i];
+        let li = (ol || "").split("-");
+        let o = {
+          type: li[0],
+          name: li[1],
+        };
+        l.push(o);
+      }
+      this.columns[2] = l;
+    },
+    format(v) {
+      return (v * 100).toFixed(4);
+    },
+    remoteMethod(query) {
+      if (query) {
+        this.loading = true;
+        setTimeout(() => {
+          this.loading = false;
+          this.options = this.list.filter(item => {
+            return item.label.toLowerCase().indexOf(query.toLowerCase()) > -1;
+          });
+        }, 200);
+      } else {
+        this.options = [];
+      }
+    },
   },
-  components: { lineCharts },
+  components: {},
 };
 </script>
 
 <style>
-.SingleDay {
+.Compete {
   margin: 10px 15px;
 }
-.SingleDay .nowrap .cell {
+.Compete .nowrap .cell {
   white-space: nowrap;
 }
+.Compete .nowrap .cell {
+  white-space: nowrap;
+}
+.Compete .title {
+  box-sizing: border-box;
+  font-size: 14px;
+  color: #787b82;
+  padding: auto 1em;
+}
+.Compete .body {
+  width: 100%;
+  height: 108px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+  flex-wrap: nowrap;
+  font-size: 30px;
+  color: #53507b;
+  font-weight: 700;
+}
 </style>