Browse Source

Merge branch 'master' into fm-dev

# Conflicts:
#	components.d.ts
#	src/views/pc/device/index.vue
wangfumin 1 tuần trước cách đây
mục cha
commit
dd31ebac2b

+ 0 - 16
components.d.ts

@@ -10,22 +10,6 @@ export {}
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
     Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
-    ElButton: typeof import('element-plus/es')['ElButton']
-    ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
-    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
-    ElCol: typeof import('element-plus/es')['ElCol']
-    ElDialog: typeof import('element-plus/es')['ElDialog']
-    ElEmpty: typeof import('element-plus/es')['ElEmpty']
-    ElIcon: typeof import('element-plus/es')['ElIcon']
-    ElInput: typeof import('element-plus/es')['ElInput']
-    ElOption: typeof import('element-plus/es')['ElOption']
-    ElPagination: typeof import('element-plus/es')['ElPagination']
-    ElRow: typeof import('element-plus/es')['ElRow']
-    ElSelect: typeof import('element-plus/es')['ElSelect']
-    ElTable: typeof import('element-plus/es')['ElTable']
-    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
-    ElTabPane: typeof import('element-plus/es')['ElTabPane']
-    ElTabs: typeof import('element-plus/es')['ElTabs']
     Footer: typeof import('./src/components/mobile/footer.vue')['default']
     Header: typeof import('./src/components/mobile/header.vue')['default']
     HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']

+ 31 - 0
components.d_BACKUP_254.ts

@@ -0,0 +1,31 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
+    Footer: typeof import('./src/components/mobile/footer.vue')['default']
+    Header: typeof import('./src/components/mobile/header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
+    IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
+    IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
+    Icons: typeof import('./src/components/icons/index.vue')['default']
+    IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
+    IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
+    Mobile: typeof import('./src/components/mobile/index.vue')['default']
+    Pc: typeof import('./src/components/pc/index.vue')['default']
+    Popup: typeof import('./src/components/popup/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
+    Toast: typeof import('./src/components/Toast/Toast.vue')['default']
+    WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
+  }
+}

+ 31 - 0
components.d_BACKUP_317.ts

@@ -0,0 +1,31 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
+    Footer: typeof import('./src/components/mobile/footer.vue')['default']
+    Header: typeof import('./src/components/mobile/header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
+    IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
+    IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
+    Icons: typeof import('./src/components/icons/index.vue')['default']
+    IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
+    IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
+    Mobile: typeof import('./src/components/mobile/index.vue')['default']
+    Pc: typeof import('./src/components/pc/index.vue')['default']
+    Popup: typeof import('./src/components/popup/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
+    Toast: typeof import('./src/components/Toast/Toast.vue')['default']
+    WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
+  }
+}

+ 43 - 0
components.d_BASE_254.ts

@@ -0,0 +1,43 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
+    ElButton: typeof import('element-plus/es')['ElButton']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElIcon: typeof import('element-plus/es')['ElIcon']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    Footer: typeof import('./src/components/mobile/footer.vue')['default']
+    Header: typeof import('./src/components/mobile/header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
+    IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
+    IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
+    Icons: typeof import('./src/components/icons/index.vue')['default']
+    IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
+    IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
+    Mobile: typeof import('./src/components/mobile/index.vue')['default']
+    Pc: typeof import('./src/components/pc/index.vue')['default']
+    Popup: typeof import('./src/components/popup/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
+    Toast: typeof import('./src/components/Toast/Toast.vue')['default']
+    WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
+  }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
+}

+ 43 - 0
components.d_BASE_317.ts

@@ -0,0 +1,43 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
+    ElButton: typeof import('element-plus/es')['ElButton']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElIcon: typeof import('element-plus/es')['ElIcon']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    Footer: typeof import('./src/components/mobile/footer.vue')['default']
+    Header: typeof import('./src/components/mobile/header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
+    IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
+    IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
+    Icons: typeof import('./src/components/icons/index.vue')['default']
+    IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
+    IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
+    Mobile: typeof import('./src/components/mobile/index.vue')['default']
+    Pc: typeof import('./src/components/pc/index.vue')['default']
+    Popup: typeof import('./src/components/popup/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
+    Toast: typeof import('./src/components/Toast/Toast.vue')['default']
+    WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
+  }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
+}

+ 54 - 0
components.d_LOCAL_254.ts

@@ -0,0 +1,54 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
+    ElButton: typeof import('element-plus/es')['ElButton']
+    ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
+    ElCard: typeof import('element-plus/es')['ElCard']
+    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElCol: typeof import('element-plus/es')['ElCol']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElEmpty: typeof import('element-plus/es')['ElEmpty']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElIcon: typeof import('element-plus/es')['ElIcon']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElOption: typeof import('element-plus/es')['ElOption']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
+    ElRow: typeof import('element-plus/es')['ElRow']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTabPane: typeof import('element-plus/es')['ElTabPane']
+    ElTabs: typeof import('element-plus/es')['ElTabs']
+    ElTooltip: typeof import('element-plus/es')['ElTooltip']
+    Footer: typeof import('./src/components/mobile/footer.vue')['default']
+    Header: typeof import('./src/components/mobile/header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
+    IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
+    IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
+    Icons: typeof import('./src/components/icons/index.vue')['default']
+    IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
+    IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
+    Mobile: typeof import('./src/components/mobile/index.vue')['default']
+    Pc: typeof import('./src/components/pc/index.vue')['default']
+    Popup: typeof import('./src/components/popup/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
+    Toast: typeof import('./src/components/Toast/Toast.vue')['default']
+    WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
+  }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
+}

+ 54 - 0
components.d_LOCAL_317.ts

@@ -0,0 +1,54 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
+    ElButton: typeof import('element-plus/es')['ElButton']
+    ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
+    ElCard: typeof import('element-plus/es')['ElCard']
+    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElCol: typeof import('element-plus/es')['ElCol']
+    ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElEmpty: typeof import('element-plus/es')['ElEmpty']
+    ElForm: typeof import('element-plus/es')['ElForm']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElIcon: typeof import('element-plus/es')['ElIcon']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElOption: typeof import('element-plus/es')['ElOption']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
+    ElRow: typeof import('element-plus/es')['ElRow']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTabPane: typeof import('element-plus/es')['ElTabPane']
+    ElTabs: typeof import('element-plus/es')['ElTabs']
+    ElTooltip: typeof import('element-plus/es')['ElTooltip']
+    Footer: typeof import('./src/components/mobile/footer.vue')['default']
+    Header: typeof import('./src/components/mobile/header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
+    IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
+    IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
+    Icons: typeof import('./src/components/icons/index.vue')['default']
+    IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
+    IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
+    Mobile: typeof import('./src/components/mobile/index.vue')['default']
+    Pc: typeof import('./src/components/pc/index.vue')['default']
+    Popup: typeof import('./src/components/popup/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
+    Toast: typeof import('./src/components/Toast/Toast.vue')['default']
+    WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
+  }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
+}

+ 31 - 0
components.d_REMOTE_254.ts

@@ -0,0 +1,31 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
+    Footer: typeof import('./src/components/mobile/footer.vue')['default']
+    Header: typeof import('./src/components/mobile/header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
+    IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
+    IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
+    Icons: typeof import('./src/components/icons/index.vue')['default']
+    IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
+    IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
+    Mobile: typeof import('./src/components/mobile/index.vue')['default']
+    Pc: typeof import('./src/components/pc/index.vue')['default']
+    Popup: typeof import('./src/components/popup/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
+    Toast: typeof import('./src/components/Toast/Toast.vue')['default']
+    WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
+  }
+}

+ 31 - 0
components.d_REMOTE_317.ts

@@ -0,0 +1,31 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    Confirm: typeof import('./src/components/Toast/Confirm.vue')['default']
+    Footer: typeof import('./src/components/mobile/footer.vue')['default']
+    Header: typeof import('./src/components/mobile/header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
+    IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
+    IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
+    Icons: typeof import('./src/components/icons/index.vue')['default']
+    IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
+    IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
+    Mobile: typeof import('./src/components/mobile/index.vue')['default']
+    Pc: typeof import('./src/components/pc/index.vue')['default']
+    Popup: typeof import('./src/components/popup/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
+    Toast: typeof import('./src/components/Toast/Toast.vue')['default']
+    WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
+  }
+}

+ 3 - 0
src/App.vue

@@ -1,7 +1,10 @@
 <script setup lang="ts">
 import '@/assets/style/iconfont.css'
+import { useUserStore } from '@/stores/user'
 import { RouterView } from 'vue-router'
 import { onMounted } from 'vue'
+const useUser = useUserStore()
+useUser.getInfo()
 onMounted(() => {
   window.onresize = () => {
     console.log('window.onresize');

+ 70 - 0
src/api/user.ts

@@ -0,0 +1,70 @@
+import { request } from "@/utils/request";
+export const getUserInfo = () => {
+  return request({
+    url: "/ucenter/user/getUserInfo",
+    method: "post",
+    config: {
+      timeout: 10000,
+      headers: {
+        "Content-Type": "application/json;charset=UTF-8",
+      },
+    },
+  });
+};
+
+export const openPay = (data) => {
+    return request({
+      url: "/service/pay/openPay",
+      method: "post",
+      data,
+      config: {
+        timeout: 10000,
+        headers: {
+            "Content-Type": "application/json;charset=UTF-8",
+        },
+      },
+    });
+  };
+
+  export const wxLogin = (data:logonParam) => {
+    return request({
+      url: "/service/pay/wxLogin",
+      method: "get",
+      data,
+      config: {
+        timeout: 10000,
+        headers: {
+            "Content-Type": "application/json;charset=UTF-8",
+        },
+      },
+    });
+  };
+  
+export const getOrderInfo = (orderSn:string) => {
+    return request({
+      url: `/service/pay/order/info/${orderSn}`,
+      method: "get",
+      config: {
+        timeout: 10000,
+        loading: true,//隐藏进度条
+        headers: {
+            "Content-Type": "application/json;charset=UTF-8",
+        },
+      },
+    });
+  };
+
+export const queryOrderStatus = (data) => {
+    return request({
+      url: `/ucenter/user/order/queryOrderStatus`,
+      method: "post",
+      data,
+      config: {
+        timeout: 10000,
+        loading: true,//隐藏进度条
+        headers: {
+            "Content-Type": "application/json;charset=UTF-8",
+        },
+      },
+    });
+  };

BIN
src/assets/images/footer/shopbtn-en.gif


BIN
src/assets/images/footer/shopbtn.gif


BIN
src/assets/images/home/France@2x.jpg


BIN
src/assets/images/home/Germany@2x.jpg


BIN
src/assets/images/home/USA@2x.jpg


BIN
src/assets/images/home/china@2x.png


+ 5 - 2
src/assets/main.css

@@ -42,7 +42,10 @@ a,
 }
 
 .contentPage{
-  max-width: 1366px;
+  /* max-width: 1316px;
+  margin: 0 auto;
+  min-width: 1200px; */
+  width: 1316px;
+  padding: 0 20px;
   margin: 0 auto;
-  min-width: 1200px;
 }

+ 41 - 2
src/components/pc/data.ts

@@ -1,11 +1,14 @@
 
+import zh from '@/assets/images/home/china@2x.png'
+import en from '@/assets/images/home/USA@2x.jpg'
+
 export const pcFooter = (t:any) => {
   return [
   {
     title: t('header.core_product'),
     list: [
       {
-        text: t('manage.sceneAdmin.sdsg'),
+        text: t('header.sdsg'),
         link: 'Meta'
       },
       {
@@ -77,4 +80,40 @@ export const pcFooter = (t:any) => {
     ]
   }
 ]
-}
+}
+export const pcHeader = (t:any) => {
+  return [
+        { text: t('header.sdsg'), link: '/mall/meta' },
+        { text: t('header.Mega'), link: '/mall/mega' },
+        { text: t('header.kankanMinion'), link: '/mall/kankanMinion' },
+        { text: t('header.mallPro'), link: '/mall/kankanPro' },
+        { text: t('header.mallPeijian'), link: '/mall/zhijia',items: [
+          {
+            text: t('header.tripod'),
+            link: '/mall/zhijia'
+          },
+          {
+            text: t('header.USBdrive'),
+            link: '/mall/USBdrive'
+          },
+          {
+            text: t('header.Battery'),
+            link: '/mall/Battery'
+          },
+        ] },
+        { text: t('header.addService'), link: '/mall/member' }
+      ]
+}
+
+export const languageList = [
+  {
+        name: '简体中文',
+        img: zh,
+        value: 'zh' 
+  },
+  {
+        name: 'English',
+        img: en,
+        value: 'en' 
+  }
+]

+ 400 - 10
src/components/pc/header.vue

@@ -1,28 +1,418 @@
 <script setup lang="ts">
+import { ref, computed } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from 'vue-i18n'
 import logoCn from '@/assets/images/logoCn.png'
 import logoEn from '@/assets/images/logoEn.png'
+import shopbtn from '@/assets/images/footer/shopbtn.gif'
+import shopbtnEn from '@/assets/images/footer/shopbtn-en.gif'
+import { useUserStore } from '@/stores/user'
+import { pcHeader, languageList } from './data'
+const router = useRouter();
 //得到i18n的locale
-const { locale } = useI18n();
+const userStore = useUserStore()
+const info = computed(() => userStore.getUserInfo)
+const isLogin = computed(() => userStore.isLogin)
+const { locale:langauge, t } = useI18n();
+const navs = pcHeader(t)
+const languageObj = computed(() => languageList.find(item => item.value === langauge.value) || {})
+console.log('languageObj', info.value, userStore)
 </script>
 
 <template>
-  <div class="header">
-    <div class="logo contentPage">
+    <div class="header-layout">
+    <div class="headercontainer clear flex justify-between items-center">
       <img :src="locale === 'zh' ? logoCn : logoEn" alt="logo">
+      <div class="menu">
+        <div
+          class="list"
+          ref="list"
+        >
+          <a
+            v-for="nav in navs"
+            :key="nav.text"
+            :class="{active: hoverCp === nav.cp}"
+            class="header-item"
+            :style="{'margin-right': langauge==='en' ? '30px' : '48px'}"
+            @click="toNav(nav)"
+          >{{nav.text}}
+            <ul class="child-list" v-if="nav.items">
+              <li class="" v-for="(item, index) in nav.items" :key="index" @click.stop="toNav(item)">
+                {{ item.text }}
+              </li>
+            </ul>
+          </a>
+          
+        </div>
+      </div>
+      <div class="ctrl">
+        <div class="shop-btn" @click="router.push({name: 'mallMeta'})" v-if="!isShop">
+          <img :src="langauge==='en' ? shopbtnEn : shopbtn" alt="" srcset="">
+        </div>
+        <div class="language-w">
+          <div class="list">
+            <a class="header-item">
+              <p class="guoqi" :style="{'background-image': `url(${languageObj.img})`}">{{ languageObj.name }}</p>
+              <ul class="child-list">
+                <li v-for="item in languageList" :key="item.name" :style="{'background-image': `url(${item.img})`}" @click="changeLanguage(item.value)">{{ item.name }}</li>
+              </ul>
+            </a>
+          </div>
+        </div>
+        <div class="user" @click="router.push('/login/login')" v-if="!isLogin"></div>
+        <div v-else-if="info" class="user-w">
+          <div class="user avatar" :class="{vip: info.incrementNum}" :style="{'background-image': `url(${info.head})`}" @click="router.push('/information')">
+          <div class="member-icon" @click.stop="router.push({name: 'member'})" v-if="info.incrementNum"></div>
+          </div>
+          <div class="list avatarList">
+              <a class="header-item" @click="router.push('/information')">
+                <ul class="child-list">
+                  <li @click="router.push('/information')">{{ t('header.myaccount') }}</li>
+                  <li @click.stop="handleLogout">{{ t('header.logout') }}</li>
+                </ul>
+              </a>
+            </div>
+        </div>
+        <div class="cart" @click="handleCartClick">
+          <h-icon type="xingouwuche" class="icon" />
+          <span v-if="cartCount">{{cartCount}}</span>
+        </div>
+      </div>
     </div>
   </div>
+  <!-- <div class="header">
+    <div class="logo contentPage">
+      <img :src="locale === 'zh' ? logoCn : logoEn" alt="logo">
+    </div>
+  </div> -->
 </template>
+
 <style lang="less" scoped>
-.header{
+.language-w {
+  .header-item {
+    margin-right: 20px !important;
+    display: inline-block;
+    p {
+      color: #323233;
+      font-size: 14px;
+      font-weight: normal;
+      padding-left: 28px;
+      background: url(~@/assets/images/home/China.png) no-repeat left center;
+      background-size: 20px 14px;
+    }
+  }
+  .child-list {
+    background: #fff;
+    li {
+      color: #323233;
+      font-size: 14px;
+      font-weight: normal;
+      padding: 0 13px 0 40px;
+      background: url(~@/assets/images/home/China.png) no-repeat 13px center;
+      background-size: 20px 14px;
+    }
+  }
+}
+
+.header-layout {
+  // $mc: .5s cubic-bezier(.77, 0, .175, 1);
+  // $ts: all $mc;
+  background-color: #fff;
+  color: #fff;
+  justify-content: space-between;
+  color: #323233;
   height: 80px;
-  background: #FFFFFF;
-  box-shadow: 0px 3px 6px 0px rgba(0,0,0,0.1);
-  border-radius: 0px 0px 0px 0px;
-  opacity: 1;
-  .logo{
+  box-shadow:0px 3px 6px rgba(0,0,0,0.1);
+  .headercontainer {
     height: 100%;
-    padding: 17px 0;
+    width: 1316px;
+    padding: 0 20px;
+    margin: 0 auto;
+  }
+  .child-layout {
+    position: absolute;
+    top: 100%;
+    max-height: 0;
+    left: 0;
+    right: 0;
+    // transition: max-height $mc;
+    overflow: hidden;
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+    .silp {
+      position: absolute;
+      width: 1px;
+      height: 100%;
+      background-color: #ededed;
+      z-index: 2;
+    }
+  }
+
+  .logo-layout {
+    text-align: center;
+    text-align: left;
+    margin-right: 52px;
+    float: left;
+    height: 100%;
+    .logo {
+      position: relative;
+      width: 150px;
+      height: 100%;
+      display: inline-block;
+      color: #fff;
+    }
+  }
+
+  .menu {
+    float: left;
+    min-width: 600px;
+
+    .lang {
+      box-sizing: border-box;
+      text-align: center;
+      padding-right: 10px;
+
+      a {
+        display: inline-block;
+        font-size: 16px;
+        font-weight: bold;
+        margin-left: 5px;
+        color: #727272;
+        transition: color 0.2s ease;
+        &:hover{
+          color: #fff;
+        }
+      }
+
+      .active{
+        color: #fff;
+      }
+    }
+
+    
+  }
+  .list {
+      justify-content: space-between;
+      height: 100%;
+      a {
+        display: inline-block;
+        // padding: 30px 47px 25px 0;
+        margin-right: 48px;
+        line-height: 80px;
+        font-size: 16px;
+        text-decoration: none;
+        font-weight: bold;
+        position: relative;
+        transition: color 0.3s ease;
+
+        &::before {
+          z-index: 2;
+        }
+
+        &.active {
+          color: #727272;
+
+          &::after {
+            border-top-color: #727272;
+            transform: translateY(-50%) scale(1.3);
+          }
+
+          &::before {
+            transform: translateY(-50%) scale(0.7);
+            border-top-color: #000;
+          }
+        }
+
+      }
+    }
+  .ctrl {
+    align-items: center;
+    font-size: 16px;
+    &>div {
+      display: inline-block;
+      vertical-align: middle;
+    }
+    .shop-btn {
+      margin-right: 25px;
+      cursor: pointer;
+      
+      height: 34px;
+      line-height: 34px;
+      text-align: center;
+      font-weight: 400;
+      height: 34px;
+      background: linear-gradient(180deg, #1EDAE5 1%, #069199 100%);
+      border-radius: 17px;
+      color: #FFFFFF;
+        
+  img {
+    height: 34px;
+  }
+    }
+    .country {
+      width: 20px;
+      height: 21px;
+      display: inline-block;
+      background: url(~@/assets/images/home/China.png) no-repeat center center;
+      vertical-align: middle;
+    }
+    .language-w {
+      // padding: 0 30px  0 10px;
+    }
+    .language {
+      margin: 0 15px 0 0;
+      color: #8F8F8F;
+      cursor: pointer;
+      vertical-align: middle;
+      font-size: 16px;
+      font-weight: 600;
+      &.is-active {
+        color: #323233;
+      }
+    }
+    .user {
+      width: 20px;
+      height: 20px;
+      background: url(~@/assets/images/home/account-icon.png) no-repeat center center;
+      cursor: pointer;
+      background-size: cover;
+      &.avatar {
+        width: 34px;
+        height: 34px;
+        border: 1px solid #EFEFEF;
+        border-radius: 50%;
+        position: relative;
+        &.vip {
+          border-color: #D8AF7C;
+        }
+      }
+      
+      .member-icon {
+        width: 12px;
+        height: 12px;
+        background: url(~@/assets/images/refactor/usercenter/avatar_vip.png) no-repeat center center;
+        background-size: cover;
+        position: absolute;
+        right: -2px;
+        bottom: 0;
+      }
+    }
+    .cart {
+      color: #323233;
+      margin-left: 18px;
+      cursor: pointer;
+      position: relative;
+      .icon {
+        font-size: 26px;
+      }
+      span {
+        display: block;
+        position: absolute;
+        left: 9px;
+        top: 0;
+        font-size: 12px;
+        line-height: 16px;
+        text-align: center;
+        font-weight: bold;
+        color: #FFFFFF;
+        line-height: 14px;
+
+        height: 16px;
+        min-width: 16px;
+        padding: 0 2px;
+        background: linear-gradient(135deg, #696D75 0%, #3C3F41 100%);
+        border-radius: 4px;
+      }
+    }
+  }
+
+}
+.header-item:hover {
+  position: relative;
+  .child-list {
+    display: block;
+  }
+}
+.child-list {
+  position: absolute;
+  // width: 112px;
+  box-shadow: 0px -2px 6px rgba(113,113,113,0.16);
+  margin-top: -10px;
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 1;
+  display: none;
+  transition: all linear 0.5s;
+  padding-top: 6px;
+  white-space: nowrap;
+  
+  background: #fff;
+  &::before {
+    position: absolute;
+    display: block;
+    top: -5px;
+    left: 50%;
+    margin-left: -2px;
+    width: 0;
+    height: 0px;
+    content: '';
+    background: #fff;
+    border-style: solid;
+    border-width: 4px;
+    border-color: #fff #fff transparent transparent;
+    transform: rotate(-45deg);
+    box-shadow: 1px -1px 0px rgba(113,113,113,0.16);
+    z-index: 1;
+  }
+  li {
+    height: 48px;
+    line-height: 48px;
+    font-size: 16px;
+    text-align: center;
+    position: relative;
+    background: #fff;
+    z-index: 2;
+    width: 100%;
+    padding: 0 25px;
+    &:hover {
+      background-color: #EBEBEB;
+    }
+  }
+}
+.list {
+  &:hover {
+    .header-item {
+      color: #909090;
+    }
+  }
+  .header-item:hover {
+    position: relative;
+    color: #323233;
+    transition: 0s;
+    .child-list {
+      display: block;
+    }
+  }
+}
+.user-w {
+  .avatarList{
+    position: absolute;
+    z-index: 1;
+  }
+  .list {
+    height: 100%;
+    top: 0;
+    .header-item {
+      display: block;
+      width: 32px;
+      padding-top: 80px;
+    }
   }
 }
+@media screen and (max-width: 1600px){
+}
+
+@media screen and (max-width: 1450px){
+}
+
 </style>
+

+ 57 - 0
src/i18n/modules/zh/header.js

@@ -0,0 +1,57 @@
+export default {
+        "core_product": "核心产品",
+        "solutions": "行业解决方案",
+        "solutionsText": "四维时代致力于人工智能三维数字化技术<br/>的研究和应用",
+        "kankan_space": "经典案例",
+        "core_tech": "核心技术",
+        "service": "服务支持",
+        "about": "关于我们",
+        "online_shop": "商城",
+        "foreign": "中德合作",
+        "solutionsHouse": "房产营销",
+        "solutionsExi": "智慧城市",
+        "solutionsSubject": "博物馆",
+        "solutionsShop": "VR购物",
+        "solutionsSec": "刑侦消防",
+        "serviceApp": "App 下载",
+        "joinUs": "加入我们",
+        "serviceUse": "使用教程",
+        "serviceBaoxiu": "售后服务",
+        "serviceVideo": "视频教程",
+        "aboutNews": "新闻中心",
+        "aboutAgent": "成为经销商",
+        "aboutCompany": "公司介绍",
+        "mallPro": "四维看看",
+        "kankanMinion": "四维看见",
+        "Mega": "四维深时",
+        "sdsg": "四维深光",
+        "mallPeijian": "精选配件",
+        "tripod": "三脚架套装",
+        "USBdrive": "U盘",
+        "Battery": "电池",
+        "addService": "会员权益",
+        "corePruductItemPro": "四维看看",
+        "corePruductItemSxz": "四维 • 随心装",
+        "footer": {
+            "bannerTitle": "四维看看,让空间讲故事",
+            "find": "发现精彩",
+            "saleEmail": "销售合作",
+            "meitiEmail": "媒体采访",
+            "phone": "联系电话",
+            "find4D": "探索四维",
+            "kankan": "四维时代",
+            "zhongde": "中德人工智能研究院",
+            "moku": "四维模库",
+            "workTime": "周一至周五 9:00-18:30"
+        },
+        "moreName": {
+            "1": "装修家装",
+            "2": "商圈",
+            "3": "线上会展",
+            "4": "地下管网",
+            "5": "BIM"
+        },
+        myaccount: '我的账号',
+        logout: '退出登录'
+
+    }

+ 0 - 0
src/i18n/modules/zh/home.js


+ 81 - 0
src/i18n/modules/zh/manage.js

@@ -0,0 +1,81 @@
+export default {
+        "information": {
+            "uploadTip": "图片大小不能大于",
+            "editInformation": "修改信息",
+            "editAddressLinkText": "修改地址",
+            "editInfo": "修改账号信息",
+            "gotoPay": "购买权益",
+            "memberDevice": "权益相机",
+            "expiredDownload": "剩余下载",
+            "takePhotos": "拍摄场景",
+            "defaultAddress": "默认收货地址",
+            "informationOverview": "账户概览",
+            "uploadAvatar": "上传头像",
+            "uploadAvatarTips": "推荐分辨率为:512*512px,<br/>允许JPG、PNG格式,小于1MB",
+            "userName": "账号",
+            "nickName": "昵称",
+            "email": "邮箱",
+            "cameraCount": "相机数量",
+            "memberCount": "权益数量",
+            "tai": "台",
+            "ci": "次",
+            "ge": "个",
+            "tai1": "(台)",
+            "ci1": "(次)",
+            "ge1": "(个)",
+            "editAddress": {
+                "editAddress": "修改收货地址",
+                "country": "国家/地区",
+                "province": "省",
+                "city": "市",
+                "qu": "区",
+                "addressDetail": "详细地址",
+                "receiver": "收件人",
+                "receiverPhone": "手机号码",
+                "internationalCity": "城市",
+                "internationalProvince": "州/省/地区"
+            },
+            "changePassword": {
+                "changePassword": "修改密码",
+                "verCode": "验证码",
+                "newPassword": "新密码",
+                "confirmNewPassword": "确认密码"
+            },
+            "memberTable": {
+                "deviceMember": "会员权益",
+                "bindDeviceNum": "授权相机{num}台",
+                "snInputPlaceHolder": "搜索相机S/N码",
+                "searchNoData": "抱歉,没有找到相关信息。",
+                "nodataPre": "请先购买会员权益",
+                "nodataGoBuy": "购买权益",
+                "isExpired": "已过期"
+            },
+            "nameArr": {
+                "information": "账号信息",
+                "scene": "场景管理",
+                "order": "我的订单",
+                "device": "我的相机",
+                "consumption": "消费记录",
+                "change": "修改密码",
+                "logout": "退出登录",
+                "products": "应用中心"
+            },
+            "deviceSettings": [
+                {
+                    "name": "security",
+                    "items": [
+                        {
+                            "name": "场景管理",
+                            "to": {
+                                "name": "scene"
+                            }
+                        },
+                        {
+                            "name": "退出登录",
+                            "to": "logout"
+                        }
+                    ]
+                }
+            ]
+        },
+    }

+ 130 - 0
src/i18n/modules/zh/toast.js

@@ -0,0 +1,130 @@
+export default {
+  '1': '获取订单信息失败',
+  '2': '获取支付二维码失败',
+  '3': '正在跳转至paypal支付链接,请稍等',
+  '4': '信息填写不能为空',
+  '5': '开票成功',
+  '6': '开票成功',
+  '7': '不能为空',
+  '8': '请输入正确的邮箱地址',
+  '9': '提交成功',
+  '10': '联系信息不正确,请修改',
+  '11': '系统繁忙,请稍后再试',
+  '12': '您的信息已收到,我们会尽快与您联系',
+  '13': '获取相机详情失败',
+  '14': '无法删除最后的商品',
+  '15': '确定删除吗?',
+  '16': '请同意条款',
+  '17': '请先保存发票信息',
+  '18': '请先保存收货信息',
+  '19': '请完善收货信息',
+  '20': '请完善发票信息',
+  '21': '请输入18位的税务登记号',
+  '22': '请输入正确的手机号码',
+  '23': '注册成功,是否立刻登录?',
+  '24': '密码修改成功,请重新登录',
+  '25': '没有找到相应的记录',
+  '26': '确定要解绑当前相机吗?',
+  '27': '上传的图片类型不正确,请重新上传',
+  '28': '您的云端容量已用完,请删除其他场景或扩容, 以保证足够的容量。',
+  '29': '账号不合法',
+  '30': '请重新登录',
+  '31': '密码位数不能少于8位',
+  '32': '密码修改成功',
+  '33': '解绑成功',
+  '34': '失败',
+  '35': '昵称不能为空',
+  '36': '上传的图片类型不正确,请重新上传',
+  '37': '删除成功',
+  '38': '删除失败',
+  '39': '商品加入购物车成功',
+  '40': '请登录您的用户账号,再进行充值续费。',
+  '41': '去登录',
+  '42': '暂不支持降级套餐,您可进行续费或升级套餐。',
+  '43': '请绑定用户账号后,进行充值扩容。',
+  '44': '分配成功',
+  '45': '已取消协作',
+  '46': '解除成功',
+  '3001': '缺少必要参数',
+  '3002': '访问异常!',
+  '3003': '非法访问!',
+  '3004': '请重新登录',
+  '3005': '验证码错误',
+  '3006': '验证码错误',
+  '3007': '昵称已存在',
+  '3008': '该账号已被注册',
+  '3009': '两次输入的密码不一致',
+  '3010': '昵称长度在2-11位之间',
+  '3011': '密码必须包含英文大小写、数字,长度8-16个字符',
+  '3012': '昵称包含敏感词',
+  '3013': '手机号码格式错误',
+  '3014': '账号或密码不正确',
+  '3015': '用户不存在',
+  '3016': '您没有权限,请联系管理员',
+  '3017': '空文件',
+  '3018': '需要上传或使用的文件不存在',
+  '3019': '邮箱格式不正确',
+  '3020': '邮箱地址已存在',
+  '3021': '账号不存在,请核对后重新输入',
+  '3022': '该场景已添加协作者,请先取消协作后再添加',
+  '3023': '手机验证码获取验证码次数过多,请明天再试',
+  '3024': '不能将场景协作给自己',
+  '3025': '不能将相机分配给自己',
+  '3026': '有部分场景已存在协作者,请先取消协作后再添加。',
+  '3030': '当前会员权益不存在',
+  '3031': '当前会员权益已绑定相机,请先解除绑定',
+  '3032': '当前相机已被授权,请选择其它相机。',
+  '3037': '当前场景不支持该操作。',
+  '4007': '用户名不存在',
+  '4010': '绑定的相机不存在',
+  '4011': '相机已经被绑定',
+  '4012': '账号或密码错误',
+  '4021': '已开票',
+  '5001': 'modeldata.json为空',
+  '5002': 'order值为空',
+  '5003': 'guideSid或order值为空',
+  '5004': 'guideSid或guideName值为空',
+  '5005': '场景为空',
+  '5006': '余额不足',
+  '5007': '非八目场景',
+  '5008': '该场景已经被封存,无法删除',
+  '5009': '场景被删除',
+  '5010': '场景不属于该相机',
+  '5011': '创建',
+  '5012': '数据不正常',
+  '5013': '场景对应的用户名为空',
+  '5014': '该用户无权操作该场景',
+  '5015': '该场景不属于当前登录账号',
+  '5016': '热点外链场景不对',
+  '5030': '同级文件夹名称不能重复',
+  '5038': '生成 obj 失败,请重算场景。',
+  '5051': '无法将上级目录移动到下级目录',
+  '6001': '物理地址重复',
+  '6002': '退充值超过了充值总额',
+  '6003': '该相机未被绑定,请前往 我的相机 先绑定相机后再进行授权',
+  '6004': '表示相机的点数超过了10万',
+  '6005': '会员权益不可授权非本账号相机',
+  '6006': '不支持重复绑定',
+  '6008': '云容量不足,无法复制。',
+  '6009': 'data.fdage文件不存在',
+  '6010': '该SN码已被绑定',
+  '6012': '相机未绑定用户',
+  '6013': '必须输入需迁相机所绑定用户的验证码',
+  '6014': '必须输入目标相机所绑定用户的验证码',
+  '6015': '该相机已添加协作者,请先取消协作后再添加',
+  '6016': '有部分相机已存在协作者,请先取消协作后再添加。',
+  '6028': 'xxxxx',
+  '7001': '标题包含敏感词',
+  '7002': '内容包含敏感词',
+  '7019': '场景升级中,请勿重复升级',
+  '8001': '订单不存在',
+  '8002': '支付失败',
+  '8016': '场景使用中,无法删除,请稍候。',
+  '400002': '当前场景不支持该操作。',
+  '400003': '数据生成失败!',
+  '400013': '该功能需要使用场景原始数据,需要较长时间等待。如需继续使用,请联系客服。',
+  '400014': '原始数据不存在。',
+  '400015': '数据资源冲突,请稍候重试。',
+  '400016': '数据生成失败!'
+
+}

+ 7 - 134
src/i18n/zh.js

@@ -1,141 +1,14 @@
+import header from './modules/zh/header.js';
+import manage from './modules/zh/manage.js';
+import toast from './modules/zh/toast.js';
+
 export default {
     confirm: {
         text: '确认'
     },
-    manage: {
-        "information": {
-            "uploadTip": "图片大小不能大于",
-            "editInformation": "修改信息",
-            "editAddressLinkText": "修改地址",
-            "editInfo": "修改账号信息",
-            "gotoPay": "购买权益",
-            "memberDevice": "权益相机",
-            "expiredDownload": "剩余下载",
-            "takePhotos": "拍摄场景",
-            "defaultAddress": "默认收货地址",
-            "informationOverview": "账户概览",
-            "uploadAvatar": "上传头像",
-            "uploadAvatarTips": "推荐分辨率为:512*512px,<br/>允许JPG、PNG格式,小于1MB",
-            "userName": "账号",
-            "nickName": "昵称",
-            "email": "邮箱",
-            "cameraCount": "相机数量",
-            "memberCount": "权益数量",
-            "tai": "台",
-            "ci": "次",
-            "ge": "个",
-            "tai1": "(台)",
-            "ci1": "(次)",
-            "ge1": "(个)",
-            "editAddress": {
-                "editAddress": "修改收货地址",
-                "country": "国家/地区",
-                "province": "省",
-                "city": "市",
-                "qu": "区",
-                "addressDetail": "详细地址",
-                "receiver": "收件人",
-                "receiverPhone": "手机号码",
-                "internationalCity": "城市",
-                "internationalProvince": "州/省/地区"
-            },
-            "changePassword": {
-                "changePassword": "修改密码",
-                "verCode": "验证码",
-                "newPassword": "新密码",
-                "confirmNewPassword": "确认密码"
-            },
-            "memberTable": {
-                "deviceMember": "会员权益",
-                "bindDeviceNum": "授权相机{num}台",
-                "snInputPlaceHolder": "搜索相机S/N码",
-                "searchNoData": "抱歉,没有找到相关信息。",
-                "nodataPre": "请先购买会员权益",
-                "nodataGoBuy": "购买权益",
-                "isExpired": "已过期"
-            },
-            "nameArr": {
-                "information": "账号信息",
-                "scene": "场景管理",
-                "order": "我的订单",
-                "device": "我的相机",
-                "consumption": "消费记录",
-                "change": "修改密码",
-                "logout": "退出登录",
-                "products": "应用中心"
-            },
-            "deviceSettings": [
-                {
-                    "name": "security",
-                    "items": [
-                        {
-                            "name": "场景管理",
-                            "to": {
-                                "name": "scene"
-                            }
-                        },
-                        {
-                            "name": "退出登录",
-                            "to": "logout"
-                        }
-                    ]
-                }
-            ]
-        },
-    },
-    header: {
-        "core_product": "核心产品",
-        "solutions": "行业解决方案",
-        "solutionsText": "四维时代致力于人工智能三维数字化技术<br/>的研究和应用",
-        "kankan_space": "经典案例",
-        "core_tech": "核心技术",
-        "service": "服务支持",
-        "about": "关于我们",
-        "online_shop": "商城",
-        "foreign": "中德合作",
-        "solutionsHouse": "房产营销",
-        "solutionsExi": "智慧城市",
-        "solutionsSubject": "博物馆",
-        "solutionsShop": "VR购物",
-        "solutionsSec": "刑侦消防",
-        "serviceApp": "App 下载",
-        "joinUs": "加入我们",
-        "serviceUse": "使用教程",
-        "serviceBaoxiu": "售后服务",
-        "serviceVideo": "视频教程",
-        "aboutNews": "新闻中心",
-        "aboutAgent": "成为经销商",
-        "aboutCompany": "公司介绍",
-        "mallPro": "四维看看",
-        "kankanMinion": "四维看见",
-        "Mega": "四维深时",
-        "mallPeijian": "精选配件",
-        "tripod": "三脚架套装",
-        "USBdrive": "U盘",
-        "Battery": "电池",
-        "addService": "会员权益",
-        "corePruductItemPro": "四维看看",
-        "corePruductItemSxz": "四维 • 随心装",
-        "footer": {
-            "bannerTitle": "四维看看,让空间讲故事",
-            "find": "发现精彩",
-            "saleEmail": "销售合作",
-            "meitiEmail": "媒体采访",
-            "phone": "联系电话",
-            "find4D": "探索四维",
-            "kankan": "四维时代",
-            "zhongde": "中德人工智能研究院",
-            "moku": "四维模库",
-            "workTime": "周一至周五 9:00-18:30"
-        },
-        "moreName": {
-            "1": "装修家装",
-            "2": "商圈",
-            "3": "线上会展",
-            "4": "地下管网",
-            "5": "BIM"
-        }
-    },
+    manage,
+    header,
+    toast,
     payInfo: {
         payErr: '支付异常',
         payfail: '支付失败',

+ 15 - 15
src/router/pcRoute.ts

@@ -1,48 +1,48 @@
-const pc = () => import('../components/pc/index.vue')
-const pcHome = () => import('../views/pc/index.vue')
-const information = () => import('../views/pc/information/index.vue')
-const scene = () => import('../views/pc/scene/index.vue')
-const order = () => import('../views/pc/order/index.vue')
-const device = () => import('../views/pc/device/index.vue')
-const appProduct = () => import('../views/pc/appProduct/index.vue')
+const Pc = () => import('../components/pc/index.vue')
+const PcHome = () => import('../views/pc/index.vue')
+const Information = () => import('../views/pc/information/index.vue')
+const Scene = () => import('../views/pc/scene/index.vue')
+const Order = () => import('../views/pc/order/index.vue')
+const Device = () => import('../views/pc/device/index.vue')
+const AppProduct = () => import('../views/pc/appProduct/index.vue')
 const routesP = [{
   path: '/',
-  name: 'Pc',
+  name: 'pc',
   redirect: '/',
-  component: pc,
+  component: Pc,
   children: [{
     path: '/',
     name: 'index',
-    component: pcHome
+    component: PcHome
   }, {
     name: 'information',
     path: '/information',
-    component: information,
+    component: Information,
     meta: { hideFooterFind: false, requireAuth: true }
   },
   {
     name: 'scene',
     path: '/scene',
-    component: scene,
+    component: Scene,
     meta: { hideFooterFind: false, requireAuth: true }
   },
   {
     name: 'order',
     path: '/order',
-    component: order,
+    component: Order,
     meta: { hideFooterFind: false, requireAuth: true }
 
   },
   {
     name: 'device',
     path: '/device',
-    component: device,
+    component: Device,
     meta: { hideFooterFind: false, requireAuth: true }
   },
   {
     name: 'appProduct',
     path: '/appProduct',
-    component: appProduct,
+    component: AppProduct,
     meta: { hideFooterFind: false, requireAuth: true }
   }]
 

+ 16 - 2
src/stores/user.ts

@@ -1,13 +1,17 @@
 import { defineStore } from 'pinia';
+import { getUserInfo } from '@/api/user'
 // defineStore 方法有两个参数,第一个参数是模块化名字(也就相当于身份证一样,不能重复)
 
 // 第二个参数是选项,对象里面有三个属性,相比于vuex 少了一个 mutations.
 export const useUserStore = defineStore('user', {
   state(){  // 存放的就是模块的变量
     return {
-      token: localStorage.getItem('token'),
+      token: localStorage.getItem('token') || 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMzYzMTI2MjkyNiIsImxvZ2luVHlwZSI6InVzZXIiLCJ1c2VyTmFtZSI6IjEzNjMxMjYyOTI2IiwiaWF0IjoxNzU4ODU2MzkzLCJqdGkiOiI3MmY0NWYwMC1iOTFkLTQ1ZGItYWIzZS1iOGNlNGVhNDNkMTMifQ.PG85Q044plwHvak1aRGgKXYYsH1hXhueKK1X-2Q65Lo',
       openId: localStorage.getItem('openId'),
-      isEur: window.location.hostname.includes('eur'),
+      isLogin: false,
+      info: {
+
+      },
     }
   },
   getters:{ // 相当于vue里面的计算属性,可以缓存数据
@@ -16,12 +20,22 @@ export const useUserStore = defineStore('user', {
     },
     getOpenId(state){
       return state.openId || localStorage.getItem('openId')
+    },
+    getUserInfo(state){
+      return state.info || localStorage.getItem('info')
     }
   },
   actions:{ // 可以通过actions 方法,改变 state 里面的值。
     setUserOpenId(value:string){
       this.openId = value
       localStorage.setItem('openId', value,)
+    },
+    async getInfo(){
+      let infoRes = await getUserInfo()
+      console.log('infoRes', infoRes)
+      this.info = infoRes
+      this.isLogin = true
+
     }
   }
 })

+ 27 - 8
src/utils/api.js

@@ -1,9 +1,14 @@
 import axios from "axios";
+import { useUserStore } from '@/stores/user'
+import i18n from '@/i18n/index.js'
 import { ElLoading, ElMessage } from "element-plus";
 let current = (localStorage && localStorage.getItem('language')) || 'zh'
 let token = (localStorage && localStorage.getItem('token')) || 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxNTkxNTgxNjA0MSIsImxvZ2luVHlwZSI6InVzZXIiLCJ1c2VyTmFtZSI6IjE1OTE1ODE2MDQxIiwiaWF0IjoxNjgxODAzNzY1LCJqdGkiOiJhMDU4M2EwZS01M2EzLTQ1YTUtOTI1ZS1kZDgzYzU5Y2Y5MGMifQ.bdu5jqbSxSlo9LH4w_uPEuP67DUJk6w5Yqnu633OtQI'
 // request是一个axios实例,每一个实例你都可以单独定制它的baseURL,超时时间,请求头和一些其他配置项。
 const baseUrl = import.meta.env.VITE_BASE_URL; //接口统一域名
+let store = null
+const timeZone = new Date().getTimezoneOffset()
+const lang = i18n.global.locale;
 const instance = axios.create({
     baseURL: baseUrl,
     // timeout: 60 * 1000, //设置超时
@@ -11,9 +16,9 @@ const instance = axios.create({
         "Content-Type": "application/json;charset=UTF-8;",
         "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
         ".AspNetCore.Culture": "c=zh-Hans|uic=zh-Hans",
-        token:token,
+        token: token,
         current,
-        lang:current,
+        lang: current,
     },
 });
 
@@ -42,8 +47,14 @@ const hideLoading = () => {
 //请求拦截器
 instance.interceptors.request.use(
     (config) => {
-        console.log('config',config.loading,'if',config.loading != true)
-        if(!config.loading){
+        if (!store) store = useUserStore()
+        console.log('config', config.loading, 'if', store)
+        token = store.token
+        if (token) {
+            // 判断是否存在token,如果存在的话,则每个http header都加上token
+            config.headers.token = token //请求头加上token
+        }
+        if (!config.loading) {
             showLoading();
         }
         // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
@@ -61,10 +72,18 @@ instance.interceptors.request.use(
 //响应拦截器
 instance.interceptors.response.use(
     (response) => {
+        console.log('response', i18n, response.data)
+        let t = i18n.global.t;
+        let code = Number(response.data.code)
+
         hideLoading();
-        if (response.data.code !== 200) {
-            ElMessage.error(response.data.message);
-            return  Promise.reject(response.data)
+        if(code == 3004){
+            ElMessage.error(t(`toast.${code}`) || response.data.message);
+            throw response.data.msg || response.data.message;
+            // window.location.href = '/#/login/login?from=%2F'
+        }else if (code !== 200 && code !== 0) {
+            ElMessage.error(t(`toast.${code}`) || response.data.message);
+            return Promise.reject(response.data)
             // router.push("/");
         }
         else return response.data?.data;
@@ -75,7 +94,7 @@ instance.interceptors.response.use(
         let message = "";
         if (error.response && error.response.status) {
             const status = error.response.status;
-            console.log('Error',error.response)
+            console.log('Error', error.response)
             switch (status) {
                 case 400:
                     message = "请求错误";

+ 2 - 2
src/views/mobile/vite.config.ts

@@ -44,12 +44,12 @@ export default defineConfig({
   server: {
     proxy: {
       '/service': {
-        target: 'https://testeur.4dkankan.com/',
+        target: 'https://test.4dkankan.com/',
         changeOrigin: true,
         // rewrite: (path) => path.replace(/^\/api/, '')
       },
       '/ucenter': {
-        target: 'https://testeur.4dkankan.com/',
+        target: 'https://test.4dkankan.com/',
         changeOrigin: true,
         // rewrite: (path) => path.replace(/^\/api/, '')
       }

+ 699 - 0
src/views/pc/device/index_BACKUP_382.vue

@@ -0,0 +1,699 @@
+<template>
+<<<<<<< HEAD
+  <div class="camera-page">
+    <!-- 顶部标签页 -->
+    <div class="camera-header" v-if="Object.values(oldTotalObj).reduce((t,c) => t+c, 0)">
+      <el-tabs v-model="tabActive" @tab-click="handleTabClick">
+        <el-tab-pane 
+          v-for="item in tabList" 
+          :key="item.id"
+          :label="`${item.name}(${totalObj[item.id]})`"
+          :name="item.id"
+          v-show="oldTotalObj[item.id]"
+        />
+      </el-tabs>
+      
+      <!-- 操作栏 -->
+      <div class="main-list">
+        <div class="btns">
+          <!-- 全选 -->
+          <div v-show="isImgType" class="all-select" :class="{disable: !cameraList.length}">
+            <el-checkbox 
+              v-model="selectAll" 
+              @change="handleSelectAll"
+              :disabled="!cameraList.length"
+            >
+              全选
+            </el-checkbox>
+          </div>
+          
+          <!-- 协作按钮 -->
+          <el-button 
+            v-if="tabActive !== 0 && selectedArr.length > 0"
+            type="primary" 
+            size="small"
+            @click="multCop"
+          >
+            协作
+          </el-button>
+          
+          <!-- 解绑按钮 -->
+          <el-button 
+            v-if="tabActive !== 0 && selectedArr.length > 0"
+            type="danger" 
+            size="small"
+            @click="multDel"
+          >
+            解绑
+          </el-button>
+        </div>
+
+        <div class="rig-con">
+          <!-- 添加设备 -->
+          <el-button type="primary" size="small" @click="addDevice">
+            添加设备
+          </el-button>
+          
+          <!-- 视图切换 -->
+          <template v-if="tabActive !== 0">
+            <el-button-group class="view-toggle">
+              <el-button 
+                :type="isImgType ? 'primary' : 'default'"
+                size="small"
+                @click="changeType(true)"
+              >
+                卡片
+              </el-button>
+              <el-button 
+                :type="!isImgType ? 'primary' : 'default'"
+                size="small"
+                @click="changeType(false)"
+              >
+                列表
+              </el-button>
+            </el-button-group>
+          </template>
+          
+          <!-- 搜索框 -->
+          <el-input
+            v-model="searchKey"
+            placeholder="搜索设备ID"
+            class="search-input"
+            @keyup.enter="handleSearch"
+            clearable
+          >
+            <template #prepend>
+              <el-select v-model="selectedType" placeholder="选择类型" style="width: 100px">
+                <el-option
+                  v-for="item in searchTypeList"
+                  :key="item.value"
+                  :label="item.name"
+                  :value="item"
+                />
+              </el-select>
+            </template>
+            <template #append>
+              <el-button @click="handleSearch">
+                搜索
+              </el-button>
+            </template>
+          </el-input>
+        </div>
+      </div>
+    </div>
+
+    <!-- 内容区域 -->
+    <template>
+      <!-- 卡片视图 -->
+      <el-row :gutter="20" class="camera-cards" v-show="isImgType || tabActive === 0">
+        <template v-if="!loading">
+          <el-col
+            :span="8"
+            v-for="(item, index) in cameraList"
+            :key="index"
+            class="camera-item"
+          >
+            <div class="card-wrapper">
+              <el-checkbox 
+                v-model="item.selected" 
+                @change="handleItemSelect(item)"
+                class="item-checkbox"
+              />
+              <camera-item
+                :item="item"
+                :tabActive="tabActive"
+                @handleCooperation="handleCooperation"
+                @unbind="unbind"
+                @renew="handleRenew"
+              />
+            </div>
+          </el-col>
+        </template>
+      </el-row>
+
+      <!-- 列表视图 -->
+      <table-list
+        v-show="!(isImgType || tabActive === 0)"
+        ref="tableRef"
+        @selection-change="selectHandle"
+        @unbind="unbind"
+        @cooperation="handleCooperation"
+        @renew="handleRenew"
+        :header="tabHeader"
+        :selection="cameraList.length > 0"
+        :data="cameraList"
+        :show-view-toggle="false"
+        class="table-list"
+      >
+        <template #item="{ data, type, canclick, item }">
+          <template v-if="canclick">
+            <span
+              class="table-btn"
+              @click="handleCooperation(item)"
+              v-if="item.status !== 0"
+            >
+              协作列表
+            </span>
+
+            <span class="info-wrapper">
+              <span class="table-btn" @mouseover="showInfo = item.id" @mouseout="showInfo = null">
+                详细信息
+              </span>
+              <div
+                v-show="showInfo === item.id"
+                class="info-tooltip"
+              >
+                <div class="info-content">
+                  <span class="th">场景数量</span><span class="th">最后时间</span>
+                  <span class="td">{{ item.sceneNum || '-' }}</span><span class="td">{{ item.lastTime || '-' }}</span>
+                </div>
+              </div>
+            </span>
+          </template>
+          
+          <span v-else-if="type === 'image'" class="flex-avatar">
+            <span v-if="isMember(item)" class="vip-icon"></span>
+            <span v-else-if="isExpiredMember(item)" class="vip-icon vip-expired-icon"></span>
+            {{ item.snCode }}
+          </span>
+          
+          <span v-else-if="type === 'qingkuang'">
+            <span v-if="item.usedSpaceStr && item.totalSpaceStr && item.totalSpaceStr != '0B'">
+              {{ item.usedSpaceStr }}{{ isMember(item) ? "" : ` / ${item.totalSpaceStr}` }}
+            </span>
+            <span v-else>--</span>
+          </span>
+          
+          <span v-else-if="type === 'spaceEndStr'">
+            {{ item.spaceEndStr || "--" }}
+            <span v-if="isExpired(item)" class="expired-icon" @mouseover="showCtrls = item.id" @mouseout="showCtrls = null">
+              ⚠️
+              <div v-show="showCtrls === item.id" class="expired-tooltip">
+                <p>{{ isExpired(item) ? '会员已过期' : '会员即将过期' }}</p>
+                <div class="ctrls-w">
+                  <el-button size="small" @click="showCtrls = null">取消</el-button>
+                  <el-button size="small" type="primary" @click="handleRenew(item)">续费</el-button>
+                </div>
+              </div>
+            </span>
+          </span>
+          
+          <span v-else>{{ data || "-" }}</span>
+        </template>
+      </table-list>
+    </template>
+
+    <!-- 空状态 -->
+    <div class="empty-state" v-if="!loading && !total">
+      <el-empty description="暂无数据">
+        <template v-if="!Object.values(oldTotalObj).reduce((t,c) => t+c, 0)">
+          <el-button type="primary" @click="addDevice">添加设备</el-button>
+        </template>
+      </el-empty>
+    </div>
+
+    <!-- 分页 -->
+    <div class="pagination-wrapper" v-if="total">
+      <el-pagination
+        v-model:current-page="currentPage"
+        v-model:page-size="pageSize"
+        :page-sizes="[9, 18, 36, 72]"
+        :total="total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+
+    <!-- 绑定设备弹窗 -->
+    <el-dialog v-model="showBinding" title="绑定设备" width="600px">
+      <div>绑定设备功能</div>
+      <template #footer>
+        <el-button @click="showBinding = false">取消</el-button>
+        <el-button type="primary" @click="handleBindingSuccess">确定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 续费弹窗 -->
+    <el-dialog v-model="showRenew" title="会员续费" width="600px">
+      <div>会员续费功能</div>
+      <template #footer>
+        <el-button @click="showRenew = false">取消</el-button>
+        <el-button type="primary" @click="handleRenewSuccess">确定</el-button>
+      </template>
+    </el-dialog>
+=======
+  <div class="pcPage">
+      <h-icon class="icon" type="sousuo" />
+>>>>>>> master
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, watch, nextTick } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import TableList from '@/components/tableList/index.vue'
+
+// 静态数据
+const tabList = ref([
+  { id: 0, name: '协作设备' },
+  { id: 4, name: '我的设备' },
+  { id: 10, name: '共享设备' },
+  { id: 11, name: '租赁设备' }
+])
+
+const searchTypeList = ref([
+  { name: '设备编号', value: 2 },
+  { name: '用户名', value: 1 }
+])
+
+// 表格头部配置
+const tabHeader = ref([
+  { key: 'snCode', name: '设备编号', type: 'image', width: 200 },
+  { key: 'status', name: '状态', width: 100 },
+  { key: 'qingkuang', name: '使用情况', type: 'qingkuang', width: 150 },
+  { key: 'spaceEndStr', name: '到期时间', type: 'spaceEndStr', width: 150 },
+  { key: 'operation', name: '操作', canclick: true, width: 200 }
+])
+
+// 静态设备数据
+const mockCameraData = ref([
+  {
+    id: 1,
+    snCode: 'CAM001',
+    status: 1,
+    usedSpaceStr: '2.5GB',
+    totalSpaceStr: '10GB',
+    spaceEndStr: '2024-12-31',
+    spaceEndTime: '2024-12-31',
+    userIncrementId: 'inc_001',
+    sceneNum: 5,
+    lastTime: '2024-01-15 10:30:00',
+    selected: false
+  },
+  {
+    id: 2,
+    snCode: 'CAM002', 
+    status: 1,
+    usedSpaceStr: '5.2GB',
+    totalSpaceStr: '20GB',
+    spaceEndStr: '2024-11-30',
+    spaceEndTime: '2024-11-30',
+    userIncrementId: 'inc_002',
+    sceneNum: 8,
+    lastTime: '2024-01-14 15:20:00',
+    selected: false
+  },
+  {
+    id: 3,
+    snCode: 'CAM003',
+    status: 0,
+    usedSpaceStr: '1.8GB',
+    totalSpaceStr: '5GB', 
+    spaceEndStr: '2024-10-15',
+    spaceEndTime: '2024-10-15',
+    userIncrementId: null,
+    sceneNum: 3,
+    lastTime: '2024-01-13 09:15:00',
+    selected: false
+  }
+])
+
+// 响应式数据
+const tabActive = ref(4)
+const currentPage = ref(1)
+const pageSize = ref(9)
+const total = ref(0)
+const isImgType = ref(localStorage.getItem("isImgTypeForDevice") !== "false")
+const searchKey = ref("")
+const loading = ref(false)
+const selectedArr = ref([])
+const selectAll = ref(false)
+const showBinding = ref(false)
+const showRenew = ref(false)
+const reNewItem = ref({})
+const showCtrls = ref(null)
+const showInfo = ref(null)
+const selectedType = ref({ name: '设备编号', value: 2 })
+
+// 计算属性
+const totalObj = ref({
+  0: 0,
+  4: 0, 
+  10: 0,
+  11: 0
+})
+
+const oldTotalObj = ref({
+  0: 0,
+  4: 0,
+  10: 0, 
+  11: 0
+})
+
+const cameraList = computed(() => {
+  let filteredData = mockCameraData.value
+  
+  // 根据当前标签页过滤
+  if (tabActive.value !== 4) {
+    // 这里可以根据不同的标签页显示不同的数据
+    filteredData = mockCameraData.value.filter(item => {
+      if (tabActive.value === 0) return item.status === 0 // 协作设备
+      if (tabActive.value === 10) return item.id % 2 === 0 // 共享设备
+      if (tabActive.value === 11) return item.id % 3 === 0 // 租赁设备
+      return true
+    })
+  }
+  
+  // 搜索过滤
+  if (searchKey.value) {
+    filteredData = filteredData.filter(item => {
+      if (selectedType.value.value === 2) {
+        return item.snCode.toLowerCase().includes(searchKey.value.toLowerCase())
+      }
+      return true
+    })
+  }
+  
+  // 分页
+  const start = (currentPage.value - 1) * pageSize.value
+  const end = start + pageSize.value
+  total.value = filteredData.length
+  
+  return filteredData.slice(start, end)
+})
+
+// 组件引用
+const tableRef = ref()
+
+// 方法
+const handleTabClick = (tab: any) => {
+  tabActive.value = parseInt(tab.name)
+  currentPage.value = 1
+  updateTotalCount()
+}
+
+const updateTotalCount = () => {
+  // 更新各个标签的数量
+  totalObj.value[0] = mockCameraData.value.filter(item => item.status === 0).length
+  totalObj.value[4] = mockCameraData.value.length
+  totalObj.value[10] = mockCameraData.value.filter(item => item.id % 2 === 0).length
+  totalObj.value[11] = mockCameraData.value.filter(item => item.id % 3 === 0).length
+  
+  oldTotalObj.value = { ...totalObj.value }
+}
+
+const changeType = (status: boolean) => {
+  isImgType.value = status
+  localStorage.setItem("isImgTypeForDevice", status.toString())
+}
+
+const handleSearch = () => {
+  currentPage.value = 1
+}
+
+const handleSelectAll = (val: boolean) => {
+  cameraList.value.forEach(item => {
+    item.selected = val
+  })
+  updateSelectedArr()
+}
+
+const handleItemSelect = (item: any) => {
+  updateSelectedArr()
+  
+  // 更新全选状态
+  selectAll.value = cameraList.value.every(item => item.selected)
+}
+
+const updateSelectedArr = () => {
+  selectedArr.value = cameraList.value.filter(item => item.selected)
+}
+
+const selectHandle = (selection: any[]) => {
+  selectedArr.value = selection
+}
+
+const multCop = () => {
+  if (selectedArr.value.length === 0) {
+    ElMessage.warning('请至少选择一个设备')
+    return
+  }
+  ElMessage.success('批量协作功能')
+}
+
+const multDel = async () => {
+  if (selectedArr.value.length === 0) {
+    ElMessage.warning('请至少选择一个设备')
+    return
+  }
+  
+  try {
+    await ElMessageBox.confirm('确定要解绑选中的设备吗?', '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning'
+    })
+    ElMessage.success('解绑成功')
+    selectedArr.value = []
+  } catch {
+    // 用户取消
+  }
+}
+
+const handleCooperation = (item: any) => {
+  ElMessage.success(`设备 ${item.snCode} 协作功能`)
+}
+
+const unbind = async (item: any) => {
+  try {
+    await ElMessageBox.confirm(`确定要解绑设备 ${item.snCode} 吗?`, '提示', {
+      confirmButtonText: '确定', 
+      cancelButtonText: '取消',
+      type: 'warning'
+    })
+    ElMessage.success('解绑成功')
+  } catch {
+    // 用户取消
+  }
+}
+
+const handleRenew = (item: any) => {
+  reNewItem.value = item
+  showRenew.value = true
+}
+
+const addDevice = () => {
+  showBinding.value = true
+}
+
+const handleBindingSuccess = () => {
+  showBinding.value = false
+  ElMessage.success('绑定成功')
+  updateTotalCount()
+}
+
+const handleRenewSuccess = () => {
+  showRenew.value = false
+  ElMessage.success('续费成功')
+}
+
+const handleSizeChange = (val: number) => {
+  pageSize.value = val
+  currentPage.value = 1
+}
+
+const handleCurrentChange = (val: number) => {
+  currentPage.value = val
+}
+
+// 工具方法
+const isMember = (item: any) => {
+  return item.userIncrementId && !isExpired(item)
+}
+
+const isExpiredMember = (item: any) => {
+  return item.userIncrementId && isExpired(item)
+}
+
+const isExpired = (item: any) => {
+  if (!item.spaceEndTime) return false
+  const expired = Math.floor((new Date(item.spaceEndTime).getTime() - new Date().getTime()) / 86400000) + 1
+  return expired < 0
+}
+
+// 生命周期
+onMounted(() => {
+  updateTotalCount()
+})
+
+// 监听器
+watch(tabActive, () => {
+  selectedArr.value = []
+  selectAll.value = false
+})
+</script>
+
+<style lang="less" scoped>
+.camera-page {
+  padding: 30px;
+  background: #fff;
+  min-height: calc(100vh - 60px);
+}
+
+.camera-header {
+  margin-bottom: 20px;
+  
+  :deep(.el-tabs__header) {
+    margin-bottom: 20px;
+  }
+  
+  .main-list {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 10px 0;
+    border-bottom: 1px solid #e5e5e5;
+    
+    .btns {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+      
+      .all-select {
+        &.disable {
+          opacity: 0.5;
+          pointer-events: none;
+        }
+      }
+    }
+    
+    .rig-con {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+      
+      .view-toggle {
+        margin: 0 10px;
+      }
+      
+      .search-input {
+        width: 300px;
+        
+        :deep(.el-input-group__prepend) {
+          padding: 0;
+          
+          .el-select {
+            border: none;
+            
+            .el-input__wrapper {
+              box-shadow: none;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.camera-cards {
+  padding: 20px 0;
+  
+  .camera-item {
+    margin-bottom: 20px;
+    
+    .card-wrapper {
+      position: relative;
+      background: #f7f7f7;
+      padding: 20px;
+      border-radius: 8px;
+      min-height: 180px;
+      
+      .item-checkbox {
+        position: absolute;
+        top: 10px;
+        left: 10px;
+        z-index: 1;
+      }
+    }
+  }
+}
+
+.table-list {
+  margin: 20px 0;
+}
+
+.empty-state {
+  padding: 60px 0;
+  text-align: center;
+}
+
+.pagination-wrapper {
+  margin-top: 40px;
+  text-align: center;
+}
+
+// 工具提示样式
+.info-wrapper, .expired-icon {
+  position: relative;
+  
+  .info-tooltip, .expired-tooltip {
+    position: absolute;
+    background: #fff;
+    border: 1px solid #e5e5e5;
+    border-radius: 4px;
+    padding: 10px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+    z-index: 1000;
+    min-width: 200px;
+    top: 100%;
+    left: 0;
+    
+    .info-content {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 5px;
+      
+      .th {
+        font-weight: 600;
+        color: #323233;
+      }
+      
+      .td {
+        color: #666;
+      }
+    }
+    
+    .ctrls-w {
+      margin-top: 10px;
+      text-align: right;
+    }
+  }
+}
+
+.table-btn {
+  color: #15bec8;
+  cursor: pointer;
+  
+  &:hover {
+    text-decoration: underline;
+  }
+}
+
+.flex-avatar {
+  display: flex;
+  align-items: center;
+  
+  .vip-icon {
+    width: 16px;
+    height: 16px;
+    background: url('@/assets/images/information/avatar_vip.png') no-repeat center center;
+    background-size: cover;
+    margin-right: 5px;
+    
+    &.vip-expired-icon {
+      opacity: 0.5;
+    }
+  }
+}
+</style>

+ 26 - 0
src/views/pc/device/index_BASE_382.vue

@@ -0,0 +1,26 @@
+<script setup lang="ts">
+import { showConfirm } from '@/components/Toast'
+import { ref, computed, onMounted } from 'vue'
+import { useUserStore } from '@/stores/user'
+import { openPay, getOrderInfo, wxLogin } from '@/api/api'
+import { useRoute } from 'vue-router'
+import { GetRequest, getWeChatCode, getRemark } from '@/utils/index'
+import { useI18n } from 'vue-i18n'
+const route = useRoute()
+const { locale: language, t } = useI18n()
+const userStore = useUserStore();
+const isEur = userStore.isEur
+</script>
+<template>
+  <div class="pcPage">mobilePage
+  </div>
+</template>
+
+<style lang="less" scoped>
+.pcPage {
+  background: #f7f7f7;
+  max-width: 100vw;
+  display: block;
+  color:#202020;
+}
+</style>

+ 694 - 0
src/views/pc/device/index_LOCAL_382.vue

@@ -0,0 +1,694 @@
+<template>
+  <div class="camera-page">
+    <!-- 顶部标签页 -->
+    <div class="camera-header" v-if="Object.values(oldTotalObj).reduce((t,c) => t+c, 0)">
+      <el-tabs v-model="tabActive" @tab-click="handleTabClick">
+        <el-tab-pane 
+          v-for="item in tabList" 
+          :key="item.id"
+          :label="`${item.name}(${totalObj[item.id]})`"
+          :name="item.id"
+          v-show="oldTotalObj[item.id]"
+        />
+      </el-tabs>
+      
+      <!-- 操作栏 -->
+      <div class="main-list">
+        <div class="btns">
+          <!-- 全选 -->
+          <div v-show="isImgType" class="all-select" :class="{disable: !cameraList.length}">
+            <el-checkbox 
+              v-model="selectAll" 
+              @change="handleSelectAll"
+              :disabled="!cameraList.length"
+            >
+              全选
+            </el-checkbox>
+          </div>
+          
+          <!-- 协作按钮 -->
+          <el-button 
+            v-if="tabActive !== 0 && selectedArr.length > 0"
+            type="primary" 
+            size="small"
+            @click="multCop"
+          >
+            协作
+          </el-button>
+          
+          <!-- 解绑按钮 -->
+          <el-button 
+            v-if="tabActive !== 0 && selectedArr.length > 0"
+            type="danger" 
+            size="small"
+            @click="multDel"
+          >
+            解绑
+          </el-button>
+        </div>
+
+        <div class="rig-con">
+          <!-- 添加设备 -->
+          <el-button type="primary" size="small" @click="addDevice">
+            添加设备
+          </el-button>
+          
+          <!-- 视图切换 -->
+          <template v-if="tabActive !== 0">
+            <el-button-group class="view-toggle">
+              <el-button 
+                :type="isImgType ? 'primary' : 'default'"
+                size="small"
+                @click="changeType(true)"
+              >
+                卡片
+              </el-button>
+              <el-button 
+                :type="!isImgType ? 'primary' : 'default'"
+                size="small"
+                @click="changeType(false)"
+              >
+                列表
+              </el-button>
+            </el-button-group>
+          </template>
+          
+          <!-- 搜索框 -->
+          <el-input
+            v-model="searchKey"
+            placeholder="搜索设备ID"
+            class="search-input"
+            @keyup.enter="handleSearch"
+            clearable
+          >
+            <template #prepend>
+              <el-select v-model="selectedType" placeholder="选择类型" style="width: 100px">
+                <el-option
+                  v-for="item in searchTypeList"
+                  :key="item.value"
+                  :label="item.name"
+                  :value="item"
+                />
+              </el-select>
+            </template>
+            <template #append>
+              <el-button @click="handleSearch">
+                搜索
+              </el-button>
+            </template>
+          </el-input>
+        </div>
+      </div>
+    </div>
+
+    <!-- 内容区域 -->
+    <template>
+      <!-- 卡片视图 -->
+      <el-row :gutter="20" class="camera-cards" v-show="isImgType || tabActive === 0">
+        <template v-if="!loading">
+          <el-col
+            :span="8"
+            v-for="(item, index) in cameraList"
+            :key="index"
+            class="camera-item"
+          >
+            <div class="card-wrapper">
+              <el-checkbox 
+                v-model="item.selected" 
+                @change="handleItemSelect(item)"
+                class="item-checkbox"
+              />
+              <camera-item
+                :item="item"
+                :tabActive="tabActive"
+                @handleCooperation="handleCooperation"
+                @unbind="unbind"
+                @renew="handleRenew"
+              />
+            </div>
+          </el-col>
+        </template>
+      </el-row>
+
+      <!-- 列表视图 -->
+      <table-list
+        v-show="!(isImgType || tabActive === 0)"
+        ref="tableRef"
+        @selection-change="selectHandle"
+        @unbind="unbind"
+        @cooperation="handleCooperation"
+        @renew="handleRenew"
+        :header="tabHeader"
+        :selection="cameraList.length > 0"
+        :data="cameraList"
+        :show-view-toggle="false"
+        class="table-list"
+      >
+        <template #item="{ data, type, canclick, item }">
+          <template v-if="canclick">
+            <span
+              class="table-btn"
+              @click="handleCooperation(item)"
+              v-if="item.status !== 0"
+            >
+              协作列表
+            </span>
+
+            <span class="info-wrapper">
+              <span class="table-btn" @mouseover="showInfo = item.id" @mouseout="showInfo = null">
+                详细信息
+              </span>
+              <div
+                v-show="showInfo === item.id"
+                class="info-tooltip"
+              >
+                <div class="info-content">
+                  <span class="th">场景数量</span><span class="th">最后时间</span>
+                  <span class="td">{{ item.sceneNum || '-' }}</span><span class="td">{{ item.lastTime || '-' }}</span>
+                </div>
+              </div>
+            </span>
+          </template>
+          
+          <span v-else-if="type === 'image'" class="flex-avatar">
+            <span v-if="isMember(item)" class="vip-icon"></span>
+            <span v-else-if="isExpiredMember(item)" class="vip-icon vip-expired-icon"></span>
+            {{ item.snCode }}
+          </span>
+          
+          <span v-else-if="type === 'qingkuang'">
+            <span v-if="item.usedSpaceStr && item.totalSpaceStr && item.totalSpaceStr != '0B'">
+              {{ item.usedSpaceStr }}{{ isMember(item) ? "" : ` / ${item.totalSpaceStr}` }}
+            </span>
+            <span v-else>--</span>
+          </span>
+          
+          <span v-else-if="type === 'spaceEndStr'">
+            {{ item.spaceEndStr || "--" }}
+            <span v-if="isExpired(item)" class="expired-icon" @mouseover="showCtrls = item.id" @mouseout="showCtrls = null">
+              ⚠️
+              <div v-show="showCtrls === item.id" class="expired-tooltip">
+                <p>{{ isExpired(item) ? '会员已过期' : '会员即将过期' }}</p>
+                <div class="ctrls-w">
+                  <el-button size="small" @click="showCtrls = null">取消</el-button>
+                  <el-button size="small" type="primary" @click="handleRenew(item)">续费</el-button>
+                </div>
+              </div>
+            </span>
+          </span>
+          
+          <span v-else>{{ data || "-" }}</span>
+        </template>
+      </table-list>
+    </template>
+
+    <!-- 空状态 -->
+    <div class="empty-state" v-if="!loading && !total">
+      <el-empty description="暂无数据">
+        <template v-if="!Object.values(oldTotalObj).reduce((t,c) => t+c, 0)">
+          <el-button type="primary" @click="addDevice">添加设备</el-button>
+        </template>
+      </el-empty>
+    </div>
+
+    <!-- 分页 -->
+    <div class="pagination-wrapper" v-if="total">
+      <el-pagination
+        v-model:current-page="currentPage"
+        v-model:page-size="pageSize"
+        :page-sizes="[9, 18, 36, 72]"
+        :total="total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+
+    <!-- 绑定设备弹窗 -->
+    <el-dialog v-model="showBinding" title="绑定设备" width="600px">
+      <div>绑定设备功能</div>
+      <template #footer>
+        <el-button @click="showBinding = false">取消</el-button>
+        <el-button type="primary" @click="handleBindingSuccess">确定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 续费弹窗 -->
+    <el-dialog v-model="showRenew" title="会员续费" width="600px">
+      <div>会员续费功能</div>
+      <template #footer>
+        <el-button @click="showRenew = false">取消</el-button>
+        <el-button type="primary" @click="handleRenewSuccess">确定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, watch, nextTick } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import TableList from '@/components/tableList/index.vue'
+
+// 静态数据
+const tabList = ref([
+  { id: 0, name: '协作设备' },
+  { id: 4, name: '我的设备' },
+  { id: 10, name: '共享设备' },
+  { id: 11, name: '租赁设备' }
+])
+
+const searchTypeList = ref([
+  { name: '设备编号', value: 2 },
+  { name: '用户名', value: 1 }
+])
+
+// 表格头部配置
+const tabHeader = ref([
+  { key: 'snCode', name: '设备编号', type: 'image', width: 200 },
+  { key: 'status', name: '状态', width: 100 },
+  { key: 'qingkuang', name: '使用情况', type: 'qingkuang', width: 150 },
+  { key: 'spaceEndStr', name: '到期时间', type: 'spaceEndStr', width: 150 },
+  { key: 'operation', name: '操作', canclick: true, width: 200 }
+])
+
+// 静态设备数据
+const mockCameraData = ref([
+  {
+    id: 1,
+    snCode: 'CAM001',
+    status: 1,
+    usedSpaceStr: '2.5GB',
+    totalSpaceStr: '10GB',
+    spaceEndStr: '2024-12-31',
+    spaceEndTime: '2024-12-31',
+    userIncrementId: 'inc_001',
+    sceneNum: 5,
+    lastTime: '2024-01-15 10:30:00',
+    selected: false
+  },
+  {
+    id: 2,
+    snCode: 'CAM002', 
+    status: 1,
+    usedSpaceStr: '5.2GB',
+    totalSpaceStr: '20GB',
+    spaceEndStr: '2024-11-30',
+    spaceEndTime: '2024-11-30',
+    userIncrementId: 'inc_002',
+    sceneNum: 8,
+    lastTime: '2024-01-14 15:20:00',
+    selected: false
+  },
+  {
+    id: 3,
+    snCode: 'CAM003',
+    status: 0,
+    usedSpaceStr: '1.8GB',
+    totalSpaceStr: '5GB', 
+    spaceEndStr: '2024-10-15',
+    spaceEndTime: '2024-10-15',
+    userIncrementId: null,
+    sceneNum: 3,
+    lastTime: '2024-01-13 09:15:00',
+    selected: false
+  }
+])
+
+// 响应式数据
+const tabActive = ref(4)
+const currentPage = ref(1)
+const pageSize = ref(9)
+const total = ref(0)
+const isImgType = ref(localStorage.getItem("isImgTypeForDevice") !== "false")
+const searchKey = ref("")
+const loading = ref(false)
+const selectedArr = ref([])
+const selectAll = ref(false)
+const showBinding = ref(false)
+const showRenew = ref(false)
+const reNewItem = ref({})
+const showCtrls = ref(null)
+const showInfo = ref(null)
+const selectedType = ref({ name: '设备编号', value: 2 })
+
+// 计算属性
+const totalObj = ref({
+  0: 0,
+  4: 0, 
+  10: 0,
+  11: 0
+})
+
+const oldTotalObj = ref({
+  0: 0,
+  4: 0,
+  10: 0, 
+  11: 0
+})
+
+const cameraList = computed(() => {
+  let filteredData = mockCameraData.value
+  
+  // 根据当前标签页过滤
+  if (tabActive.value !== 4) {
+    // 这里可以根据不同的标签页显示不同的数据
+    filteredData = mockCameraData.value.filter(item => {
+      if (tabActive.value === 0) return item.status === 0 // 协作设备
+      if (tabActive.value === 10) return item.id % 2 === 0 // 共享设备
+      if (tabActive.value === 11) return item.id % 3 === 0 // 租赁设备
+      return true
+    })
+  }
+  
+  // 搜索过滤
+  if (searchKey.value) {
+    filteredData = filteredData.filter(item => {
+      if (selectedType.value.value === 2) {
+        return item.snCode.toLowerCase().includes(searchKey.value.toLowerCase())
+      }
+      return true
+    })
+  }
+  
+  // 分页
+  const start = (currentPage.value - 1) * pageSize.value
+  const end = start + pageSize.value
+  total.value = filteredData.length
+  
+  return filteredData.slice(start, end)
+})
+
+// 组件引用
+const tableRef = ref()
+
+// 方法
+const handleTabClick = (tab: any) => {
+  tabActive.value = parseInt(tab.name)
+  currentPage.value = 1
+  updateTotalCount()
+}
+
+const updateTotalCount = () => {
+  // 更新各个标签的数量
+  totalObj.value[0] = mockCameraData.value.filter(item => item.status === 0).length
+  totalObj.value[4] = mockCameraData.value.length
+  totalObj.value[10] = mockCameraData.value.filter(item => item.id % 2 === 0).length
+  totalObj.value[11] = mockCameraData.value.filter(item => item.id % 3 === 0).length
+  
+  oldTotalObj.value = { ...totalObj.value }
+}
+
+const changeType = (status: boolean) => {
+  isImgType.value = status
+  localStorage.setItem("isImgTypeForDevice", status.toString())
+}
+
+const handleSearch = () => {
+  currentPage.value = 1
+}
+
+const handleSelectAll = (val: boolean) => {
+  cameraList.value.forEach(item => {
+    item.selected = val
+  })
+  updateSelectedArr()
+}
+
+const handleItemSelect = (item: any) => {
+  updateSelectedArr()
+  
+  // 更新全选状态
+  selectAll.value = cameraList.value.every(item => item.selected)
+}
+
+const updateSelectedArr = () => {
+  selectedArr.value = cameraList.value.filter(item => item.selected)
+}
+
+const selectHandle = (selection: any[]) => {
+  selectedArr.value = selection
+}
+
+const multCop = () => {
+  if (selectedArr.value.length === 0) {
+    ElMessage.warning('请至少选择一个设备')
+    return
+  }
+  ElMessage.success('批量协作功能')
+}
+
+const multDel = async () => {
+  if (selectedArr.value.length === 0) {
+    ElMessage.warning('请至少选择一个设备')
+    return
+  }
+  
+  try {
+    await ElMessageBox.confirm('确定要解绑选中的设备吗?', '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning'
+    })
+    ElMessage.success('解绑成功')
+    selectedArr.value = []
+  } catch {
+    // 用户取消
+  }
+}
+
+const handleCooperation = (item: any) => {
+  ElMessage.success(`设备 ${item.snCode} 协作功能`)
+}
+
+const unbind = async (item: any) => {
+  try {
+    await ElMessageBox.confirm(`确定要解绑设备 ${item.snCode} 吗?`, '提示', {
+      confirmButtonText: '确定', 
+      cancelButtonText: '取消',
+      type: 'warning'
+    })
+    ElMessage.success('解绑成功')
+  } catch {
+    // 用户取消
+  }
+}
+
+const handleRenew = (item: any) => {
+  reNewItem.value = item
+  showRenew.value = true
+}
+
+const addDevice = () => {
+  showBinding.value = true
+}
+
+const handleBindingSuccess = () => {
+  showBinding.value = false
+  ElMessage.success('绑定成功')
+  updateTotalCount()
+}
+
+const handleRenewSuccess = () => {
+  showRenew.value = false
+  ElMessage.success('续费成功')
+}
+
+const handleSizeChange = (val: number) => {
+  pageSize.value = val
+  currentPage.value = 1
+}
+
+const handleCurrentChange = (val: number) => {
+  currentPage.value = val
+}
+
+// 工具方法
+const isMember = (item: any) => {
+  return item.userIncrementId && !isExpired(item)
+}
+
+const isExpiredMember = (item: any) => {
+  return item.userIncrementId && isExpired(item)
+}
+
+const isExpired = (item: any) => {
+  if (!item.spaceEndTime) return false
+  const expired = Math.floor((new Date(item.spaceEndTime).getTime() - new Date().getTime()) / 86400000) + 1
+  return expired < 0
+}
+
+// 生命周期
+onMounted(() => {
+  updateTotalCount()
+})
+
+// 监听器
+watch(tabActive, () => {
+  selectedArr.value = []
+  selectAll.value = false
+})
+</script>
+
+<style lang="less" scoped>
+.camera-page {
+  padding: 30px;
+  background: #fff;
+  min-height: calc(100vh - 60px);
+}
+
+.camera-header {
+  margin-bottom: 20px;
+  
+  :deep(.el-tabs__header) {
+    margin-bottom: 20px;
+  }
+  
+  .main-list {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 10px 0;
+    border-bottom: 1px solid #e5e5e5;
+    
+    .btns {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+      
+      .all-select {
+        &.disable {
+          opacity: 0.5;
+          pointer-events: none;
+        }
+      }
+    }
+    
+    .rig-con {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+      
+      .view-toggle {
+        margin: 0 10px;
+      }
+      
+      .search-input {
+        width: 300px;
+        
+        :deep(.el-input-group__prepend) {
+          padding: 0;
+          
+          .el-select {
+            border: none;
+            
+            .el-input__wrapper {
+              box-shadow: none;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+.camera-cards {
+  padding: 20px 0;
+  
+  .camera-item {
+    margin-bottom: 20px;
+    
+    .card-wrapper {
+      position: relative;
+      background: #f7f7f7;
+      padding: 20px;
+      border-radius: 8px;
+      min-height: 180px;
+      
+      .item-checkbox {
+        position: absolute;
+        top: 10px;
+        left: 10px;
+        z-index: 1;
+      }
+    }
+  }
+}
+
+.table-list {
+  margin: 20px 0;
+}
+
+.empty-state {
+  padding: 60px 0;
+  text-align: center;
+}
+
+.pagination-wrapper {
+  margin-top: 40px;
+  text-align: center;
+}
+
+// 工具提示样式
+.info-wrapper, .expired-icon {
+  position: relative;
+  
+  .info-tooltip, .expired-tooltip {
+    position: absolute;
+    background: #fff;
+    border: 1px solid #e5e5e5;
+    border-radius: 4px;
+    padding: 10px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+    z-index: 1000;
+    min-width: 200px;
+    top: 100%;
+    left: 0;
+    
+    .info-content {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 5px;
+      
+      .th {
+        font-weight: 600;
+        color: #323233;
+      }
+      
+      .td {
+        color: #666;
+      }
+    }
+    
+    .ctrls-w {
+      margin-top: 10px;
+      text-align: right;
+    }
+  }
+}
+
+.table-btn {
+  color: #15bec8;
+  cursor: pointer;
+  
+  &:hover {
+    text-decoration: underline;
+  }
+}
+
+.flex-avatar {
+  display: flex;
+  align-items: center;
+  
+  .vip-icon {
+    width: 16px;
+    height: 16px;
+    background: url('@/assets/images/information/avatar_vip.png') no-repeat center center;
+    background-size: cover;
+    margin-right: 5px;
+    
+    &.vip-expired-icon {
+      opacity: 0.5;
+    }
+  }
+}
+</style>

+ 27 - 0
src/views/pc/device/index_REMOTE_382.vue

@@ -0,0 +1,27 @@
+<script setup lang="ts">
+import { showConfirm } from '@/components/Toast'
+import { ref, computed, onMounted } from 'vue'
+import { useUserStore } from '@/stores/user'
+import { openPay, getOrderInfo, wxLogin } from '@/api/api'
+import { useRoute } from 'vue-router'
+import { GetRequest, getWeChatCode, getRemark } from '@/utils/index'
+import { useI18n } from 'vue-i18n'
+const route = useRoute()
+const { locale: language, t } = useI18n()
+const userStore = useUserStore();
+const isEur = userStore.isEur
+</script>
+<template>
+  <div class="pcPage">
+      <h-icon class="icon" type="sousuo" />
+  </div>
+</template>
+
+<style lang="less" scoped>
+.pcPage {
+  background: #f7f7f7;
+  max-width: 100vw;
+  display: block;
+  color:#202020;
+}
+</style>

+ 2 - 2
vite.config.ts

@@ -46,12 +46,12 @@ export default defineConfig({
   server: {
     proxy: {
       '/service': {
-        target: 'https://testeur.4dkankan.com/',
+        target: 'https://test.4dkankan.com/',
         changeOrigin: true,
         // rewrite: (path) => path.replace(/^\/api/, '')
       },
       '/ucenter': {
-        target: 'https://testeur.4dkankan.com/',
+        target: 'https://test.4dkankan.com/',
         changeOrigin: true,
         // rewrite: (path) => path.replace(/^\/api/, '')
       }