Bläddra i källkod

feat: 门户搜索关键词排行模块开发

周玉环 2 dagar sedan
förälder
incheckning
93caba8cef

+ 51 - 40
xinkeaboard-admin/src/components/WordCloud/index.js

@@ -1,49 +1,60 @@
 import React, { Component } from "react";
 import ReactWordCloud from "react-wordcloud";
 
-class WordCloud extends Component {
-  constructor(props) {
-    super(props);
-    this.state = {
-      words: props.words ?? [
-        { text: "Apparel", value: 1000 },
-        { text: "Fashion", value: 800 },
-        { text: "Style", value: 700 },
-        { text: "Trend", value: 600 },
-        { text: "Wear", value: 500 },
-        { text: "Outfit", value: 400 },
-        { text: "Clothing", value: 300 },
-        { text: "Textile", value: 200 },
-      ],
-      options: props.options ?? {
-        // deterministic: true,
-        spiral: "archimedean",
-        fontFamily: "sans-serif",
-        fontSizes: [30, 80], // 字体范围
-        // deterministic: true, // 尝试保持一致
-        // enableOptimizations: true, // 优化性能,避免重新布局
-        rotations: 0, // 不旋转
-        // rotationAngles: [0, 90], // 旋转角度范围
-        scale: "sqrt", // 字体缩放方式:linear、sqrt、log
-        // spiral: "rectangular", // 词云排列方式:archimedean 或 rectangular
-        padding: 20, // 单词之间的间距
-        colors: ["#fff"],
-        // colors: [
-        //   "#f87171",
-        //   "#fb923c",
-        //   "#facc15",
-        //   "#4ade80",
-        //   "#38bdf8",
-        //   "#a78bfa",
-        //   "#f472b6",
-        // ], // 多彩渐变
-        enableTooltip: false, // 鼠标悬停提示
-      },
-    };
+// 工具函数:生成渐变颜色
+function generateGradientColors(startColor, endColor, steps) {
+  function hexToRgb(hex) {
+    const r = parseInt(hex.slice(1, 3), 16);
+    const g = parseInt(hex.slice(3, 5), 16);
+    const b = parseInt(hex.slice(5, 7), 16);
+    return [r, g, b];
+  }
+
+  function rgbToHex([r, g, b]) {
+    return (
+      "#" +
+      r.toString(16).padStart(2, "0") +
+      g.toString(16).padStart(2, "0") +
+      b.toString(16).padStart(2, "0")
+    );
+  }
+
+  const [r1, g1, b1] = hexToRgb(startColor);
+  const [r2, g2, b2] = hexToRgb(endColor);
+
+  const colors = [];
+  for (let i = 0; i < steps; i++) {
+    const t = i / (steps - 1);
+    const r = Math.round(r1 + (r2 - r1) * t);
+    const g = Math.round(g1 + (g2 - g1) * t);
+    const b = Math.round(b1 + (b2 - b1) * t);
+    colors.push(rgbToHex([r, g, b]));
   }
+  return colors;
+}
 
+class WordCloud extends Component {
   render() {
-    const { words, options } = this.state;
+    const { data } = this.props;
+    // 根据索引生成 words,索引越小,value 越大
+    const words = data.map((item, idx) => ({
+      text: item,
+      value: data.length - idx // 越小索引值越大
+    }));
+
+    const colors = generateGradientColors("#7ECEF4", "#4032A6", words.length);
+
+    const options = {
+      spiral: "archimedean",
+      fontFamily: "sans-serif",
+      fontSizes: [20, 80], // 控制字体范围
+      fontWeight: 'bold',
+      rotations: 0,
+      scale: "sqrt",
+      padding: 10,
+      colors,
+      enableTooltip: false,
+    };
 
     return (
       <div style={{ width: "100%", height: "100%" }}>

+ 0 - 1
xinkeaboard-admin/src/pages/statistics/bigscreen/components/PortalTraffic.js

@@ -5,7 +5,6 @@ import RadioButtonGroup from "./RadioButtonGroup";
 import TrafficLineChart from "./TrafficLineChart";
 import PanelNav from "./PanelNav";
 import styles from "../styles/portal_traffic.less";
-import { color } from "echarts";
 
 const relation = {
   viewNumList: {

+ 58 - 0
xinkeaboard-admin/src/pages/statistics/bigscreen/components/SearchRankCloud.js

@@ -0,0 +1,58 @@
+import React from "react";
+import { Spin } from "antd";
+import { connect } from "dva";
+import RadioButtonGroup from "./RadioButtonGroup";
+import WordCloud from "@/components/WordCloud";
+import styles from "../styles/search_rank_cloud.less";
+import PanelNav from "./PanelNav";
+
+const SearchRankCloud = ({
+  data,
+  options,
+  current,
+  loading,
+  currentSite,
+  dispatch,
+}) => {
+  const onChange = (val) => {
+    dispatch({
+      type: "bigscreen/setSearchRankData",
+      payload: { current: val },
+    });
+    dispatch({
+      type: "bigscreen/load_search_rank",
+    });
+  };
+
+  const title = currentSite === "1" ? "海外" : "分销商";
+
+  const parseData = (keywords) => {
+    return keywords.map(item => decodeURIComponent(item))
+  }
+
+  return (
+    <div className={styles.search_rank}>
+      <div className={styles.search_rank_header}>
+        <PanelNav title={title + "门户搜索关键词排行"} />
+        <div className={styles.search_rank_header_conditions}>
+          <RadioButtonGroup
+            options={options}
+            label={current}
+            onChange={onChange}
+          />
+        </div>
+      </div>
+      <div className={styles.search_rank_content}>
+        <WordCloud data={parseData(data)}></WordCloud>
+      </div>
+    </div>
+  );
+};
+
+export default connect(({ bigscreen, global }) => ({
+  data: bigscreen.searchRankData.data,
+  options: bigscreen.searchRankData.options,
+  current: bigscreen.searchRankData.current,
+  loading: bigscreen.searchRankData.loading,
+  currentSite: global.currentSite,
+}))(SearchRankCloud);

+ 3 - 3
xinkeaboard-admin/src/pages/statistics/bigscreen/index.js

@@ -3,7 +3,6 @@ import { connect } from "dva";
 import styles from "./index.less";
 import PanelBlock from "./components/PanelBlock";
 import HeaderContent from "./components/HeadContent";
-import WordCloud from "@/components/WordCloud";
 import OverView from "./components/Overview";
 import PortalTraffic from "./components/PortalTraffic";
 import Enquire from "./components/Enquire";
@@ -11,6 +10,7 @@ import OverseasEnquireLocation from "./components/OverseasEnquireLocation";
 import DistributorEnquireLocation from "./components/DistributorEnquireLocation";
 import AddMemberTrend from "./components/addMemberTrend";
 import AddProductTrend from "./components/addProductTrend";
+import SearchRankCloud from "./components/SearchRankCloud";
 
 @connect(({ bigscreen }) => ({
   bigscreen,
@@ -183,8 +183,8 @@ class BigScreen extends React.Component {
 
             {/* 右侧 */}
             <div className={styles.contentPanelRight}>
-              <PanelBlock title="sdsdsd">
-                <WordCloud />
+              <PanelBlock>
+                <SearchRankCloud />
               </PanelBlock>
               <PanelBlock />
             </div>

+ 37 - 0
xinkeaboard-admin/src/pages/statistics/bigscreen/styles/search_rank_cloud.less

@@ -0,0 +1,37 @@
+.search_rank {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.search_rank_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;
+  }
+}
+
+.search_rank_header_conditions {
+}
+
+.search_rank_content {
+  flex: 1;
+  width: 100%;
+  height: 100%;
+  padding: 20px;
+}

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

@@ -91,6 +91,52 @@ export default {
       loading: false,
       data: [],
     },
+    searchRankData: {
+      loading: false,
+      data: [],
+      options: [
+        {
+          label: "昨日",
+          value: {
+            startTime:
+              moment()
+                .subtract(1, "days")
+                .format("YYYY-MM-DD") + " 00:00:00",
+            endTime:
+              moment()
+                .subtract(1, "days")
+                .format("YYYY-MM-DD") + " 23:59:59:999",
+          },
+        },
+        {
+          label: "近7日",
+          value: {
+            startTime:
+              moment()
+                .subtract(7, "days")
+                .format("YYYY-MM-DD") + " 00:00:00",
+            endTime:
+              moment()
+                .subtract(1, "days")
+                .format("YYYY-MM-DD") + " 23:59:59:999",
+          },
+        },
+        {
+          label: "近30日",
+          value: {
+            startTime:
+              moment()
+                .subtract(30, "days")
+                .format("YYYY-MM-DD") + " 00:00:00",
+            endTime:
+              moment()
+                .subtract(1, "days")
+                .format("YYYY-MM-DD") + " 23:59:59:999",
+          },
+        },
+      ],
+      current: "昨日",
+    },
   },
 
   effects: {
@@ -238,6 +284,33 @@ export default {
       });
     },
 
+    // 门户关键词搜索
+    *load_search_rank({ payload }, { put, call, select }) {
+      const current = yield select(
+        (state) => state.bigscreen.searchRankData.current
+      );
+      const options = yield select(
+        (state) => state.bigscreen.searchRankData.options
+      );
+      const condition = options.filter((item) => item.label == current)[0]
+        .value;
+      payload = { ...payload, ...condition };
+      yield put({
+        type: "setSearchRankData",
+        payload: { loading: true },
+      });
+      const response = yield call(
+        sldCommonService,
+        payload,
+        "get",
+        "v3/statistics/screen/analysis/searchRank"
+      );
+      yield put({
+        type: "setSearchRankData",
+        payload: { data: response.data, loading: false },
+      });
+    },
+
     // 初始化数据
     *load_data({ payload }, { put, call }) {
       // 调用 load_data
@@ -270,14 +343,28 @@ export default {
         type: "load_add_member_trend",
       });
 
-      // 新增商品趋势 load_add_product_trend
+      // 新增商品趋势
       yield put.resolve({
         type: "load_add_product_trend",
       });
+
+      // 关键词排行
+      yield put.resolve({
+        type: "load_search_rank",
+      });
     },
   },
 
   reducers: {
+    setSearchRankData(state, { payload }) {
+      return {
+        ...state,
+        searchRankData: {
+          ...state.searchRankData,
+          ...payload,
+        },
+      };
+    },
     setOverviewData(state, { payload }) {
       return {
         ...state,