liyongli 3 jaren geleden
bovenliggende
commit
0b26d65bb6

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
   },
   "dependencies": {
     "@element-plus/icons": "^0.0.11",
-    "@element-plus/icons-vue": "^0.2.4",
+    "@element-plus/icons-vue": "^1.1.3",
     "core-js": "^3.6.5",
     "echarts": "^5.2.1",
     "echarts-wordcloud": "^2.0.0",

+ 16 - 0
src/api/index.js

@@ -589,3 +589,19 @@ export function ChannelTop(data) {
     data,
   });
 }
+
+/**
+ * 投放分析
+ * @props {string} channelId
+ * @props {string} start
+ * @props {string} end
+ * @return {AxjxPromise}
+ */
+ export function channelRank(data) {
+    return ajax({
+      urlType: "adAPI",
+      url: "/ad/channel/rank",
+      method: "POST",
+      data,
+    });
+  }

+ 4 - 0
src/main.js

@@ -2,6 +2,10 @@ import { createApp } from 'vue'
 import App from "./App.vue";
 import router from "./router";
 
+import { SortDown } from '@element-plus/icons';
+
 const app = createApp(App).use(router);
 
 app.mount("#app");
+
+app.component("SortDown",SortDown)

+ 5 - 0
src/router/index.js

@@ -51,6 +51,11 @@ const routes = [
       name: "AdvertisingEye",
       component: () => import("../views/AdvertisingEye/AdvertisingEye.vue"),
   },
+  {
+      path: "/channelRanking",
+      name: "ChannelRanking",
+      component: () => import("../views/ChannelRanking/ChannelRanking.vue"),
+  },
   {
     path: "/country",
     component: () => import("../views/Country/Index.vue"),

+ 241 - 0
src/views/ChannelRanking/ChannelRanking.vue

@@ -0,0 +1,241 @@
+<template>
+  <div class="ChannelRanking">
+    <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="dateRank"
+            type="daterange"
+            :default-value="new Date()"
+            :disabled-date="time => disabledDate(time)"
+            range-separator="-"
+            value-format="YYYY-MM-DD"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="排序">
+          <el-select v-model="type" placeholder="请选择">
+            <el-option
+              v-for="item in col"
+              :key="item.type"
+              :label="item.name"
+              :value="item.type - 0"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item style="float: right">
+          <el-button type="primary" :loading="loading" @click="onSubmit">
+            查询
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <br />
+    <el-card class="box-card">
+      <line-charts
+        :list="tableData"
+        v-if="tableData.length"
+        xName="channelName"
+        toolTipUnit="元"
+        :notzoom="true"
+        :keys="select"
+      ></line-charts>
+      <el-table
+        :data="tableData"
+        :header-cell-style="{ backgroundColor: '#f4f5f7', color: '#606266' }"
+        style="width: 100%"
+      >
+        <el-table-column
+          align="center"
+          type="index"
+          label="排名"
+          class-name="nowrap"
+        />
+        <el-table-column
+          align="center"
+          show-overflow-tooltip
+          prop="channelName"
+          label="频道名称"
+          class-name="nowrap"
+        />
+        <el-table-column
+          v-for="(item, i) in col"
+          :key="item.type"
+          align="center"
+          :prop="item.key"
+          :label="item.name"
+          class-name="nowrap"
+        >
+          <template #header>
+            <div>
+            <!-- <div @click="() => sortChange({ prop: item.key })"> -->
+              <el-icon
+                style="vertical-align: middle;"
+                :color="i === type - 1 ? '#409eff' : ''"
+                ><sort-down
+              /></el-icon>
+              <span
+                :style="
+                  'vertical-align: middle;' +
+                    (i === type - 1 ? 'color: #409eff' : '')
+                "
+              >
+                {{ `${item.name}(${item.unit})` }}
+              </span>
+            </div>
+          </template>
+          <template v-slot="scope">
+            <span v-if="item.key === 'fee'">
+              {{ douhao((scope.row[item.key] / 10000 || 0).toFixed(2)) }}
+            </span>
+            <span v-if="item.key === 'timeSize'">
+              {{ douhao((scope.row[item.key] / 60000 || 0).toFixed(2)) }}
+            </span>
+            <span v-if="item.key === 'pinci'">
+              {{ douhao((scope.row[item.key] || 0).toFixed(0)) }}
+            </span>
+            <span v-if="item.key === 'customer'">
+              {{ douhao((scope.row[item.key] || 0).toFixed(0)) }}
+            </span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+import { channelRank } from "@/api/index";
+// import { ElNotification } from 'element-plus'
+import lineCharts from "@/views/ChannelRanking/components/lineCharts";
+
+// import config from "@/config/index";
+export default {
+  name: "Channel",
+  data() {
+    return {
+      type: 1,
+      dataType: 1,
+      col: [
+        {
+          key: "fee",
+          name: "费用",
+          unit: "万元",
+          type: 1,
+        },
+        {
+          key: "timeSize",
+          name: "时长",
+          unit: "分",
+          type: 2,
+        },
+        {
+          key: "pinci",
+          name: "频次",
+          unit: "次",
+          type: 3,
+        },
+        {
+          key: "customer",
+          name: "客户数",
+          unit: "个",
+          type: 4,
+        },
+      ],
+      dateRank: [],
+      tableData: [],
+      loading: false,
+      from: {},
+    };
+  },
+  computed: {
+    select() {
+      return [this.col[this.type - 1]];
+    },
+  },
+  mounted() {
+    const date = new Date();
+    let month = date.getMonth() + 1,
+      day = date.getDate();
+    month > 9 ? "" : (month = "0" + month);
+    day > 9 ? "" : (day = "0" + day);
+    let dateRank = [
+      [date.getFullYear(), month, day].join("-"),
+      [date.getFullYear(), month, day].join("-"),
+    ];
+    this.from = {
+      dataType: this.type,
+      end: dateRank[0] || "",
+      start: dateRank[1] || "",
+    };
+    channelRank(this.from).then(res => {
+      this.tableData = res || [];
+      this.dateRank = dateRank;
+    });
+  },
+  methods: {
+    sortChange(v) {
+      const T = {
+        fee: 1,
+        timeSize: 2,
+        pinci: 3,
+        customer: 4,
+      };
+      this.from.dataType = T[v.prop] || 1;
+      channelRank(this.from).then(res => {
+        this.tableData = res || [];
+        this.type = T[v.prop] || 1;
+        this.dateRank = [this.from.start, this.from.end];
+      });
+    },
+    douhao(text) {
+      if (/\./.test(text)) {
+        return text.replace(/(?=(\B)(\d{3})+\.)/g, ",");
+      } else {
+        return text.replace(/(?=(\B)(\d{3})+$)/g, ",");
+      }
+    },
+    format(v, digit = 2) {
+      if (isNaN(v)) return 0;
+      let o = v.toFixed(digit);
+      if (v > 10000) {
+        o = (v / 10000).toFixed(digit) + "万";
+      }
+
+      return o;
+    },
+    disabledDate(time) {
+      return time.getTime() > Date.now() - 86400000;
+    },
+    onSubmit() {
+      channelRank({
+        dataType: this.type,
+        end: this.dateRank[0] || "",
+        start: this.dateRank[1] || "",
+      }).then(res => {
+        this.tableData = res || [];
+      });
+    },
+  },
+  components: { lineCharts },
+};
+</script>
+
+<style>
+.ChannelRanking {
+  margin: 10px 15px;
+}
+.ChannelRanking .nowrap .cell {
+  white-space: nowrap;
+}
+</style>

+ 188 - 0
src/views/ChannelRanking/components/lineCharts.vue

@@ -0,0 +1,188 @@
+<template>
+  <div class="countryLineChart" ref="countryLineChart"></div>
+</template>
+
+<script>
+// import * as ethis.charts from "ethis.charts";
+import config from "@/config/index";
+import * as echarts from "echarts/core";
+import { BarChart } from "echarts/charts";
+import {
+  TitleComponent,
+  TooltipComponent,
+  LegendComponent,
+  DataZoomComponent,
+  GridComponent,
+} from "echarts/components";
+import { CanvasRenderer } from "echarts/renderers";
+echarts.use([
+  TitleComponent,
+  TooltipComponent,
+  GridComponent,
+  BarChart,
+  CanvasRenderer,
+  LegendComponent,
+  DataZoomComponent,
+]);
+
+let lineTypeOneCharts = undefined;
+
+export default {
+  name: "countryLineChart",
+  props: ["list", "keys", "xName", "toolTipUnit", "notzoom"],
+  c: 12,
+  data: function() {
+    return {
+      leftList: [],
+      rightList: [],
+      chart: undefined,
+    };
+  },
+  filters: {},
+  methods: {
+    format(v, digit = 4) {
+      if (isNaN(v)) return 0;
+      let o = v.toFixed(digit);
+      if (v > 100000000) {
+        o = (v / 100000000).toFixed(digit) + "亿";
+      } else if (v > 10000) {
+        o = (v / 10000).toFixed(digit) + "万";
+      }
+
+      return o;
+    },
+    formatte(list) {
+      let values = [],
+        keys = [];
+      for (let o = 0; o < list.length; o++) {
+        const v = list[o];
+        keys.push(v[this.xName]);
+        for (let i = 0; i < this.keys.length; i++) {
+          const p = this.keys[i];
+          if (!values[i]) values[i] = [];
+          values[i][o] = v[p.key] * 100;
+        }
+      }
+      return {
+        keys,
+        values,
+      };
+    },
+    getTvName(tvId) {
+      let li = config.channelNameList || [];
+      let t = "";
+      for (let i = 0; i < li.length; i++) {
+        const v = li[i];
+        if (v.value !== tvId) continue;
+        t = v.label;
+        break;
+      }
+      return t;
+    },
+    createChart() {
+      if (!this.$refs.countryLineChart) return;
+      if (lineTypeOneCharts && lineTypeOneCharts.dispose)
+        lineTypeOneCharts.dispose(), (lineTypeOneCharts = undefined);
+      lineTypeOneCharts = echarts.init(this.$refs.countryLineChart);
+      let chartData = this.formatte(this.list);
+
+      lineTypeOneCharts.resize({
+        height: (this.$refs.countryLineChart.offsetWidth * 6) / 16,
+      });
+      var option = {
+        tooltip: {
+          trigger: "axis",
+          triggerOn: "mousemove",
+          axisPointer: {
+            type: "line",
+            axis: "x",
+          },
+          formatter: params => {
+            let out = "";
+            let t = "";
+            for (let i = 0; i < params.length; i++) {
+              const par = params[i];
+              if (t === "") {
+                t = par.axisValueLabel;
+                t += "<br />";
+              }
+              out +=
+                par.seriesName +
+                ": " +
+                this.format(par.value, 2) +
+                (this.toolTipUnit || "%") +
+                " <br />";
+            }
+            return t + out;
+          },
+        },
+        grid: { left: 50, top: 10, bottom: 100, right: 50 },
+        xAxis: {
+          type: "category",
+          boundaryGap: true,
+          data: chartData.keys,
+          axisLabel: {
+            formatter: arr => {
+              const li = arr.split("");
+              let out = "", num = 2;
+              for (let i = 0; i < li.length; i++) {
+                const v = li[i];
+                out += v;
+                if(!isNaN(v)) num = 3;
+                if (i !== 0 && i !== li.length - 1 && i % num === 1) out += "\n", num = 2;
+              }
+              return out;
+            },
+          },
+        },
+        yAxis: {
+          type: "value",
+          scale: true,
+          axisLabel: {
+            formatter: arr => {
+              return this.format(arr, 0);
+            },
+          },
+        },
+        series: chartData.values.map((v, i) => {
+          return {
+            type: "bar",
+            name: this.keys[i].name,
+            data: v,
+            showAllSymbol: false,
+            smooth: true,
+          };
+        }),
+      };
+    
+      option.dataZoom = [
+        {
+          show: true,
+          realtime: true,
+          start: 0,
+          end: Math.ceil(this.$refs.countryLineChart.offsetWidth/20),
+          maxValueSpan: Math.ceil(this.$refs.countryLineChart.offsetWidth/20),
+          minValueSpan: Math.ceil(this.$refs.countryLineChart.offsetWidth/40),
+          xAxisIndex: [0, 1],
+        }
+      ];
+      option && lineTypeOneCharts.setOption(option);
+    },
+  },
+  watch: {
+    list() {
+      if (lineTypeOneCharts) lineTypeOneCharts.dispose();
+      this.createChart();
+    },
+  },
+  mounted() {
+    if (lineTypeOneCharts) lineTypeOneCharts.dispose();
+    this.createChart();
+  },
+  beforeUnmount: function() {
+    lineTypeOneCharts && lineTypeOneCharts.dispose();
+  },
+  components: {},
+};
+</script>
+<style></style>

+ 33 - 21
src/views/Country/components/lineCharts.vue

@@ -29,7 +29,7 @@ let lineTypeOneCharts = undefined;
 
 export default {
   name: "countryLineChart",
-  props: ["list", "keys", "xName"],
+  props: ["list", "keys", "xName", "toolTipUnit", "notzoom"],
   c: 12,
   data: function() {
     return {
@@ -40,11 +40,13 @@ export default {
   },
   filters: {},
   methods: {
-    format(v) {
+    format(v, digit = 4) {
       if (isNaN(v)) return 0;
-      let o = v.toFixed(4);
-      if (v > 10000) {
-        o = (v / 10000).toFixed(4) + "万";
+      let o = v.toFixed(digit);
+      if (v > 100000000) {
+        o = (v / 100000000).toFixed(digit) + "亿";
+      } else if (v > 10000) {
+        o = (v / 10000).toFixed(digit) + "万";
       }
 
       return o;
@@ -102,7 +104,11 @@ export default {
                 t += "<br />";
               }
               out +=
-                par.seriesName + ": " + this.format(par.value) + "% <br />";
+                par.seriesName +
+                ": " +
+                this.format(par.value) +
+                (this.toolTipUnit || "%") +
+                " <br />";
             }
             return t + out;
           },
@@ -119,8 +125,25 @@ export default {
         yAxis: {
           type: "value",
           scale: true,
+          axisLabel: {
+            formatter: arr => {
+              return this.format(arr, 0);
+            },
+          },
         },
-        dataZoom: [
+        series: chartData.values.map((v, i) => {
+          return {
+            type: "line",
+            name: this.keys[i].name,
+            data: v,
+            showAllSymbol: false,
+            smooth: true,
+          };
+        }),
+      };
+
+      !this.notzoom &&
+        (option.dataZoom = [
           {
             show: true,
             realtime: true,
@@ -135,18 +158,7 @@ export default {
             end: 70,
             xAxisIndex: [0, 1],
           },
-        ],
-        series: chartData.values.map((v, i) => {
-          return {
-            type: "line",
-            name: this.keys[i].name,
-            data: v,
-            showAllSymbol: false,
-            smooth: true,
-          };
-        }),
-      };
-
+        ]);
       option && lineTypeOneCharts.setOption(option);
     },
   },
@@ -157,8 +169,8 @@ export default {
     },
   },
   mounted() {
-      if (lineTypeOneCharts) lineTypeOneCharts.dispose();
-      this.createChart();
+    if (lineTypeOneCharts) lineTypeOneCharts.dispose();
+    this.createChart();
   },
   beforeUnmount: function() {
     lineTypeOneCharts && lineTypeOneCharts.dispose();

+ 4 - 4
yarn.lock

@@ -910,10 +910,10 @@
     "@babel/helper-validator-identifier" "^7.14.9"
     to-fast-properties "^2.0.0"
 
-"@element-plus/icons-vue@^0.2.4":
-  version "0.2.4"
-  resolved "https://registry.yarnpkg.com/@element-plus/icons-vue/-/icons-vue-0.2.4.tgz#dadcf72f0cea53dc83b7b7db80e1418716d7b02c"
-  integrity sha512-RsJNyL58rwxtsjeMy34o8txkL6UlME1stWsUlRpTac6UE9Bx9gdJvnDXbIKhOJqBLX17fBjmposdrn6VTqim2w==
+"@element-plus/icons-vue@^1.1.3":
+  version "1.1.3"
+  resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-1.1.3.tgz#e19dc155918ebf55c601acee8079b4108f363b2b"
+  integrity sha512-eaL3acguwQMmQcGssYtzDYJLgXwXae7zpKuA6uXCtz1wrEMVtN+T3u5tY+p7FzZ+2d/R9tzT/rq0EHmjKs33XQ==
 
 "@element-plus/icons@^0.0.11":
   version "0.0.11"