Ver código fonte

chore: 接口对接

chenlei 2 anos atrás
pai
commit
ee6df22042

+ 4 - 2
package.json

@@ -10,6 +10,7 @@
     "@types/node": "^16.18.38",
     "@types/react": "^18.2.14",
     "@types/react-dom": "^18.2.6",
+    "axios": "^1.4.0",
     "classnames": "^2.3.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
@@ -19,8 +20,8 @@
     "web-vitals": "^2.1.4"
   },
   "scripts": {
-    "start": "react-scripts start",
-    "build": "react-scripts build",
+    "start": "cross-env REACT_APP_BACKEND_URL=https://sit-shgybwg.4dage.com react-scripts start",
+    "build": "cross-env PUBLIC_URL=./ REACT_APP_BACKEND_URL=https://sit-shgybwg.4dage.com react-scripts build",
     "test": "react-scripts test",
     "eject": "react-scripts eject"
   },
@@ -43,6 +44,7 @@
     ]
   },
   "devDependencies": {
+    "cross-env": "^7.0.3",
     "node-sass": "6.x",
     "sass-loader": "^13.3.2"
   }

+ 1 - 1
public/model.html

@@ -36,7 +36,7 @@
 
       function initModel(isDev) {
         if (loaded) return;
-        fdage.embed(isDev ? "https://hnbwg.4dage.com" + number : number, {
+        fdage.embed(isDev ? "https://sit-shgybwg.4dage.com" + number : number, {
           transparentBackground: true,
           width: 800,
           height: 600,

+ 1 - 1
src/App.tsx

@@ -7,7 +7,7 @@ function App() {
   return (
     <div className="App">
       <Routes>
-        <Route path="/model" element={<ModelInfoPage />} />
+        <Route path="/model/:id" element={<ModelInfoPage />} />
       </Routes>
     </div>
   );

+ 5 - 0
src/api/index.ts

@@ -0,0 +1,5 @@
+import service from "../utils/services";
+
+export const getGoodsDetailApi = (id: string) => {
+  return service.get(`/api/show/goods/detail/${id}`);
+};

+ 3 - 3
src/index.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 import ReactDOM from "react-dom/client";
-import { BrowserRouter } from "react-router-dom";
+import { HashRouter } from "react-router-dom";
 import App from "./App";
 // import reportWebVitals from './reportWebVitals';
 
@@ -9,9 +9,9 @@ const root = ReactDOM.createRoot(
 );
 root.render(
   <React.StrictMode>
-    <BrowserRouter>
+    <HashRouter>
       <App />
-    </BrowserRouter>
+    </HashRouter>
   </React.StrictMode>
 );
 

+ 20 - 1
src/pages/model-info/index.scss

@@ -51,6 +51,8 @@
 
   &-info {
     flex: 1;
+    display: flex;
+    flex-direction: column;
     position: relative;
     padding: 0 30px;
     color: #ffffff;
@@ -63,8 +65,25 @@
       &:not(:first-child) {
         margin-top: 30px;
       }
+      &:last-child {
+        flex: 1;
+        margin-bottom: 10px;
+
+        div {
+          position: relative;
+          width: 100%;
+        }
+        span {
+          position: absolute;
+          top: 0;
+          left: 0;
+          right: 0;
+          height: 100%;
+          overflow-y: auto;
+        }
+      }
       span {
-        opacity: 0.8;
+        color: rgba(255, 255, 255, 0.8);
       }
       img {
         position: relative;

+ 70 - 32
src/pages/model-info/index.tsx

@@ -5,26 +5,56 @@ import IconCircle from "./images/icon_circle@2x.png";
 import "./index.scss";
 import { useCallback, useEffect, useRef, useState } from "react";
 import classNames from "classnames";
+import { getGoodsDetailApi } from "../../api";
+import { useParams } from "react-router-dom";
 
 const INFO_LIST = [
   {
     label: "厂家",
-    key: "航天八院",
+    key: "company",
   },
   {
     label: "尺寸",
-    key: "3.22m×2.87m×4.12m",
+    key: "size",
   },
   {
     label: "简介",
-    key: "风云四号气象卫星是我国第二代静止轨道气象卫星,获取地球表面和云的多光谱、高精度定量观测数据和图像,全面提高对地球表面和大气物理参数的多光谱、高频次、定量探测能力;实现大气温度和湿度参数垂直结构观测,提高探测精度;实现观测区内闪电成像观测;监测空间高能粒子、空间充放电效应、地磁场强度等空间环境。",
+    key: "description",
   },
 ];
 
 const ModelInfoPage = () => {
+  const route = useParams();
   // 控制模型放大缩小和复位
   const ifrBoxRef = useRef<HTMLIFrameElement>(null);
   const [isFullScreen, setIsFullScreen] = useState(false);
+  const [info, setInfo] = useState<{
+    name: string;
+    company: string;
+    size: string;
+    description: string;
+    dagePath: string;
+  } | null>(null);
+
+  const getGoodsDetail = useCallback(async () => {
+    const {
+      data: { entity, file },
+    } = await getGoodsDetailApi(route.id as string);
+
+    const dage = file.find((i: any) => i.type === "model");
+
+    setInfo({
+      name: entity.name,
+      company: entity.company,
+      size: entity.size,
+      description: entity.description,
+      dagePath: dage ? dage.filePath : "",
+    });
+  }, [route]);
+
+  useEffect(() => {
+    getGoodsDetail();
+  }, [getGoodsDetail]);
 
   useEffect(() => {
     const init = (e: MessageEvent<any>) => {
@@ -55,39 +85,47 @@ const ModelInfoPage = () => {
     <div
       className={classNames("model-info-page", isFullScreen && "fullscreen")}
     >
-      <div className="model-info-page-header">
-        <span>风云四号模型</span>
-        <img className="border" src={HeaderBdImage} alt="" />
-      </div>
+      {info ? (
+        <>
+          <div className="model-info-page-header">
+            <span>{info.name}</span>
+            <img className="border" src={HeaderBdImage} alt="" />
+          </div>
 
-      {/* 模型 */}
-      <div className="model-info-page-iframe">
-        <iframe
-          ref={ifrBoxRef}
-          title="模型"
-          src="model.html?m=/goods/base/model/hnbwy263.4dage"
-        />
-      </div>
+          {/* 模型 */}
+          <div className="model-info-page-iframe">
+            {info.dagePath && (
+              <iframe
+                ref={ifrBoxRef}
+                title="模型"
+                src={"model.html?m=" + info.dagePath}
+              />
+            )}
+          </div>
 
-      <img
-        className="model-info-page__btn"
-        src={isFullScreen ? IconScreen : IconFullScreen}
-        alt={isFullScreen ? "缩小" : "放大"}
-        onClick={handleScreen}
-      />
+          <img
+            className="model-info-page__btn"
+            src={isFullScreen ? IconScreen : IconFullScreen}
+            alt={isFullScreen ? "缩小" : "放大"}
+            onClick={handleScreen}
+          />
 
-      {!isFullScreen && (
-        <div className="model-info-page-info">
-          {INFO_LIST.map((item) => (
-            <div key={item.key} className="model-info-page-info__line">
-              <img src={IconCircle} alt="" />
-              <span>
-                {item.label}:{item.key}
-              </span>
+          {!isFullScreen && (
+            <div className="model-info-page-info">
+              {INFO_LIST.map((item) => (
+                <div key={item.key} className="model-info-page-info__line">
+                  <img src={IconCircle} alt="" />
+                  <div>
+                    <span>
+                      {item.label}:{info[item.key as keyof typeof info]}
+                    </span>
+                  </div>
+                </div>
+              ))}
             </div>
-          ))}
-        </div>
-      )}
+          )}
+        </>
+      ) : null}
     </div>
   );
 };

+ 96 - 0
src/utils/services.ts

@@ -0,0 +1,96 @@
+import axios, {
+  type AxiosInstance,
+  type InternalAxiosRequestConfig,
+  type AxiosResponse,
+} from "axios";
+
+export enum ResponseStatusCode {
+  SUCCESS = 0,
+  TOKEN_INVALID = 5001,
+  TOKEN_INVALID2 = 5002,
+}
+
+interface DageConfig<D = any> extends InternalAxiosRequestConfig<D> {
+  /**
+   * 隐藏错误提醒
+   */
+  hidden?: boolean;
+}
+
+export interface DageResponse<T = any, D = any> extends AxiosResponse<T, D> {
+  code: number;
+  msg: string;
+}
+
+interface Service extends AxiosInstance {
+  get<T = any, R = DageResponse<T>, D = any>(
+    url: string,
+    config?: Partial<DageConfig<D>>
+  ): Promise<R>;
+  post<T = any, R = DageResponse<T>, D = any>(
+    url: string,
+    data?: D,
+    config?: DageConfig<D>
+  ): Promise<R>;
+}
+
+const service: Service = axios.create({
+  baseURL: process.env.REACT_APP_BACKEND_URL,
+  timeout: 5 * 60 * 1000,
+  headers: {
+    "Cache-Control": "no-cache",
+    "Content-Type": "application/json;charset=UTF-8",
+    "X-Requested-With": "XMLHttpRequest",
+  },
+});
+
+/**
+ * 服务端接口empty字符串跟null返回的结果不同,过滤掉empty字符串
+ * @param params
+ * @param emptyString 是否过滤空字符串
+ */
+function filterEmptyKey(params: any, emptyString = false) {
+  if (Array.isArray(params) || params == null) {
+    return params;
+  }
+
+  Object.keys(params).forEach((key) => {
+    if (params[key] === null || (emptyString && params[key] === "")) {
+      delete params[key];
+    }
+  });
+}
+
+service.interceptors.request.use((config: DageConfig) => {
+  if (config.method === "post") {
+    if (!(config.data instanceof FormData)) {
+      const params = {
+        ...config.data,
+      };
+      filterEmptyKey(params); // 过滤空字符串
+      config.data = params;
+    }
+  } else if (config.method === "get") {
+    config.params = {
+      _t: new Date().getTime() / 1000,
+      ...config.params,
+    };
+    filterEmptyKey(config.params, true);
+  }
+  return config;
+});
+
+service.interceptors.response.use(
+  (res) => {
+    const { data }: { config: DageConfig; data: DageResponse } = res;
+
+    return data || {};
+  },
+  (error) => {
+    return new Promise((res, rej) => {
+      rej(error);
+    });
+  }
+);
+
+export default service as Required<Service>;

+ 32 - 2
yarn.lock

@@ -2873,6 +2873,15 @@ axe-core@^4.6.2:
   resolved "https://registry.npmmirror.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0"
   integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==
 
+axios@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
+  integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
+  dependencies:
+    follow-redirects "^1.15.0"
+    form-data "^4.0.0"
+    proxy-from-env "^1.1.0"
+
 axobject-query@^3.1.1:
   version "3.2.1"
   resolved "https://registry.npmmirror.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
@@ -3557,7 +3566,14 @@ cosmiconfig@^7.0.0:
     path-type "^4.0.0"
     yaml "^1.10.0"
 
-cross-spawn@^7.0.2, cross-spawn@^7.0.3:
+cross-env@^7.0.3:
+  version "7.0.3"
+  resolved "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
+  integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
+  dependencies:
+    cross-spawn "^7.0.1"
+
+cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
   version "7.0.3"
   resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
   integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -4801,7 +4817,7 @@ flatted@^3.1.0:
   resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
   integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
 
-follow-redirects@^1.0.0:
+follow-redirects@^1.0.0, follow-redirects@^1.15.0:
   version "1.15.2"
   resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
   integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
@@ -4846,6 +4862,15 @@ form-data@^3.0.0:
     combined-stream "^1.0.8"
     mime-types "^2.1.12"
 
+form-data@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+  integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    mime-types "^2.1.12"
+
 form-data@~2.3.2:
   version "2.3.3"
   resolved "https://registry.npmmirror.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -8031,6 +8056,11 @@ proxy-addr@~2.0.7:
     forwarded "0.2.0"
     ipaddr.js "1.9.1"
 
+proxy-from-env@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+  integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
 psl@^1.1.28, psl@^1.1.33:
   version "1.9.0"
   resolved "https://registry.npmmirror.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"