liyongli 2 years ago
parent
commit
da138ef998

+ 3 - 2
package.json

@@ -3,13 +3,14 @@
   "version": "0.1.0",
   "private": true,
   "scripts": {
-    "serve": "vue-cli-service serve",
-    "build": "vue-cli-service build",
+    "serve": "set NODE_OPTIONS=--openssl-legacy-provider & vue-cli-service serve",
+    "build": "set NODE_OPTIONS=--openssl-legacy-provider & vue-cli-service build",
     "lint": "vue-cli-service lint",
     "preinstall": "npx pm-keeper yarn@1.22.10"
   },
   "dependencies": {
     "core-js": "^3.6.5",
+    "echarts": "^5.3.2",
     "element-ui": "^2.15.6",
     "sha256": "^0.2.0",
     "vant": "^2.12.37",

+ 1 - 1
public/index.html

@@ -4,7 +4,7 @@
 <head>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0;user-scalable=0;" />
+    <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
     <!-- <script src="//cdn.jsdelivr.net/npm/eruda"></script>
     <script>

+ 18 - 9
src/api/index.js

@@ -134,7 +134,7 @@ export function getAPPUserDetail(data) {
   return requestOrther({
     url: "/seeyon/rest/orgMember",
     method: "GET",
-    data
+    data,
   });
 }
 
@@ -166,11 +166,20 @@ export function upRule(data) {
  * 菜单列表
  * @returns {AjaxPromise}
  */
- export function menulist(data) {
-    return ajax({
-      urlType: 'menuURL',
-      url: "/menu/recently",
-      method: "GET",
-      data,
-    });
-  }
+export function menulist(data) {
+  return ajax({
+    urlType: "menuURL",
+    url: "/menu/recently",
+    method: "GET",
+    data,
+  });
+}
+
+export function jsonDataRanking(data) {
+  return ajax({
+    urlType: "dataJSON",
+    url: `/data/oa-rank/${data.url}.json`,
+    method: "GET",
+    data: data.data || undefined,
+  });
+}

+ 2 - 1
src/config/index.js

@@ -2,6 +2,7 @@ export default {
     base:{
         defaultURl: "http://47.108.249.49:9001",
         M3BaseURL: "http://113.142.79.115:8081",
-        menuURL: "http://47.108.249.49:9002"
+        menuURL: "http://47.108.249.49:9002",
+        dataJSON: "https://djweb.smcic.net"
     }
 }

+ 7 - 3
src/main.js

@@ -1,13 +1,17 @@
 import Vue from "vue";
 import App from "./App.vue";
 import router from "./router";
-import ElementUI from 'element-ui';
-import 'element-ui/lib/theme-chalk/index.css';
+import ElementUI from "element-ui";
+import "element-ui/lib/theme-chalk/index.css";
 
 Vue.use(ElementUI);
 
 Vue.config.productionTip = false;
-document.title = "美发预约系统";
+
+router.beforeEach((to, from, next) => {
+  if (to.meta.title) document.title = to.meta.title;
+  next();
+});
 
 new Vue({
   router,

+ 34 - 4
src/router/index.js

@@ -8,36 +8,66 @@ const routes = [
       path: "/",
       name: "Queue",
       component: () => import("../views/Queue.vue"),
+      meta: {
+          title: '美发预约系统'
+      }
     },
     {
       path: "/apply",
       name: "Apply",
       component: () => import("../views/Apply.vue"),
+      meta: {
+          title: '美发预约系统'
+      }
     },
     {
       path: "/applyreal",
       name: "Applyreal",
       component: () => import("../views/Applyreal.vue"),
+      meta: {
+          title: '美发预约系统'
+      }
     },
     {
       path: "/appointment",
       name: "Appointment",
-      component: Appointment
+      component: Appointment,
+      meta: {
+          title: '美发预约系统'
+      }
     },
     {
         path: '/actionSheet',
         name: "ActionSheet",
-        component: () => import("../views/ActionSheet.vue")
+        component: () => import("../views/ActionSheet.vue"),
+        meta: {
+            title: '美发预约系统'
+        }
     },
     {
         path: '/menuDiet',
         name: "MenuDiet",
-        component: () => import("../views/MenuDiet.vue")
+        component: () => import("../views/MenuDiet.vue"),
+        meta: {
+            title: '菜单'
+        }
     },
     {
         path: '/pushMenu',
         name: "PushMenu",
-        component: () => import("../views/PushMenu.vue")
+        component: () => import("../views/PushMenu.vue"),
+        meta: {
+            title: '添加菜单'
+        }
+    },
+    // 排行
+    {
+        path: '/ranking',
+        name: "Ranking",
+        component: () => import("../views/ranking/index.vue"),
+        meta: {
+            title: '排行'
+        }
     }
 ];
 // http://djweb.smcic.net/hair/index.html#/appointment

+ 27 - 0
src/utils/echarts.js

@@ -0,0 +1,27 @@
+import * as echarts from "echarts/core";
+import { RadarChart } from "echarts/charts";
+import { TitleComponent } from "echarts/components";
+import {
+  TooltipComponent,
+  GridComponent,
+  LegendComponent,
+  DatasetComponent,
+  TransformComponent,
+} from "echarts/components";
+import { LabelLayout, UniversalTransition } from "echarts/features";
+import { SVGRenderer } from "echarts/renderers";
+
+echarts.use([
+  TitleComponent,
+  TooltipComponent,
+  GridComponent,
+  LegendComponent,
+  DatasetComponent,
+  TransformComponent,
+  LabelLayout,
+  UniversalTransition,
+  RadarChart,
+  SVGRenderer,
+]);
+
+export default echarts;

+ 15 - 1
src/utils/request.js

@@ -15,6 +15,11 @@ function getdata(data) {
 export default function (ori) {
   let baseurl = config.base[ori.urlType || "defaultURl"];
   let url = baseurl + ori.url;
+  Toast.loading({
+    message: '加载中...',
+    forbidClick: true,
+    duration: 0
+  });
   return new Promise((resolve, reject) => {
     var xhttp;
     if (window.XMLHttpRequest) xhttp = new XMLHttpRequest();
@@ -32,9 +37,18 @@ export default function (ori) {
     method === "GET" ? xhttp.send() : xhttp.send(JSON.stringify(ori.data));
     xhttp.onreadystatechange = function () {
       if (this.readyState != 4) return;
+      Toast.clear()
+      if (this.status === 404) {
+        Toast("请求失败 " + this.status);
+        reject(this.status);
+        return;
+      }
       if (this.status != 200) {
+        console.log("-:", this.responseText);
         let t = JSON.parse(this.responseText || "{}");
-        return Toast(t.message || "请求失败 " + this.status);
+        Toast(t.message || "请求失败 " + this.status);
+        reject(t.message);
+        return;
       }
       let data = {};
       try {

+ 6 - 0
src/utils/requestOrther.js

@@ -17,6 +17,11 @@ export default function (ori) {
   let url = baseurl + ori.url;
   return new Promise((resolve, reject) => {
     var xhttp;
+    Toast.loading({
+      message: '加载中...',
+      forbidClick: true,
+      duration: 0
+    });
     if (window.XMLHttpRequest) xhttp = new XMLHttpRequest();
     else if (window.ActiveXObject)
       xhttp = new window.ActiveXObject("Microsoft.XMLHTTP");
@@ -33,6 +38,7 @@ export default function (ori) {
     method === "GET" ? xhttp.send() : xhttp.send(JSON.stringify(ori.data));
     xhttp.onreadystatechange = function () {
       if (this.readyState != 4) return;
+      Toast.clear()
       if (this.status != 200) return Toast("请求失败 " + this.status);
       let data = {};
       try {

+ 169 - 0
src/views/ranking/components/moveClient.vue

@@ -0,0 +1,169 @@
+<template>
+  <div class="move-client">
+    <van-tabs v-model="select" @change="change">
+      <van-tab
+        v-for="(item, i) in platfrom"
+        :key="item.platfrom"
+        :title="item.name"
+      >
+        <div class="main">
+          <div class="item">
+            <van-row>
+              <van-col span="3" class="td"> 排名 </van-col>
+              <van-col span="13" class="td"> 账号 </van-col>
+              <van-col span="8" class="td"> 传播量 </van-col>
+            </van-row>
+          </div>
+          <van-row
+            class="item"
+            v-for="(v, o) in platfromData[i] || []"
+            :key="v.name"
+          >
+            <van-col
+              span="3"
+              :class="{ td: true }"
+              :style="{ color: color[o] || '#000', fontWeight: 600 }"
+            >
+              {{ o + 1 }}
+            </van-col>
+            <van-col span="13" :class="{ td: true }">
+              {{ v.nick_name }}
+            </van-col>
+            <van-col span="8" :class="{ td: true }" style="color: #e1a74f">
+              {{ formatNum(v.read_count) }}
+            </van-col>
+          </van-row>
+        </div>
+      </van-tab>
+    </van-tabs>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+import {
+  Tab as vanTab,
+  Tabs as vanTabs,
+  Col as vanCol,
+  Row as vanRow,
+} from "vant";
+import "vant/lib/tab/style/index";
+import "vant/lib/tabs/style/index";
+import "vant/lib/col/style/index";
+import "vant/lib/row/style/index";
+// import {} from "../utils/tool";
+import { jsonDataRanking } from "../../../api/index";
+
+export default {
+  name: "move-client",
+  data() {
+    return {
+      setact: false,
+      select: 0,
+      platfrom: [],
+      platfromData: {},
+      color: ["#ff0036", "#ff9b00", "#ffcc00"],
+    };
+  },
+  props: {
+    active: Boolean,
+  },
+  watch: {
+    active(n) {
+      if (!n || this.setact) return;
+      this.init();
+    },
+  },
+  mounted() {
+    if (this.setact) return;
+    this.init();
+  },
+  computed: {},
+  methods: {
+    init() {
+      //   初始
+      this.setact = true;
+      jsonDataRanking({
+        url: ["platform", this.$route.query.date].join("-"),
+      })
+        .then(res => {
+          this.platfrom = res || [];
+          this.getData();
+          this.$emit("start", {
+            status: 200,
+          });
+        })
+        .catch(err => {
+          this.$emit("start", {
+            status: err,
+          });
+        });
+    },
+    formatNum(num) {
+      if (isNaN(num)) return 0;
+      if (num > 100000000) return (num / 100000000).toFixed(2) - 0 + "亿";
+      if (num > 10000) return (num / 10000).toFixed(2) - 0 + "万";
+      return num;
+    },
+    getData() {
+      jsonDataRanking({
+        url: [this.platfrom[this.select].platform, this.$route.query.date].join(
+          "_"
+        ),
+      })
+        .then(res => {
+          const p = JSON.parse(JSON.stringify(this.platfromData));
+          p[this.select] = res.data || [];
+          this.platfromData = p;
+          this.$emit("start", {
+            status: 200,
+          });
+        })
+        .catch(err => {
+          this.$emit("start", {
+            status: err,
+          });
+        });
+    },
+    change() {
+      return this.getData();
+    },
+  },
+  beforeUnmount() {},
+  components: {
+    vanTabs,
+    vanTab,
+    vanCol,
+    vanRow,
+  },
+};
+</script>
+
+<style scoped>
+.move-client {
+  font-size: 15px;
+}
+.td {
+  text-align: center;
+  line-height: 2.5em;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.item {
+  overflow: hidden;
+  border-radius: 3px;
+  margin-top: 11px;
+  background-color: #f5f6f8;
+}
+.main {
+  padding: 3px;
+  overflow: auto;
+  height: calc(100vh - 130px);
+}
+</style>
+<style>
+.move-client .van-tab {
+  font-size: 16px;
+}
+</style>

+ 253 - 0
src/views/ranking/components/tvList.vue

@@ -0,0 +1,253 @@
+<template>
+  <div class="tv-list">
+    <div class="itemHead">
+      <van-row>
+        <van-col span="3" class="td"> 排名 </van-col>
+        <van-col span="13" class="td"> 栏目 </van-col>
+        <van-col span="6" class="td"> 收视率 </van-col>
+        <van-col span="2" class="td"></van-col>
+      </van-row>
+    </div>
+    <van-row
+      @click="() => showPopup(o)"
+      class="item"
+      v-for="(v, o) in platfromData || []"
+      :key="v.name"
+    >
+      <van-col
+        span="3"
+        :class="{ td: true }"
+        :style="{ color: color[o] || '#000', fontWeight: 600 }"
+      >
+        {{ o + 1 }}
+      </van-col>
+      <van-col span="13" :class="{ td: true }">
+        {{ v.schedulename }}
+      </van-col>
+      <van-col span="6" :class="{ td: true }" style="color: #e1a74f">
+        {{ formatNum(v.watchrate) }}%
+      </van-col>
+      <van-col span="2" class="td">
+        <van-icon name="arrow" />
+      </van-col>
+    </van-row>
+    <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">
+            返回
+            <van-icon name="share-o" />
+          </div>
+        </van-cell>
+        <div style="padding: 0 16px">
+          <van-row>
+            <van-col span="6" class="td"> 收视率: </van-col>
+            <van-col span="6" class="td"> {{ ratios.watchrate }}% </van-col>
+            <van-col span="6" class="td"> 市占率: </van-col>
+            <van-col span="6" class="td">{{ ratios.occrate }}% </van-col>
+          </van-row>
+          <div ref="pie"></div>
+        </div>
+      </div>
+    </van-popup>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+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 { jsonDataRanking } from "../../../api/index";
+
+import echarts from "../../../utils/echarts";
+
+export default {
+  name: "tv-list",
+  data() {
+    return {
+      setact: false,
+      show: false,
+      charts: undefined,
+      ratios: [],
+      platfromData: [],
+      color: ["#ff0036", "#ff9b00", "#ffcc00"],
+    };
+  },
+  props: {
+    active: Boolean,
+  },
+  watch: {
+    active(n) {
+      if (!n || this.setact) return;
+      this.init();
+    },
+  },
+  mounted() {
+    if (this.setact) return;
+    this.init();
+  },
+  computed: {},
+  methods: {
+    init() {
+      //   初始
+      this.setact = true;
+      this.getData();
+    },
+    formatNum(num) {
+      if (isNaN(num)) return 0;
+      return Number(num).toFixed(4) - 0;
+    },
+    showPopup(i) {
+      this.show = true;
+      this.ratios = this.platfromData[i] || [];
+      console.log(this.ratios);
+      this.$nextTick(() => {
+        this.upEcharts();
+      });
+    },
+    upEcharts() {
+      if (!this.ratios.ratios || !this.ratios.ratios.length) {
+        this.echarts && this.echarts.dispose && this.echarts.dispose();
+        return;
+      }
+      if (!this.echarts) {
+        const doc = document.body;
+        const width = doc.offsetWidth - 32;
+        this.echarts = echarts.init(
+          this.$refs.pie,
+          {},
+          {
+            width,
+            height: width,
+          }
+        );
+      }
+      const key = [],
+        value = [];
+      let max = 0;
+      this.ratios.ratios.map(v => {
+        let val = (v.value * 100).toFixed(2) - 0;
+        key.push(v.name);
+        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,
+                },
+              },
+            ],
+          },
+        ],
+      });
+    },
+    getData() {
+      jsonDataRanking({
+        url: ["tv", this.$route.query.date].join("-"),
+      })
+        .then(res => {
+          this.platfromData = res || [];
+          this.$emit("start", {
+            status: 200,
+          });
+        })
+        .catch(err => {
+          this.$emit("start", {
+            status: err,
+          });
+        });
+    },
+  },
+  beforeUnmount() {},
+  components: {
+    vanCol,
+    vanRow,
+    vanIcon,
+    vanPopup,
+    vanCell,
+  },
+};
+</script>
+
+<style scoped>
+.tv-list {
+  font-size: 15px;
+  padding: 5px 5px 0 5px;
+}
+.td {
+  text-align: center;
+  line-height: 2.5em;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.item {
+  overflow: hidden;
+  border-radius: 3px;
+  margin-top: 11px;
+  background-color: #f5f6f8;
+}
+.itemHead {
+  margin-top: 0;
+  background-color: #f5f6f8;
+}
+.main {
+  padding: 0.5em;
+}
+</style>
+<style>
+.tv-list .van-tab {
+  font-size: 16px;
+}
+</style>

+ 116 - 0
src/views/ranking/index.vue

@@ -0,0 +1,116 @@
+<template>
+  <div class="ranking">
+    <div class="tip">数据来源于集团大数据平台</div>
+    <div v-show="errorInformation.text === ''">
+      <keep-alive>
+        <component
+          :active="errorInformation.text === '' && active === 1"
+          @start="comError"
+          :is="tabs[active].com"
+        />
+      </keep-alive>
+    </div>
+    <van-empty
+      v-show="errorInformation.text !== ''"
+      :image="errorInformation.type"
+      :description="errorInformation.text"
+    />
+    <van-tabbar v-model="active" active-color="#ee0a24" @change="change">
+      <van-tabbar-item
+        v-for="item in tabs"
+        :key="item.icon"
+        :icon="item.icon"
+        >{{ item.name }}</van-tabbar-item
+      >
+    </van-tabbar>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+import {
+  Tabbar as vanTabbar,
+  TabbarItem as vanTabbarItem,
+  Empty as vanEmpty,
+} from "vant";
+import "vant/lib/empty/style/index";
+import "vant/lib/tabbar/style/index";
+import "vant/lib/tabbar-item/style/index";
+
+import tvList from "./components/tvList.vue";
+import moveClient from "./components/moveClient.vue";
+
+// import {} from "../utils/tool";
+// import {} from "../api/index";
+export default {
+  name: "Ranking",
+
+  data() {
+    return {
+      active: window.sessionStorage
+        ? sessionStorage.getItem("active") - 0 || 0
+        : 0,
+      tabs: [
+        {
+          name: "大屏排行",
+          icon: "tv-o",
+          com: "tvList",
+        },
+        {
+          name: "新媒体排行",
+          icon: "bar-chart-o",
+          com: "moveClient",
+        },
+      ],
+      errorInformation: {
+        text: "",
+        type: "search",
+      },
+    };
+  },
+  mounted() {},
+  computed: {},
+  methods: {
+    change() {
+      if (window.sessionStorage) sessionStorage.setItem("active", this.active);
+    },
+    comError(data) {
+      let p = {
+        text: "",
+        type: "search",
+      };
+      if (data.status === 200) {
+        this.errorInformation = p;
+        return;
+      }
+      p.text = data.msg || "网络错误";
+      p.type = "network";
+      this.errorInformation = p;
+    },
+  },
+  beforeUnmount() {},
+  components: {
+    vanTabbar,
+    vanTabbarItem,
+    vanEmpty,
+    tvList,
+    moveClient,
+  },
+};
+</script>
+
+<style scoped>
+.ranking {
+  width: 100vw;
+  height: 100vh;
+  padding-bottom: 50px;
+  box-sizing: border-box;
+  background-color: #fff;
+}
+.tip{
+    text-align: center;
+    font-size: 12px;
+    color: #999;
+    padding: 5px 0;
+}
+</style>

+ 20 - 0
yarn.lock

@@ -3346,6 +3346,14 @@ ecc-jsbn@~0.1.1:
     jsbn "~0.1.0"
     safer-buffer "^2.1.0"
 
+echarts@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.npmmirror.com/echarts/-/echarts-5.3.2.tgz#0a7b3be8c48a48b2e7cb1b82121df0c208d42d2c"
+  integrity sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==
+  dependencies:
+    tslib "2.3.0"
+    zrender "5.3.1"
+
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz"
@@ -7933,6 +7941,11 @@ ts-pnp@^1.1.6:
   resolved "https://registry.npmmirror.com/ts-pnp/download/ts-pnp-1.2.0.tgz"
   integrity sha1-pQCtCEsHmPHDBxrzkeZZEshrypI=
 
+tslib@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
+  integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
+
 tslib@^1.9.0:
   version "1.14.1"
   resolved "https://registry.nlark.com/tslib/download/tslib-1.14.1.tgz"
@@ -8663,3 +8676,10 @@ yorkie@^2.0.0:
     is-ci "^1.0.10"
     normalize-path "^1.0.0"
     strip-indent "^2.0.0"
+
+zrender@5.3.1:
+  version "5.3.1"
+  resolved "https://registry.npmmirror.com/zrender/-/zrender-5.3.1.tgz#fa8e63ac7e719cfd563831fe8c42a9756c5af384"
+  integrity sha512-7olqIjy0gWfznKr6vgfnGBk7y4UtdMvdwFmK92vVQsQeDPyzkHW1OlrLEKg6GHz1W5ePf0FeN1q2vkl/HFqhXw==
+  dependencies:
+    tslib "2.3.0"