Bläddra i källkod

feat: 门户流量模块开发

周玉环 1 dag sedan
förälder
incheckning
a81271c074

+ 1 - 0
xinkeaboard-admin/package.json

@@ -38,6 +38,7 @@
     "bizcharts-plugin-slider": "^2.1.1-beta.1",
     "classnames": "^2.2.6",
     "dva": "^2.4.0",
+    "echarts": "^6.0.0",
     "enquire-js": "^0.2.1",
     "hash.js": "^1.1.5",
     "jquery": "^3.5.1",

+ 30 - 56
xinkeaboard-admin/pnpm-lock.yaml

@@ -35,6 +35,9 @@ importers:
       dva:
         specifier: ^2.4.0
         version: 2.4.1(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
+      echarts:
+        specifier: ^6.0.0
+        version: 6.0.0
       enquire-js:
         specifier: ^0.2.1
         version: 0.2.1
@@ -254,7 +257,7 @@ importers:
         version: 1.1.8
       umi-plugin-react:
         specifier: ^1.2.0
-        version: 1.15.9(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react-router@5.1.2(react@16.14.0))(react@16.14.0)(redbox-react@1.6.0(react-dom@16.14.0(react@16.14.0))(react@16.14.0))
+        version: 1.15.9(@babel/core@7.4.5)(react-dom@16.14.0(react@16.14.0))(react-router@5.1.2(react@16.14.0))(react@16.14.0)(redbox-react@1.6.0(react-dom@16.14.0(react@16.14.0))(react@16.14.0))
     optionalDependencies:
       puppeteer:
         specifier: ^1.10.0
@@ -3742,6 +3745,9 @@ packages:
   ecc-jsbn@0.1.2:
     resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
 
+  echarts@6.0.0:
+    resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==}
+
   editions@2.3.1:
     resolution: {integrity: sha512-ptGvkwTvGdGfC0hfhKg0MT+TRLRKGtUiWGBInxOm5pz7ssADezahjCUaYuZ8Dr+C05FW0AECIIPt4WBxVINEhA==}
     engines: {node: '>=0.8'}
@@ -7803,7 +7809,7 @@ packages:
   puppeteer@1.20.0:
     resolution: {integrity: sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ==}
     engines: {node: '>=6.4.0'}
-    deprecated: < 22.8.2 is no longer supported
+    deprecated: < 24.9.0 is no longer supported
 
   q@1.5.1:
     resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==}
@@ -9775,6 +9781,9 @@ packages:
   tslib@1.14.1:
     resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
 
+  tslib@2.3.0:
+    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
+
   tslib@2.8.1:
     resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
 
@@ -10658,6 +10667,9 @@ packages:
   yup@0.27.0:
     resolution: {integrity: sha512-v1yFnE4+u9za42gG/b/081E7uNW9mUj3qtkmelLbW5YPROZzSH/KUUyJu9Wt8vxFJcT9otL/eZopS0YK1L5yPQ==}
 
+  zrender@6.0.0:
+    resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==}
+
   zscroller@0.4.8:
     resolution: {integrity: sha512-G5NiNLKx2+QhhvZi2yV1jjVXY50otktxkseX2hG2N/eixohOUk0AY8ZpbAxNqS9oJS/NxItCsowupy2tsXxAMw==}
 
@@ -13885,26 +13897,14 @@ snapshots:
 
   babel-plugin-react-require@3.0.0: {}
 
-  babel-plugin-styled-components@2.1.4(@babel/core@7.28.0)(styled-components@4.4.1(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0))(supports-color@5.5.0):
-    dependencies:
-      '@babel/helper-annotate-as-pure': 7.27.3
-      '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0)
-      '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0)
-      lodash: 4.17.21
-      picomatch: 2.3.1
-      styled-components: 4.4.1(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
-    transitivePeerDependencies:
-      - '@babel/core'
-      - supports-color
-
-  babel-plugin-styled-components@2.1.4(@babel/core@7.4.5)(styled-components@4.4.1(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0))(supports-color@5.5.0):
+  babel-plugin-styled-components@2.1.4(@babel/core@7.4.5)(styled-components@4.4.1(@babel/core@7.4.5)(react-dom@16.14.0(react@16.14.0))(react@16.14.0))(supports-color@5.5.0):
     dependencies:
       '@babel/helper-annotate-as-pure': 7.27.3
       '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0)
       '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.4.5)
       lodash: 4.17.21
       picomatch: 2.3.1
-      styled-components: 4.4.1(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
+      styled-components: 4.4.1(@babel/core@7.4.5)(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
     transitivePeerDependencies:
       - '@babel/core'
       - supports-color
@@ -15922,6 +15922,11 @@ snapshots:
       jsbn: 0.1.1
       safer-buffer: 2.1.2
 
+  echarts@6.0.0:
+    dependencies:
+      tslib: 2.3.0
+      zrender: 6.0.0
+
   editions@2.3.1:
     dependencies:
       errlop: 2.2.0
@@ -23220,33 +23225,13 @@ snapshots:
 
   style-utils@0.2.1: {}
 
-  styled-components@4.4.1(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0):
-    dependencies:
-      '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0)
-      '@babel/traverse': 7.28.0(supports-color@5.5.0)
-      '@emotion/is-prop-valid': 0.8.8
-      '@emotion/unitless': 0.7.5
-      babel-plugin-styled-components: 2.1.4(@babel/core@7.28.0)(styled-components@4.4.1(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0))(supports-color@5.5.0)
-      css-to-react-native: 2.3.2
-      memoize-one: 5.2.1
-      merge-anything: 2.4.4
-      prop-types: 15.8.1
-      react: 16.14.0
-      react-dom: 16.14.0(react@16.14.0)
-      react-is: 16.13.1
-      stylis: 3.5.4
-      stylis-rule-sheet: 0.0.10(stylis@3.5.4)
-      supports-color: 5.5.0
-    transitivePeerDependencies:
-      - '@babel/core'
-
   styled-components@4.4.1(@babel/core@7.4.5)(react-dom@16.14.0(react@16.14.0))(react@16.14.0):
     dependencies:
       '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0)
       '@babel/traverse': 7.28.0(supports-color@5.5.0)
       '@emotion/is-prop-valid': 0.8.8
       '@emotion/unitless': 0.7.5
-      babel-plugin-styled-components: 2.1.4(@babel/core@7.4.5)(styled-components@4.4.1(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0))(supports-color@5.5.0)
+      babel-plugin-styled-components: 2.1.4(@babel/core@7.4.5)(styled-components@4.4.1(@babel/core@7.4.5)(react-dom@16.14.0(react@16.14.0))(react@16.14.0))(supports-color@5.5.0)
       css-to-react-native: 2.3.2
       memoize-one: 5.2.1
       merge-anything: 2.4.4
@@ -23765,6 +23750,8 @@ snapshots:
 
   tslib@1.14.1: {}
 
+  tslib@2.3.0: {}
+
   tslib@2.8.1: {}
 
   tslint-config-prettier@1.18.0: {}
@@ -24123,7 +24110,7 @@ snapshots:
       umi-utils: 1.4.2
       url-polyfill: 1.1.3
 
-  umi-plugin-react@1.15.9(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react-router@5.1.2(react@16.14.0))(react@16.14.0)(redbox-react@1.6.0(react-dom@16.14.0(react@16.14.0))(react@16.14.0)):
+  umi-plugin-react@1.15.9(@babel/core@7.4.5)(react-dom@16.14.0(react@16.14.0))(react-router@5.1.2(react@16.14.0))(react@16.14.0)(redbox-react@1.6.0(react-dom@16.14.0(react@16.14.0))(react@16.14.0)):
     dependencies:
       antd: 3.26.20(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
       antd-mobile: 2.3.4(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
@@ -24140,7 +24127,7 @@ snapshots:
       umi-plugin-locale: 2.11.7(react@16.14.0)
       umi-plugin-polyfills: 1.4.2
       umi-plugin-routes: 1.8.9
-      umi-plugin-ui: 1.5.3(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
+      umi-plugin-ui: 1.5.3(@babel/core@7.4.5)(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
       umi-utils: 1.7.3
       webpack: 4.41.1
       workbox-webpack-plugin: 3.6.3(webpack@4.41.1)
@@ -24160,23 +24147,6 @@ snapshots:
 
   umi-plugin-routes@1.8.9: {}
 
-  umi-plugin-ui@1.5.3(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0):
-    dependencies:
-      got: 9.6.0
-      immer: 5.3.6
-      is-mobile: 2.1.0
-      mkdirp: 0.5.6
-      sockjs-client: 1.3.0(supports-color@6.1.0)
-      styled-components: 4.4.1(@babel/core@7.28.0)(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
-      umi-ui-tasks: 1.3.12
-    transitivePeerDependencies:
-      - '@babel/core'
-      - bufferutil
-      - react
-      - react-dom
-      - supports-color
-      - utf-8-validate
-
   umi-plugin-ui@1.5.3(@babel/core@7.4.5)(react-dom@16.14.0(react@16.14.0))(react@16.14.0):
     dependencies:
       got: 9.6.0
@@ -25300,6 +25270,10 @@ snapshots:
       synchronous-promise: 2.0.17
       toposort: 2.0.2
 
+  zrender@6.0.0:
+    dependencies:
+      tslib: 2.3.0
+
   zscroller@0.4.8:
     dependencies:
       babel-runtime: 6.26.0

+ 11 - 5
xinkeaboard-admin/src/pages/statistics/bigscreen/components/PortalTraffic.js

@@ -1,20 +1,26 @@
 import React from "react";
 import { connect } from "dva";
 import RadioButtonGroup from "./RadioButtonGroup";
+import TrafficLineChart from "./TrafficLineChart";
+import PanelNav from "./PanelNav";
 import styles from "../styles/portal_traffic.less";
 
 const PortalTraffic = ({}) => {
   const options = [
     { label: "日", value: "day" },
     { label: "月", value: "month" },
-    { label: "年", value: "year" },
   ];
 
   return (
-    <div class="traffic-header">
-      <panel-nav title="门户流量" />
-      <div class="traffic-header-conditions">
-        <RadioButtonGroup options={options} />
+    <div className={styles.traffic}>
+      <div className={styles.traffic_header}>
+        <PanelNav title="门户流量" />
+        <div className={styles.traffic_header_conditions}>
+          <RadioButtonGroup options={options} value="day" />
+        </div>
+      </div>
+      <div className={styles.traffic_content}>
+        <TrafficLineChart />
       </div>
     </div>
   );

+ 15 - 17
xinkeaboard-admin/src/pages/statistics/bigscreen/components/RadioButtonGroup.js

@@ -1,26 +1,24 @@
-import React from 'react';
-import styles from '../styles/radio_button_group.less';
+import React from "react";
+import styles from "../styles/radio_button_group.less";
 
 class RadioButtonGroup extends React.Component {
-  static defaultProps = {
-    options: [
-      { label: '日', value: 'day' },
-      { label: '月', value: 'month' },
-      { label: '年', value: 'year' },
-    ],
-    value: 'day',
-    onChange: () => {},
-  };
+  constructor(props) {
+    super(props);
+    this.state = {
+      options: props.options || [],
+      value:
+        props.value || (props.options.length > 0 ? props.options[0].value : ""),
+    };
+  }
 
   handleSelect = (val) => {
-    const { value, onChange } = this.props;
-    if (val !== value) {
-      onChange(val);
-    }
+    const { onChange } = this.props;
+    this.setState({ value: val });
+    onChange && onChange(val);
   };
 
   render() {
-    const { options, value } = this.props;
+    const { options, value } = this.state;
 
     return (
       <div className={styles.screenRadioGroup}>
@@ -28,7 +26,7 @@ class RadioButtonGroup extends React.Component {
           <div
             key={index}
             className={`${styles.customRadioButton} ${
-              value === item.value ? styles.checked : ''
+              value === item.value ? styles.checked : ""
             }`}
             onClick={() => this.handleSelect(item.value)}
           >

+ 162 - 0
xinkeaboard-admin/src/pages/statistics/bigscreen/components/TrafficLineChart.js

@@ -0,0 +1,162 @@
+import React from "react";
+import styles from "../styles/traffic_line.less";
+import * as echarts from "echarts";
+
+class LineChart extends React.Component {
+  constructor(props) {
+    super(props);
+    this.chartRef = React.createRef();
+    this.chartInstance = null;
+  }
+
+  componentDidMount() {
+    this.initChart();
+  }
+
+  componentWillUnmount() {
+    if (this.chartInstance) {
+      this.chartInstance.dispose();
+    }
+  }
+
+  initChart() {
+    this.chartInstance = echarts.init(this.chartRef.current);
+
+    const option = {
+      backgroundColor: "transparent",
+      tooltip: { trigger: "axis" },
+      legend: {
+        top: 10,
+        itemGap: 30,
+        textStyle: { color: "#fff" },
+        data: [
+          "海外门户浏览量",
+          "海外门户访客数",
+          "分销门户浏览量",
+          "分销门户访客数",
+        ],
+      },
+      grid: {
+        left: "5%",
+        right: "5%",
+        bottom: "10%",
+        containLabel: true,
+      },
+      xAxis: {
+        type: "category",
+        boundaryGap: true,
+        axisLine: { lineStyle: { color: "#666" } },
+        axisLabel: { color: "#fff" },
+        splitLine: {
+          // x 轴竖直方向虚线
+          show: true,
+          lineStyle: {
+            type: "dashed",
+            color: "#ccc",
+          },
+        },
+        data: [
+          "2025-07-08",
+          "2025-07-09",
+          "2025-07-10",
+          "2025-07-11",
+          "2025-07-12",
+          "2025-07-13",
+        ],
+      },
+      yAxis: {
+        type: "value",
+        axisLine: { show: false },
+        axisLabel: { color: "#fff" },
+        splitLine: {
+          // y 轴竖直方向虚线
+          show: true,
+          lineStyle: {
+            type: "dashed",
+            color: "#ccc",
+          },
+        },
+      },
+      series: [
+        {
+          name: "海外门户浏览量",
+          type: "line",
+          smooth: false,
+          showSymbol: true,
+          symbolSize: 10,
+          symbol: "circle",
+          lineStyle: { width: 2 },
+          itemStyle: {
+            color: "#7ECEF4",
+          },
+          //   areaStyle: {
+          //     color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          //       { offset: 0, color: "rgba(101,198,255,0.4)" },
+          //       { offset: 1, color: "rgba(101,198,255,0)" },
+          //     ]),
+          //   },
+          data: [8000, 6000, 7500, 6200, 7800, 7000],
+        },
+        {
+          name: "海外门户访客数",
+          type: "line",
+          smooth: false,
+          symbolSize: 10,
+          showSymbol: true,
+          symbol: "circle",
+          itemStyle: { color: "#036EB8" },
+          lineStyle: { width: 2 },
+          //   areaStyle: {
+          //     color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          //       { offset: 0, color: "rgba(79,157,255,0.4)" },
+          //       { offset: 1, color: "rgba(79,157,255,0)" },
+          //     ]),
+          //   },
+          data: [5000, 2800, 3600, 4000, 6000, 4200],
+        },
+        {
+          name: "分销门户浏览量",
+          type: "line",
+          smooth: false,
+          showSymbol: true,
+          symbolSize: 10,
+          symbol: "circle",
+          itemStyle: { color: "#004E9D" },
+          lineStyle: { width: 2 },
+          //   areaStyle: {
+          //     color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          //       { offset: 0, color: "rgba(115,79,255,0.4)" },
+          //       { offset: 1, color: "rgba(115,79,255,0)" },
+          //     ]),
+          //   },
+          data: [2000, 1800, 1000, 0, 4500, 1200],
+        },
+        {
+          name: "分销门户访客数",
+          type: "line",
+          smooth: false,
+          showSymbol: true,
+          symbolSize: 10,
+          symbol: "circle",
+          itemStyle: { color: "#4032A6" },
+          lineStyle: { width: 2 },
+          //   areaStyle: {
+          //     color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          //       { offset: 0, color: "rgba(155,95,255,0.4)" },
+          //       { offset: 1, color: "rgba(155,95,255,0)" },
+          //     ]),
+          //   },
+          data: [1800, 1500, 600, 0, 1200, 800],
+        },
+      ],
+    };
+
+    this.chartInstance.setOption(option);
+  }
+
+  render() {
+    return <div className={styles.traffic_line} ref={this.chartRef} />;
+  }
+}
+
+export default LineChart;

+ 0 - 1
xinkeaboard-admin/src/pages/statistics/bigscreen/styles/panel_nav.less

@@ -5,5 +5,4 @@
   font-size: 28px;
   color: #ffffff;
   width: 100%;
-  height: 22px;
 }

+ 36 - 0
xinkeaboard-admin/src/pages/statistics/bigscreen/styles/portal_traffic.less

@@ -0,0 +1,36 @@
+.traffic {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.traffic_header {
+  position: relative;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  width: 100%;
+  height: 52px;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+
+  &::after {
+    content: "";
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    width: 112px;
+    height: 4px;
+    background: #06f7ff;
+  }
+}
+
+.traffic_header_conditions {
+}
+
+.traffic_content {
+  flex: 1;
+  width: 100%;
+  height: 100%;
+  padding: 20px;
+}

+ 11 - 16
xinkeaboard-admin/src/pages/statistics/bigscreen/styles/radio_button_group.less

@@ -2,33 +2,28 @@
   display: flex;
   justify-content: center;
   align-items: center;
-  margin-top: 20px;
 }
 
 .customRadioButton {
-  width: 100px;
-  height: 50px;
-  line-height: 50px;
+  width: 96px;
+  height: 32px;
+  line-height: 32px;
+  font-weight: 400;
+  font-size: 20px;
+  color: rgba(255, 255, 255, 0.6);
   text-align: center;
-  font-size: 30px;
-  color: #fff;
-  background-color: #1a1a1a;
-  border: 1px solid #4fd2dd;
-  border-right: none;
+  background: linear-gradient(180deg, rgba(0, 98, 165, 0.2) 0%, #063d76 100%);
   cursor: pointer;
-  user-select: none;
-  transition: background 0.2s, border 0.2s;
 
   &:hover {
-    background-color: #333;
-    border-color: #4fd2dd;
+    // color: #ffffff;
+    // background: linear-gradient(180deg, rgba(0, 98, 165, 0.2) 0%, #036eb8 100%);
   }
 }
 
 .checked {
-  background-color: #4fd2dd;
-  color: #fff;
-  border-color: #4fd2dd;
+  color: #ffffff;
+  background: linear-gradient(180deg, rgba(0, 98, 165, 0.2) 0%, #036eb8 100%);
 }
 
 .customRadioButton:last-child {

+ 4 - 0
xinkeaboard-admin/src/pages/statistics/bigscreen/styles/traffic_line.less

@@ -0,0 +1,4 @@
+.traffic_line {
+  width: 100%;
+  height: 100%;
+}

+ 33 - 1
xinkeaboard-admin/src/pages/statistics/models/bigscreen.js

@@ -7,6 +7,10 @@ export default {
       loading: false,
       data: {},
     },
+    portalTrafficData: {
+      loading: false,
+      data: {},
+    }
   },
 
   effects: {
@@ -20,7 +24,7 @@ export default {
         sldCommonService,
         payload,
         "get",
-        "v3/statistics/screen/analysis/data/summary"
+        "v3/statistics/screen/analysis/dataSummary"
       );
       yield put({
         type: "setOverviewData",
@@ -28,6 +32,25 @@ export default {
       });
     },
 
+    // 门户流量
+    *load_portal_traffic({ payload }, { call, put }) {
+      yield put({
+        type: "setPortalTrafficData",
+        payload: { loading: true },
+      });
+      const response = yield call(
+        sldCommonService,
+        payload,
+        "get",
+        "v3/statistics/screen/analysis/flowTrend-"
+      );
+      yield put({
+        type: "setPortalTrafficData",
+        payload: { data: response.data, loading: false },
+      });
+    },
+     
+
     // 初始化数据
     *load_data({ payload }, { put, call }) {
       // 调用 load_data
@@ -47,6 +70,15 @@ export default {
         },
       };
     },
+    setPortalTrafficData(state, { payload }) {
+      return {
+        ...state,
+        portalTrafficData: {
+          ...state.portalTrafficData, // 保留 config 里其他字段
+          ...payload, // 覆盖需要更新的字段
+        },
+      };
+    }
   },
 
   subscriptions: {