yanghao 15 時間 前
コミット
4751b2cae7

+ 1 - 0
.env

@@ -0,0 +1 @@
+VITE_APP_TITLE=科瑞石油技术门户网站

+ 1 - 0
.env.dev

@@ -0,0 +1 @@
+VITE_BASE_URL='https://iot.deepoil.cc'

+ 1 - 0
components.d.ts

@@ -22,6 +22,7 @@ declare module 'vue' {
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElTag: typeof import('element-plus/es')['ElTag']
+    Footer: typeof import('./src/components/home/Footer.vue')['default']
     Header: typeof import('./src/components/home/header.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']

+ 3 - 1
index.html

@@ -4,7 +4,9 @@
     <meta charset="UTF-8" />
     <link rel="icon" href="/favicon.ico" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>门户网站</title>
+    <title>%VITE_APP_TITLE%</title>
+    <script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
+    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/js-base64@3.6.0/base64.min.js"></script>
   </head>
   <body>
     <div id="app"></div>

+ 1 - 0
package.json

@@ -20,6 +20,7 @@
     "element-plus": "^2.11.7",
     "motion-v": "^1.7.4",
     "pinia": "^3.0.3",
+    "pinia-plugin-persistedstate": "^4.7.1",
     "vue": "^3.5.22",
     "vue-router": "^4.6.3"
   },

+ 28 - 0
pnpm-lock.yaml

@@ -26,6 +26,9 @@ importers:
       pinia:
         specifier: ^3.0.3
         version: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))
+      pinia-plugin-persistedstate:
+        specifier: ^4.7.1
+        version: 4.7.1(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)))
       vue:
         specifier: ^3.5.22
         version: 3.5.22(typescript@5.9.3)
@@ -881,6 +884,9 @@ packages:
     resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
     engines: {node: '>=12'}
 
+  defu@6.1.4:
+    resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
   delayed-stream@1.0.0:
     resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
     engines: {node: '>=0.4.0'}
@@ -1290,6 +1296,20 @@ packages:
     engines: {node: '>=0.10'}
     hasBin: true
 
+  pinia-plugin-persistedstate@4.7.1:
+    resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==}
+    peerDependencies:
+      '@nuxt/kit': '>=3.0.0'
+      '@pinia/nuxt': '>=0.10.0'
+      pinia: '>=3.0.0'
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+      '@pinia/nuxt':
+        optional: true
+      pinia:
+        optional: true
+
   pinia@3.0.3:
     resolution: {integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==}
     peerDependencies:
@@ -2344,6 +2364,8 @@ snapshots:
 
   define-lazy-prop@3.0.0: {}
 
+  defu@6.1.4: {}
+
   delayed-stream@1.0.0: {}
 
   detect-libc@2.1.2: {}
@@ -2702,6 +2724,12 @@ snapshots:
 
   pidtree@0.6.0: {}
 
+  pinia-plugin-persistedstate@4.7.1(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))):
+    dependencies:
+      defu: 6.1.4
+    optionalDependencies:
+      pinia: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))
+
   pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)):
     dependencies:
       '@vue/devtools-api': 7.7.7

+ 0 - 5
src/api/index.ts

@@ -1,5 +0,0 @@
-import http from "@utils/request";
-
-export const getParkInfo = (data: any) => {
-  return http.get("/test/one", data);
-};

+ 5 - 0
src/api/user.ts

@@ -0,0 +1,5 @@
+import http from "@utils/request";
+
+export const qrcodeLogin = (data: any) => {
+  return http.post("/admin-api/system/auth/appSocialLogin", data);
+};

+ 63 - 0
src/components/home/Footer.vue

@@ -0,0 +1,63 @@
+<template>
+  <footer class="bg-[#0e1324] text-gray-300">
+    <div class="max-w-[1200px] mx-auto px-4 py-12 grid grid-cols-1 md:grid-cols-6 gap-6">
+      <div class="md:col-span-2 flex gap-4">
+        <div class="w-24 h-24 bg-white/5 flex items-center justify-center">
+          <!-- QR placeholder -->
+          <div class="w-20 h-20 bg-white/10 flex items-center justify-center text-xs text-white/80">扫码关注</div>
+        </div>
+        <div class="text-sm leading-relaxed">
+          <div class="text-white font-semibold mb-2 text-[14px]">DeepOil</div>
+          <div class="mb-2 text-[12px]">咨询热线:<span class="text-white font-medium">401-1888-515</span></div>
+          <div class="text-[12px]">公司邮箱:mkt@DeepOil.com</div>
+        </div>
+      </div>
+
+      <div class="md:col-span-1">
+        <div class="text-white font-semibold mb-3 text-[14px]">DeepOil产品</div>
+        <ul class="space-y-2 text-[12px] text-gray-400">
+          <li>经营驾驶舱</li>
+          <li>智能钻井系统</li>
+          <li>QHSE (安全监控、应急指挥)</li>
+          <li>全局数据治理 (数据中台)</li>
+          <li>行业AI大模型</li>
+          <li>生产指挥平台</li>
+        </ul>
+      </div>
+
+      <div class="md:col-span-1">
+        <div class="text-white text-[14px] font-semibold mb-3">行业解决方案</div>
+        <ul class="space-y-2 text-[12px] text-gray-400">
+          <li>设备管理系统 (PMS)</li>
+          <li>项目管理 (PM)</li>
+          <li>Chat BI平台</li>
+          <li>经营管理平台</li>
+        </ul>
+      </div>
+
+      <div class="md:col-span-1">
+        <div class="text-white text-[14px] font-semibold mb-3">行业模型市场</div>
+        <ul class="space-y-2 text-[12px] text-gray-400">
+          <li>基础工具</li>
+          <li>行业AI大模型</li>
+          <li>AI智能体</li>
+          <li>数据服务</li>
+        </ul>
+      </div>
+
+      <div class="md:col-span-1">
+        <div class="text-white text-[14px] font-semibold mb-3">关于DeepOil</div>
+        <ul class="space-y-2 text-[12px] text-gray-400">
+          <li>我们的客户</li>
+          <li>合作伙伴</li>
+        </ul>
+      </div>
+    </div>
+
+    <div class="bg-[#08101a] text-center text-[12px] text-gray-400 py-3">
+      Copyright ©2025 DeepOil 鲁ICP备2025162998号-1
+    </div>
+  </footer>
+</template>
+
+

+ 6 - 1
src/main.ts

@@ -1,6 +1,8 @@
 import { createApp } from "vue";
 import { createPinia } from "pinia";
 
+import piniaPersist from 'pinia-plugin-persistedstate';
+
 import "./assets/style/main.css";
 
 import App from "./App.vue";
@@ -8,7 +10,10 @@ import router from "./router";
 
 const app = createApp(App);
 
-app.use(createPinia());
+const pinia = createPinia();
+pinia.use(piniaPersist);
+
+app.use(pinia);
 app.use(router);
 
 app.mount("#app");

+ 9 - 0
src/router/index.ts

@@ -9,6 +9,7 @@ import Management from "@/views/management.vue";
 import Command from "@/views/command.vue";
 import ChatBI from "@/views/chatbi.vue";
 import Login from "@/views/login.vue";
+import { useLoginStore } from "@/stores/loginStore";
 
 const routes: RouteRecordRaw[] = [
   {
@@ -47,4 +48,12 @@ const router = createRouter({
   },
 });
 
+const publicPages = ["/", "/login"];
+
+router.beforeEach((to, from, next) => {
+  const loginStore = useLoginStore();
+
+  const authRequired = !publicPages.includes(to.path);
+});
+
 export default router;

+ 0 - 12
src/stores/counter.ts

@@ -1,12 +0,0 @@
-import { ref, computed } from 'vue'
-import { defineStore } from 'pinia'
-
-export const useCounterStore = defineStore('counter', () => {
-  const count = ref(0)
-  const doubleCount = computed(() => count.value * 2)
-  function increment() {
-    count.value++
-  }
-
-  return { count, doubleCount, increment }
-})

+ 27 - 0
src/stores/loginStore.ts

@@ -0,0 +1,27 @@
+import { defineStore } from "pinia";
+import { qrcodeLogin } from "@api/user";
+
+export const useLoginStore = defineStore("login", {
+  state: () => ({
+    userInfo: null,
+  }),
+
+  actions: {
+    // 登录成功后的操作
+    async login() {
+      const res = await qrcodeLogin({
+        type: 10,
+        code: "1024",
+        state: "9b2ffbc1-7425-4155-9894-9d5c08541d62",
+      });
+
+      this.userInfo = res.data;
+    },
+  },
+
+  persist: {
+    storage: localStorage,
+    key: "userInfo",
+    pick: ["userInfo"],
+  },
+});

+ 84 - 20
src/utils/request.ts

@@ -1,46 +1,110 @@
-import axios from 'axios'
+import axios, { type AxiosResponse } from "axios";
+import { useLoginStore } from "@stores/loginStore";
 
 const request = axios.create({
-  baseURL: 'http://39.100.95.39:8089',
+  baseURL: import.meta.env.VITE_BASE_URL as string,
+  headers: {
+    "Content-Type": "application/json",
+  },
   timeout: 5000,
-})
+});
 
-request.interceptors.request.use((config) => {
-  return config
-})
+// 请求拦截:注入 token
+request.interceptors.request.use(
+  (config) => {
+    const loginStore = useLoginStore();
+    // 添加 token
+    if (loginStore.token) {
+      config.headers.Authorization = `Bearer ${loginStore.token}`;
+    }
+    // 添加请求时间戳,防止缓存
+    if (config.method?.toLowerCase() === "get") {
+      config.params = {
+        ...config.params,
+        _t: Date.now(),
+      };
+    }
+    return config;
+  },
+  (error) => {
+    console.error("请求配置错误:", error);
+    return Promise.reject(error);
+  }
+);
 
-request.interceptors.response.use((response) => {
-  return response.data
-})
+// 统一响应处理:提取后端 data 字段并对非成功状态构造错误
+request.interceptors.response.use(
+  (response: AxiosResponse) => {
+    // 如果没有业务状态字段,直接返回数据
+    return response.data;
+  },
+  (error) => {
+    return Promise.reject(error);
+  }
+);
+
+type ResponseData = {
+  code: number;
+  msg: string;
+  data: any;
+};
 
 const http = {
-  get(url: string, data?: any) {
+  get(url: string, data?: any): Promise<ResponseData> {
     return new Promise((resolve, reject) => {
       request
         .get(url, {
           params: data,
         })
         .then((res) => {
-          resolve(res)
+          resolve(res);
         })
         .catch((err) => {
-          reject(err)
-        })
-    })
+          reject(err);
+        });
+    });
   },
 
-  post(url: string, data?: any) {
+  post(url: string, data?: any): Promise<ResponseData> {
     return new Promise((resolve, reject) => {
       request
         .post(url, data)
         .then((res) => {
-          resolve(res)
+          resolve(res);
+        })
+        .catch((err) => {
+          reject(err);
+        });
+    });
+  },
+
+  put(url: string, data?: any) {
+    return new Promise((resolve, reject) => {
+      request
+        .put(url, data)
+        .then((res) => {
+          resolve(res);
         })
         .catch((err) => {
-          reject(err)
+          reject(err);
+        });
+    });
+  },
+
+  delete(url: string, data?: any) {
+    return new Promise((resolve, reject) => {
+      request
+        .delete(url, {
+          params: data,
         })
-    })
+        .then((res) => {
+          resolve(res);
+        })
+        .catch((err) => {
+          reject(err);
+        });
+    });
   },
-}
+};
 
-export default http
+export default http;

+ 3 - 2
src/views/chatbi.vue

@@ -37,14 +37,15 @@
         </div>
       </div>
     </section>
+
+    <Footer />
   </div>
 </template>
 
 <script setup lang="ts">
 import Header from "@components/home/header.vue";
-import img1 from "@/assets/images/1.jpg";
 import img2 from "@/assets/images/2.jpg";
-import img3 from "@/assets/images/3.jpg";
+import Footer from "@components/home/Footer.vue";
 import agent from "@/assets/images/agent.png";
 import model from "@/assets/images/model.jpeg";
 import data from "@/assets/images/data.png";

+ 3 - 0
src/views/command.vue

@@ -37,6 +37,8 @@
         </div>
       </div>
     </section>
+
+    <Footer />
   </div>
 </template>
 
@@ -49,6 +51,7 @@ import pms from "@/assets/images/pms.webp";
 import qhse from "@/assets/images/qhse.jpg";
 import lianyou from "@/assets/images/lianyou.png";
 import zhuqi from "@/assets/images/zhuqi.png";
+import Footer from "@components/home/Footer.vue";
 
 type GridItem = {
   id: string;

+ 4 - 1
src/views/index.vue

@@ -2,7 +2,7 @@
   <div class="bg-white">
     <Header />
 
-    <!-- Hero with video background -->
+   
     <section class="relative w-full h-[56vh] md:h-[70vh] overflow-hidden mt-15">
       <video
         class="absolute inset-0 w-full h-full object-cover"
@@ -58,12 +58,15 @@
         />
       </div>
     </section>
+    
+    <Footer />
   </div>
 </template>
 
 <script setup lang="ts">
 import Header from "@components/home/header.vue";
 import CardItem from "@components/home/CardItem.vue";
+import Footer from "@components/home/Footer.vue";
 import img1 from "@/assets/images/1.jpg";
 import img2 from "@/assets/images/2.jpg";
 import img3 from "@/assets/images/3.jpg";

+ 2 - 1
src/views/login.vue

@@ -19,6 +19,7 @@
       <div class="bg-white/95 backdrop-blur-sm rounded-lg p-8 shadow-2xl">
         <h1 class="text-2xl font-bold text-center mb-8">登录</h1>
 
+        <div id="login_container" style="width: 200px; height: 200px;"></div>
         <el-form
           :model="form"
           :rules="rules"
@@ -99,6 +100,6 @@ const onSubmit = async () => {
 };
 
 const qrLogin = () => {
-  ElMessage.info("请接入钉钉扫码登录");
+  
 };
 </script>

+ 2 - 1
src/views/management.vue

@@ -37,12 +37,13 @@
         </div>
       </div>
     </section>
+    <Footer />
   </div>
 </template>
 
 <script setup lang="ts">
 import Header from "@components/home/header.vue";
-import img1 from "@/assets/images/1.jpg";
+import Footer from "@components/home/Footer.vue";
 import img2 from "@/assets/images/2.jpg";
 import ehr from "@/assets/images/ehr.png";
 import crm from "@/assets/images/crm.png";

+ 2 - 1
tsconfig.app.json

@@ -11,7 +11,8 @@
       "@assets/*": ["./src/assets/*"],
       "@api/*": ["./src/api/*"],
       "@utils/*": ["./src/utils/*"],
-      "@types/*": ["./src/types/*"]
+      "@types/*": ["./src/types/*"],
+      "@stores/*": ["./src/stores/*"]
     }
   }
 }

+ 3 - 2
vite.config.ts

@@ -12,8 +12,8 @@ import MotionResolver from "motion-v/resolver";
 
 export default defineConfig({
   server: {
-    host: '0.0.0.0', // 监听所有网络接口
-    port: 5173 // 可选:指定端口
+    host: "0.0.0.0", // 监听所有网络接口
+    port: 5173, // 可选:指定端口
   },
   base: "./",
   plugins: [
@@ -38,6 +38,7 @@ export default defineConfig({
       "@api": fileURLToPath(new URL("./src/api", import.meta.url)),
       "@utils": fileURLToPath(new URL("./src/utils", import.meta.url)),
       "@types": fileURLToPath(new URL("./src/types", import.meta.url)),
+      "@stores": fileURLToPath(new URL("./src/stores", import.meta.url)),
     },
   },
 });