Browse Source

feat: 首页+信息弹窗20%

chenpeng 3 days ago
parent
commit
3dae624022
38 changed files with 826 additions and 617 deletions
  1. 6 0
      components.d.ts
  2. 1 0
      package.json
  3. 9 0
      pnpm-lock.yaml
  4. 4 4
      src/App.vue
  5. 1 0
      src/assets/icons/arrow_right.svg
  6. 1 0
      src/assets/icons/user.svg
  7. BIN
      src/assets/images/arrow_left.png
  8. BIN
      src/assets/images/arrow_right.png
  9. BIN
      src/assets/images/bill.png
  10. BIN
      src/assets/images/dashboard_head_bg.png
  11. BIN
      src/assets/images/data_img.png
  12. BIN
      src/assets/images/icon_intro.png
  13. BIN
      src/assets/images/icon_withdrawal.png
  14. BIN
      src/assets/images/login_bg.png
  15. BIN
      src/assets/images/people_group.png
  16. BIN
      src/assets/images/people_single.png
  17. BIN
      src/assets/images/right_white_arrow.png
  18. BIN
      src/assets/images/swiper_no_data.png
  19. BIN
      src/assets/images/table_empty.png
  20. BIN
      src/assets/images/zc_logo.png
  21. 19 12
      src/components/layout/BaseHeader.vue
  22. 13 139
      src/components/layout/LoginLayout.vue
  23. 3 3
      src/components/layout/MainLayout.vue
  24. 0 1
      src/components/modal/companyInfo.vue
  25. 57 0
      src/components/modal/index.vue
  26. 0 111
      src/components/modal/info.vue
  27. 115 0
      src/components/modal/userInfo.vue
  28. 2 2
      src/config/interceptors/router.ts
  29. 83 95
      src/store/modules/user.ts
  30. 10 0
      src/utils/validate.ts
  31. 43 0
      src/views/dashboard/_components/header.vue
  32. 147 0
      src/views/dashboard/_components/resourceSwiper.vue
  33. 85 0
      src/views/dashboard/_components/staticData.vue
  34. 80 0
      src/views/dashboard/_components/tableData.vue
  35. 19 116
      src/views/dashboard/index.vue
  36. 38 0
      src/views/dashboard/useGetData.ts
  37. 89 133
      src/views/login/index.vue
  38. 1 1
      uno.config.ts

+ 6 - 0
components.d.ts

@@ -16,6 +16,8 @@ declare module 'vue' {
     ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup']
     ACol: typeof import('ant-design-vue/es')['Col']
     AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
+    ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
+    ADivider: typeof import('ant-design-vue/es')['Divider']
     ADropdown: typeof import('ant-design-vue/es')['Dropdown']
     AEmpty: typeof import('ant-design-vue/es')['Empty']
     AForm: typeof import('ant-design-vue/es')['Form']
@@ -44,21 +46,25 @@ declare module 'vue' {
     CaptchaCodeInput: typeof import('./src/components/CaptchaCodeInput.vue')['default']
     CompanyInfo: typeof import('./src/components/modal/companyInfo.vue')['default']
     DragVerify: typeof import('./src/components/DragVerify.vue')['default']
+    ICustomArrow_right: typeof import('~icons/custom/arrow_right')['default']
     ICustomBrand: typeof import('~icons/custom/brand')['default']
     ICustomHome: typeof import('~icons/custom/home')['default']
     ICustomMessage: typeof import('~icons/custom/message')['default']
     ICustomOrder: typeof import('~icons/custom/order')['default']
     ICustomPerson: typeof import('~icons/custom/person')['default']
     ICustomProduct: typeof import('~icons/custom/product')['default']
+    ICustomUser: typeof import('~icons/custom/user')['default']
     ICustomVip: typeof import('~icons/custom/vip')['default']
     Info: typeof import('./src/components/modal/info.vue')['default']
     LoginLayout: typeof import('./src/components/layout/LoginLayout.vue')['default']
     MainLayout: typeof import('./src/components/layout/MainLayout.vue')['default']
+    Modal: typeof import('./src/components/modal/index.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     ToggleCircle: typeof import('./src/components/ToggleCircle.vue')['default']
     UploadFile: typeof import('./src/components/upload/UploadFile.vue')['default']
     UploadFile_Old: typeof import('./src/components/upload/UploadFile_Old.vue')['default']
     UploadOneImage: typeof import('./src/components/upload/UploadOneImage.vue')['default']
+    UserInfo: typeof import('./src/components/modal/userInfo.vue')['default']
   }
 }

+ 1 - 0
package.json

@@ -31,6 +31,7 @@
     "axios": "^1.6.8",
     "dayjs": "^1.11.12",
     "pinia": "^2.1.7",
+    "swiper": "^11.2.10",
     "unocss": "^0.58.6",
     "vue": "^3.4.32",
     "vue-request": "^2.0.4",

+ 9 - 0
pnpm-lock.yaml

@@ -23,6 +23,9 @@ importers:
       pinia:
         specifier: ^2.1.7
         version: 2.1.7(typescript@5.5.3)(vue@3.4.32(typescript@5.5.3))
+      swiper:
+        specifier: ^11.2.10
+        version: 11.2.10
       unocss:
         specifier: ^0.58.6
         version: 0.58.9(postcss@8.4.39)(rollup@4.18.1)(vite@5.3.4(@types/node@20.14.11)(less@4.2.0)(terser@5.31.3))
@@ -3561,6 +3564,10 @@ packages:
     engines: {node: '>=10.13.0'}
     hasBin: true
 
+  swiper@11.2.10:
+    resolution: {integrity: sha512-RMeVUUjTQH+6N3ckimK93oxz6Sn5la4aDlgPzB+rBrG/smPdCTicXyhxa+woIpopz+jewEloiEE3lKo1h9w2YQ==}
+    engines: {node: '>= 4.7.0'}
+
   table@6.8.2:
     resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==}
     engines: {node: '>=10.0.0'}
@@ -7800,6 +7807,8 @@ snapshots:
       picocolors: 1.0.1
       stable: 0.1.8
 
+  swiper@11.2.10: {}
+
   table@6.8.2:
     dependencies:
       ajv: 8.17.1

+ 4 - 4
src/App.vue

@@ -3,19 +3,19 @@
     :locale="locale"
     :theme="{
       token: {
-        colorPrimary: '#2AC2B5',
+        colorPrimary: '#00DD99',
         borderRadius: 0,
         paddingXS: 0,
       },
     }"
   >
     <router-view />
-    <info-modal :visible="visible" v-if="visible"/>
+    <info-modal :visible="visible" v-if="visible" />
   </a-config-provider>
 </template>
 
 <script setup lang="ts">
-import InfoModal from './components/modal/info.vue'
+import InfoModal from './components/modal/index.vue'
 import useUserStore from '@/store/modules/user'
 const userStore = useUserStore()
 import zhCN from 'ant-design-vue/es/locale/zh_CN'
@@ -24,7 +24,7 @@ import 'dayjs/locale/zh-cn'
 dayjs.locale('zh-cn')
 const locale = zhCN
 const visible = computed(() => {
-  return !!(!userStore.customerType && userStore.token)
+  return !!(!userStore?.userInfo?.infoState && userStore?.token)
 })
 // import { generateArray } from '@/utils/tools'
 // console.log(generateArray(20))

+ 1 - 0
src/assets/icons/arrow_right.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="16" height="16" viewBox="0 0 16 16"><defs><clipPath id="master_svg0_2_0603"><rect x="0" y="16" width="16" height="16" rx="0"/></clipPath></defs><g transform="matrix(0,-1,1,0,-16,16)" clip-path="url(#master_svg0_2_0603)"><g transform="matrix(0,1,-1,0,34.17783212661743,8.488834857940674)"><path d="M17.986318634338378,25.446783492279053C16.99549863433838,24.65413349227905,15.073538634338378,23.116573492279052,13.880648634338378,22.162254492279054C13.45505463433838,21.82177849227905,12.844498634338379,22.130153492279053,12.844498634338379,22.675183492279054C12.844498634338379,24.42881349227905,12.844498634338379,27.48229349227905,12.844498634338379,29.235923492279053C12.844498634338379,29.780953492279053,13.45505463433838,30.089333492279053,13.880648634338378,29.748853492279054C15.07354863433838,28.794543492279054,16.99549863433838,27.25697349227905,17.986318634338378,26.464323492279053C18.311578634338378,26.204113492279053,18.311578634338378,25.707003492279053,17.986318634338378,25.446783492279053" fill-rule="evenodd" fill="#00DD99" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>

+ 1 - 0
src/assets/icons/user.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="24" height="24" viewBox="0 0 24 24"><g><g><path d="M17.25,7.5C17.25,10.3995,14.8995,12.75,12,12.75C9.1005,12.75,6.75,10.3995,6.75,7.5C6.75,4.6005,9.1005,2.25,12,2.25C14.8995,2.25,17.25,4.6005,17.25,7.5ZM15.75,7.5C15.75,5.428929999999999,14.0711,3.75,12,3.75C9.928930000000001,3.75,8.25,5.428929999999999,8.25,7.5C8.25,9.571069999999999,9.928930000000001,11.25,12,11.25C14.0711,11.25,15.75,9.571069999999999,15.75,7.5ZM20.9447,16.2792C21.4455,16.5183,21.75,17.032,21.75,17.5869C21.75,17.5869,21.75,21,21.75,21C21.75,21.4142,21.4142,21.75,21,21.75C21,21.75,3.000002,21.75,3.000002,21.75C2.585788,21.75,2.25,21.4142,2.25,21C2.25,21,2.25,17.5869,2.25,17.5869C2.25,17.032,2.554524,16.5183,3.0552960000000002,16.2792C5.7741,14.9806,8.79976,14.25,12,14.25C15.2002,14.25,18.2259,14.9806,20.9447,16.2792ZM12,15.75C9.05011,15.75,6.26152,16.418599999999998,3.75,17.6097C3.75,17.6097,3.75,20.25,3.75,20.25C3.75,20.25,20.25,20.25,20.25,20.25C20.25,20.25,20.25,17.6097,20.25,17.6097C17.738500000000002,16.418599999999998,14.9499,15.75,12,15.75Z" fill="#00DD99" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></g></svg>

BIN
src/assets/images/arrow_left.png


BIN
src/assets/images/arrow_right.png


BIN
src/assets/images/bill.png


BIN
src/assets/images/dashboard_head_bg.png


BIN
src/assets/images/data_img.png


BIN
src/assets/images/icon_intro.png


BIN
src/assets/images/icon_withdrawal.png


BIN
src/assets/images/login_bg.png


BIN
src/assets/images/people_group.png


BIN
src/assets/images/people_single.png


BIN
src/assets/images/right_white_arrow.png


BIN
src/assets/images/swiper_no_data.png


BIN
src/assets/images/table_empty.png


BIN
src/assets/images/zc_logo.png


+ 19 - 12
src/components/layout/BaseHeader.vue

@@ -1,16 +1,18 @@
 <template>
-  <div class="base-header bg-#fff flex items-center justify-between">
-    <img src="@/assets/images/ejet_new_logo.png" class="w-92px h-38px" alt="" />
+  <div class="base-header bg-#021B1B flex items-center justify-between">
+    <img src="@/assets/images/zc_logo.png" class="w-238px h-32px" alt="" />
+    <div class="mx-auto text-#fff text-20px">
+      Welcome to the EJET PartnerShare Platform
+    </div>
     <div>
       <!-- <img src="@/assets/images/user.png" alt="" class="logo" />
       <img src="@/assets/images/user.png" alt="" class="logo" /> -->
       <a-dropdown v-if="userStore.token" placement="bottom">
-        <div class="ant-dropdown-link cursor-pointer flex items-center" @click.prevent>
-          <img class="w-28 h-28 rounded-full" v-if="userStore.userInfo.photo" :src="userStore.userInfo.photo" alt="" />
-          <i-custom-person v-else class="w-28 h-28 mr-10 c-primary" />
-          <span class="mr-10">{{ userStore.userInfo?.username }}</span>
-          <!-- <DownOutlined /> -->
+        <div class="ant-dropdown-link cursor-pointer bg-#fff b-rd-50% p-10px flex items-center" @click.prevent>
+          <img class="w-20 h-20 rounded-full" v-if="userStore.userInfo.photo" :src="userStore.userInfo.photo" alt="" />
+          <i-custom-user v-else class="w-20 h-20   c-primary" />
         </div>
+        <span class="mr-10">{{ userStore.userInfo?.username }}</span>
         <template #overlay>
           <a-menu>
             <a-menu-item>
@@ -22,7 +24,8 @@
           </a-menu>
         </template>
       </a-dropdown>
-      <router-link v-else to="login" class="fw-700 text-14px text-#696F79 underline" v-show="showLogin">登录/注册</router-link>
+      <router-link v-else to="login" class="fw-700 text-14px text-#696F79 underline"
+        v-show="showLogin">登录/注册</router-link>
     </div>
   </div>
 </template>
@@ -47,17 +50,21 @@ const logout = () => {
 </script>
 <style lang="less">
 .base-header {
-  padding: 21px 56px 21px 74px;
+  padding: 20px 56px;
   position: relative;
+
   &::after {
     content: '';
     position: absolute;
     left: 0;
     right: 0;
-    bottom: -10px; /* 阴影距离盒子底部的距离,负数表示在盒子外部 */
-    height: 10px; /* 阴影的高度 */
+    bottom: -10px;
+    /* 阴影距离盒子底部的距离,负数表示在盒子外部 */
+    height: 10px;
+    /* 阴影的高度 */
     background: linear-gradient(to bottom, rgba(126, 125, 125, 0.2), rgba(0, 0, 0, 0));
-    z-index: 1; /* 确保伪元素在盒子之下 */
+    z-index: 1;
+    /* 确保伪元素在盒子之下 */
   }
 }
 </style>

+ 13 - 139
src/components/layout/LoginLayout.vue

@@ -1,163 +1,37 @@
 <template>
-  <div class="page flex h-screen flex-col bg-#000000">
-    <!-- <BaseHeader class="flex-shrink-0"></BaseHeader> -->
-    <div class="flex justify-between pt-40px px-68px">
-      <img src="@/assets/images/ejet_logo.png" class="2xl:w-180px lg:w-140px object-contain" alt="" srcset="" />
-      <img src="@/assets/images/ejet_small_logo.png" class="2xl:w-35px lg:w-28px object-contain" alt="" srcset="" />
-    </div>
+  <div class="page flex h-screen flex-col bg-#fff">
     <div class="flex main pos-relative">
-      <div class="side-left h-full flex justify-center items-center">
-        <div>
-          <div class="text-#4DD8D8 w-fit fw-700 text-14px b-1px b-rd-226px b-solid b-#4DD8D8 py-8px px-15px">
-            全方位的供应链出海解决方案,助力中国品牌全球化
-          </div>
-          <div class="lg:mt-16px lg:mb-10px 2xl:mt-25px 2xl:mb-14px 2xl:text-36px lg:text-28px text-#fff">携手共创供应链品牌</div>
-          <div class="2xl:text-42px lg:text-32px fw-700 custom-font-gradient">出海新未来</div>
-          <div class="2xl:my-60px lg:mt-20px lg:mb-60px">
-            <div class="text-12px text-#fff 2xl:mb-44px lg:mb-30px">入驻易杰,即可享受以下三大核心服务:</div>
-            <div v-for="item in list" :key="item.id" class="2xl:mb-60px lg:mb-30px last:mb-0 flex items-center">
-              <img :src="item.img" class="2xl:w-56px lg:w-40px mr-28px object-contain" alt="" srcset="" />
-              <div>
-                <img :src="item.logo" class="w-135px h-25px mb-14px" alt="" />
-                <div class="text-12px w-300px text-#fff lh-20px font-title">{{ item.title }}</div>
-              </div> 
-            </div>
+      <div class="side-left flex-1 h-full flex justify-center items-center left-bg">
+        <div class="flex flex-col h-full pt-150px pb-100px items-center justify-between">
+          <div class="text-#fff text-40px lh-60px font-bold text-center">
+            <div>EJET Partner</div>
+            <div>Referral Program</div>
           </div>
+          <img src="@/assets/images/zc_logo.png" class="w-260px h-36px" alt="" srcset="" />
         </div>
       </div>
-      <div class="pos-absolute flex 2xl:bottom-60px lg:bottom-30px left-70px">
-        <div v-for="(item, index) in platForms" :key="index">
-          <a-popover placement="top">
-            <template #content>
-              <img :src="item.img2" class="w-140px h-140px" alt="" />
-            </template>
-            <img class="mr-30px" :src="item.img" :style="item.style" alt="" />
-          </a-popover>
-        </div>
-      </div>
-      <div class="side-right w-50% flex items-center px-12 justify-center">
+      <div class="side-right w-50% flex items-center justify-center">
         <div class="pos-relative">
           <slot></slot>
-          <img
-            src="@/assets/images/bg_img01.png"
-            class="2xl:w-750px lg:w-600px 2xl:h-210px lg:h-150px pos-absolute pointer-events-none left-50% transform-translate-x--50% 2xl:bottom--108px lg:bottom--80px"
-            alt=""
-            srcset=""
-          />
         </div>
-        <!-- <img src="@/assets/images/bg_img02.png" alt="pos-absolute bottom-0" srcset="" /> -->
       </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import BaseHeader from 'Components/layout/BaseHeader.vue'
-import img01 from '@/assets/images/ejet_img01.png'
-import img02 from '@/assets/images/ejet_img02.png'
-import img03 from '@/assets/images/ejet_img03.png'
-import logo01 from '@/assets/images/ejet_logo01.png'
-import logo02 from '@/assets/images/ejet_logo02.png'
-import logo03 from '@/assets/images/ejet_logo03.png'
-
-import platform01 from '@/assets/images/wechat.png'
-import platform02 from '@/assets/images/wechatVideo.png'
-import platform03 from '@/assets/images/douyin.png'
-import platform04 from '@/assets/images/xhs.png'
-import platform011 from '@/assets/images/wechat_code.png'
-import platform021 from '@/assets/images/wechatVideo_code.jpeg'
-import platform031 from '@/assets/images/douyin_code.jpeg'
-import platform041 from '@/assets/images/xhs_code.jpeg'
-
-const list = [
-  {
-    id: 1,
-    img: img01,
-    logo: logo01,
-    title: '提供全方位的出海营销和品牌赋能服务,助力中国制造商进入国际市场。',
-  },
-  {
-    id: 2,
-    img: img02,
-    logo: logo02,
-    title: '精选中国优质供应链品牌,打造高效国际商贸平台,帮助品牌走向全球。',
-  },
-  {
-    id: 3,
-    img: img03,
-    logo: logo03,
-    title: '提供全球智慧采购解决方案,优化流程、降低成本,提升国际竞争力。',
-  },
-]
-const platForms = [
-  {
-    img: platform01,
-    img2: platform011,
-    style: {
-      width: '20px',
-      height: '20px',
-    },
-  },
-  {
-    img: platform02,
-    img2: platform021,
-    style: {
-      width: '26px',
-      height: '26px',
-    },
-  },
-  {
-    img: platform03,
-    img2: platform031,
-    style: {
-      width: '26px',
-      height: '26px',
-    },
-  },
-  {
-    img: platform04,
-    img2: platform041,
-    style: {
-      width: '26px',
-      height: '26px',
-    },
-  },
-]
 </script>
 
 <style scoped lang="less">
 .page {
-  background-image: url('../../assets/images/bg_img02.png');
-  background-size: cover;
-  background-position: center;
-
   .main {
-    min-height: calc(100vh - 80px);
+    min-height: calc(100vh);
     height: fit-content;
-  }
-
-  .side-left {
-    width: 50%;
-    height: 100%;
-
-    // background-image: url('../../assets/images/login_bg.jpg');
-    // background-size: cover;
-    // background-position: center;
-    .custom-font-gradient {
-      color: transparent;
-      /* 字体颜色设为透明,因为我们用背景色来显示 */
-      background-image: linear-gradient(to right, #0068ff, #10ffd1);
-      /* 线性渐变从左到右,颜色从#ff7e5f渐变到#feb47b */
-      -webkit-background-clip: text;
-      /* 非标准属性,用于旧版WebKit浏览器 */
-      background-clip: text;
-      /* 标准属性 */
-      -webkit-text-fill-color: transparent;
-      /* 禁止文本填充颜色,只显示背景色 */
-    }
 
-    .font-title {
-      letter-spacing: 3px;
+    .left-bg {
+      background-image: url('@/assets/images/login_bg.png');
+      background-size: cover;
+      background-position: center;
     }
   }
 }

+ 3 - 3
src/components/layout/MainLayout.vue

@@ -3,7 +3,7 @@
     <BaseHeader />
     <div class="main-container flex flex-1">
       <!-- <BaseSideNav /> -->
-      <div class="main-view ml-10 flex-1 overflow-auto">
+      <div class="main-view py-32px px-20px flex-1 overflow-auto">
         <router-view />
       </div>
     </div>
@@ -11,12 +11,12 @@
 </template>
 <script setup lang="ts">
 import BaseHeader from './BaseHeader.vue'
-import BaseSideNav from './BaseSideNav.vue'
+// import BaseSideNav from './BaseSideNav.vue'
 </script>
 <style lang="less">
 .main-layout {
   min-width: 1440px;
-  background-color: #f7f8fa;
+  background-color: #F3F3F3;
   .main-container {
     height: calc(100% - 80px);
   }

+ 0 - 1
src/components/modal/companyInfo.vue

@@ -111,7 +111,6 @@ const onSubmit = async () => {
     }
     const res = await submitCompanyApi(params)
     if (res.data.success) {
-      localStorage.setItem('customerType', props.isMyself)
       emit('success')
     }
   } catch (error) {

+ 57 - 0
src/components/modal/index.vue

@@ -0,0 +1,57 @@
+<script lang="ts" setup>
+// import companyInfo from './companyInfo.vue'
+import userInfo from './userInfo.vue'
+import { CUR_STATUS } from '@/enums/common'
+const visible = defineModel('visible', { default: false })
+enum STATUS {
+  FirstStep = 'info',
+  SecondStep = 'success',
+}
+const status = ref(STATUS.FirstStep)
+const countNum = ref(20)
+
+const onSuccess = (val: CUR_STATUS) => {
+  status.value = STATUS.SecondStep
+  // countDown()
+}
+
+const countDown = () => {
+  countNum.value = 20
+  const timer = setInterval(() => {
+    countNum.value--
+    if (countNum.value === 0) {
+      clearInterval(timer)
+      visible.value = false
+    }
+  }, 1000)
+}
+</script>
+
+<template>
+  <a-modal v-model:open="visible" :footer="false" :width="560" wrapClassName="custom-info" :keyboard="false" :maskClosable="false" :closable="false">
+    <div v-if="status === STATUS.FirstStep">
+      <user-info @success="onSuccess" v-model:visible="visible" />
+    </div>
+    <div v-if="status === STATUS.SecondStep" class="text-center py-100px">
+      <div>
+        <img class="w-88px h-88px" src="@/assets/images/smile.png" alt="" />
+        <div>
+          <h3 class="c-#000 fw-700 fs-34 mb-4px mt-37px">Information submitted</h3>
+          <div class="c-333 mb-26px">We will conduct the audit within 7 working days, and the audit results will be communicated via email</div>
+          <div class="c-333 mb-26px">You can view the submission form in the Personal Center - My Profile</div>
+        </div>
+      </div>
+    </div>
+  </a-modal>
+</template>
+
+<style lang="less">
+.custom-info {
+  .ant-modal {
+    .ant-modal-content {
+      border-radius: 18px;
+      padding: 0 !important;
+    }
+  }
+}
+</style>

+ 0 - 111
src/components/modal/info.vue

@@ -1,111 +0,0 @@
-<script lang="ts" setup>
-import companyInfo from './companyInfo.vue'
-import { isEmptyObject } from '@/utils'
-import { CUR_STATUS } from '@/enums/common'
-const visible = defineModel('visible', { default: false })
-enum STATUS {
-  FirstStep = 'select',
-  SecondStep = 'companyInfo',
-  ThirdStep = 'success',
-}
-
-const status = ref(STATUS.FirstStep)
-const countNum = ref(20)
-const isMyself = ref<CUR_STATUS>(CUR_STATUS.A)
-
-const obj_cache = JSON.parse(sessionStorage.getItem('cache_register'))
-if (obj_cache && !isEmptyObject(obj_cache)) {
-  status.value = STATUS.SecondStep
-  isMyself.value = obj_cache.type
-}
-
-const isSelf = computed(() => {
-  return isMyself.value === CUR_STATUS.B
-})
-const onSelect = (val: CUR_STATUS) => {
-  status.value = STATUS.SecondStep
-  isMyself.value = val
-}
-const onSuccess = () => {
-  status.value = STATUS.ThirdStep
-  countDown()
-}
-const countDown = () => {
-  countNum.value = 20
-  const timer = setInterval(() => {
-    countNum.value--
-    if (countNum.value === 0) {
-      clearInterval(timer)
-      visible.value = false
-    }
-  }, 1000)
-}
-</script>
-
-<template>
-  <a-modal
-    v-model:open="visible"
-    :footer="false"
-    :width="status === STATUS.FirstStep ? '646px' : '1080px'"
-    wrapClassName="custom-info"
-    :keyboard="false"
-    :maskClosable="false"
-    :closable="false"
-  >
-    <div class="pt-150px pb-180px px-135px text-center" v-if="status === STATUS.FirstStep">
-      <img class="w-88px h-88px" src="@/assets/images/check.png" alt="" />
-      <div class="my-27px fw-700 text-24px text-#000/85">注册成功</div>
-      <div class="text-14px text-#333">请选择您的情况和需求,以便后续更好服务对接:</div>
-
-      <a-button
-        class="w-full my-20px bg-#fff !b-rd-6px text-#000/60 !text-14px !h-56px !hover:text-#0068FF !hover:border-#0068FF"
-        size="large"
-        @click="onSelect(CUR_STATUS.B)"
-        >自有工厂/供应链,入驻为供应商 & 加入易杰出海圈</a-button
-      >
-      <a-button
-        class="w-full bg-#fff !b-rd-6px text-#000/60 !text-14px !h-56px !hover:text-#0068FF !hover:border-#0068FF"
-        size="large"
-        @click="onSelect(CUR_STATUS.A)"
-        >无自有工厂/供应链,加入易杰出海圈</a-button
-      >
-      <div class="mt-20 text-16px cursor-pointer text-#bbb hover:underline" @click="visible = false">跳过>>></div>
-    </div>
-    <div v-if="status === STATUS.SecondStep">
-      <company-info :isMyself @success="onSuccess" v-model:visible="visible" />
-    </div>
-    <div v-if="status === STATUS.ThirdStep" class="text-center py-100px">
-      <div>
-        <img class="w-88px h-88px" v-if="isSelf" src="@/assets/images/check.png" alt="" />
-        <img class="w-88px h-88px" v-else src="@/assets/images/smile.png" alt="" />
-        <div>
-          <h3 class="c-#000 fw-700 fs-34 mb-4px mt-37px">{{ isSelf ? '提交成功' : '欢迎加入' }}</h3>
-          <div class="c-333 mb-26px">
-            {{
-              isSelf
-                ? '您已提交成功, 请等待工作人员审核(约1-2个工作日)'
-                : '如果您后续自有供应链,并期望成为易杰供应商合作伙伴,可联系我们更改您的状态。'
-            }}
-          </div>
-          <a-button
-            class="w-376px bg-#fff b-#0068FF !b-rd-6px text-#0068FF !h-56px !hover:text-#0068FF !hover:b-#0068FF"
-            size="large"
-            @click="visible = false"
-            >已知晓,现在去“易杰出海圈”看看({{ countNum }}s)</a-button
-          >
-        </div>
-      </div>
-    </div>
-  </a-modal>
-</template>
-
-<style lang="less">
-.custom-info {
-  .ant-modal {
-    .ant-modal-content {
-      border-radius: 18px;
-      padding: 0 !important;
-    }
-  }
-}
-</style>

+ 115 - 0
src/components/modal/userInfo.vue

@@ -0,0 +1,115 @@
+<script lang="ts" setup>
+import { message } from 'ant-design-vue'
+import { getIndustryCategoryApi } from 'API/common'
+import { CUR_STATUS } from '@/enums/common'
+import { submitCompanyApi, getCompanyInfoApi } from 'API/user'
+import useUserStore from '@/store/modules/user'
+const userStore = useUserStore()
+const visible = defineModel('visible', { default: false })
+const emit = defineEmits(['success'])
+
+const checkedList = ref([])
+const checkOther = ref(false)
+const otherContent = ref()
+
+const plainOptions = ['海外资讯、出海信息、方法论案例等', '出海会员俱乐部和圈层活动', '海外游学、考察、资源对接等', '出海/营销等具体项目咨询']
+const postForm = ref<any>({
+  busLicense: '',
+  name: '',
+  account: '',
+  regCode: '',
+  legalMan: '',
+  address: '',
+  contactName: '',
+  contactMobile: '',
+  contactPosition: '',
+  wxNumber: '',
+  contactEmail: '',
+  category: '',
+  categoryTemporaryArr: null,
+  purpose: '',
+})
+const activeTab = ref('name')
+const companyName = ref()
+
+const options = ref<any>([])
+
+watch(
+  () => checkOther.value,
+  (value) => {
+    if (!value) {
+      otherContent.value = ''
+    }
+  }
+)
+
+const handleUploadSuccess = (fileList: any) => {
+  if (fileList.length) {
+    const data = fileList[0].response.result
+    postForm.value.address = data.address
+    postForm.value.name = data.companyName
+    postForm.value.legalMan = data.legalMan
+    postForm.value.regCode = data.regCode
+  }
+}
+const getCompanyInfo = async () => {
+  try {
+    if (!companyName.value) {
+      return message.error('请输入企业名称查询')
+    }
+    const res = await getCompanyInfoApi({ companyName: companyName.value })
+    if (res.data.success) {
+      postForm.value.address = res.data.result.address
+      postForm.value.name = res.data.result.companyName
+      postForm.value.legalMan = res.data.result.legalMan
+      postForm.value.regCode = res.data.result.regCode
+    }
+  } catch (error) {
+    console.log(error)
+  }
+}
+const handleOther = () => {
+  let otherStr = otherContent.value ? (checkedList.value.length ? '&' + otherContent.value : otherContent.value) : ''
+  let checkedListStr = checkedList.value.length ? checkedList.value.join('&') : ''
+  return checkedListStr + otherStr
+}
+
+const onSubmit = async () => {
+  try {
+    postForm.value.account = userStore.userInfo.accountEmail || userStore.userInfo.phoneNumber
+    const params = {
+      ...postForm.value,
+    }
+    const res = await submitCompanyApi(params)
+    if (res.data.success) {
+      emit('success')
+    }
+  } catch (error) {
+    console.log('error---', error)
+  }
+}
+const getCategoryList = async () => {
+  const res = await getIndustryCategoryApi({ async: 0, pcode: 'C05' })
+  if (res.data.success) {
+    options.value = res.data.result || []
+  }
+  // const res = await apiCategory({ all: false, code: 'C05' })
+  // if (res.data.success) {
+  //   options.value = res.data.result || []
+  // }
+}
+getCategoryList()
+</script>
+
+<template>
+  <div class="p-40px">
+    <div class="text-32px fw-700 text-#3d3d3d text-center">Improve Account Information</div>
+    <div class="text-14px text-#999 text-center my-10px">Improve information to ensure that commissions are received normally</div>
+    <div>
+      <div>撒噶时光给</div>
+    </div>
+  </div>
+</template>
+
+<style lang="less" scoped>
+</style>

+ 2 - 2
src/config/interceptors/router.ts

@@ -6,7 +6,7 @@ import useCommonStore from 'Store/modules/common'
 // userStore.authLogin().then((res) => {
 //   console.log(res)
 // })
-const whiteList = ['/login','/dashboard','/register', '/forgetPwd', '/accountStatus', '/accountStatus/fail', '/agreement', '/welcome', '/404']
+const whiteList = ['/login','/register', '/forgetPwd', '/accountStatus', '/accountStatus/fail', '/agreement', '/welcome', '/404']
 export async function routerBeforeEachFunc(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
   const userStore = useUserStore()
   const commonStore = useCommonStore()
@@ -20,7 +20,7 @@ export async function routerBeforeEachFunc(to: RouteLocationNormalized, from: Ro
     }
   } else {
     // 获取全局字典 分类
-    commonStore.getCategory()
+    // commonStore.getCategory()
     // if (!data) return
     if (['/login', '/register', '/forgetPwd', '/welcome'].includes(to.path)) {
       // console.log('已登录')

+ 83 - 95
src/store/modules/user.ts

@@ -10,103 +10,91 @@ interface ILogin {
   password: string
 }
 // 第一个参数是该 store 的唯一 id
-const userStore = defineStore('user', {
-  state: () => {
-    return {
-      userStatus: '',
-      token: localStorage.getItem('token') || '',
-      customerType: localStorage.getItem('customerType') || '',
-      userInfo: localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo') as string) : {},
-    }
-  },
-  actions: {
-    login(params: LoginData) {
-      return apiLogin(params).then((response) => {
-        const { data } = response
-        if (data?.code === 200) {
-          console.log(data)
-          const info = data.result
-
-          if (info.userEnableStatus === '1') {
-            // 0正常,1禁用
-            Modal.error({
-              title: '提示',
-              content: '您的账号已被冻结,暂时无法登录',
-              centered: true,
-              okText: '确定',
-              onOk: () => {
-                // window.location.reload()
-              },
-            })
-          } else if (info.userStatus === '0') {
-            // 0正常,1待审核,2审核不通过
-            this.userStatus = info.userStatus
-            this.token = info.token
-            localStorage.setItem('token', this.token)
-            this.userInfo = info.userInfo
-            localStorage.setItem('userInfo', JSON.stringify(this.userInfo))
-            this.customerType = info.userInfo.type || '';
-            localStorage.setItem('customerType', this.customerType)
-            router.push('/dashboard')
-          } else if (info.userStatus === '1') {
-            // 待审核
-            // router.push('/accountStatus?step=1')
-          } else if (info.userStatus === '2') {
-            // 审核不通过
-            // router.push('/accountStatus/fail')
-          }
-        }
-      })
-    },
-    register(params: ILogin) {
-      apiRegister(params).then((response: AxiosResponse) => {
-        console.log(response)
-        const { data } = response
-        if (data?.code === 200) {
-          // router.push('/accountStatus?step=0')
-          message.success('注册成功!')
-          router.push('/login')
-        }
-        // this.token = 'token'
-        // localStorage.setItem('token', this.token)
-        // this.userInfo = { name: 'test' }
-        // localStorage.setItem('userInfo', JSON.stringify(this.userInfo))
-      })
-    },
-    getUserInfo() {
-      apiUserInfo().then((response) => {
-        const data = response.data.result
-
-        this.userStatus = data.userStatus
-        this.token = data.token
-        localStorage.setItem('token', this.token)
-        this.userInfo = data
-        localStorage.setItem('userInfo', JSON.stringify(this.userInfo))
-      })
+const userStore = defineStore(
+  'user',
+  {
+    state: () => {
+      return {
+        token: localStorage.getItem('token') || '',
+        userInfo: localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo') as string) : {},
+      }
     },
-    logout() {
-      apiLogout().then((response: AxiosResponse) => {
-        if (response.data.success) {
-          this.feLogout()
-        }
-      })
-    },
-    // 前端登出(清空存储)
-    feLogout() {
-      this.userStatus = ''
-      localStorage.removeItem('userStatus')
-      this.token = ''
-      localStorage.removeItem('token')
-      this.userInfo = {}
-      localStorage.removeItem('userInfo')
-      router.push('/login')
+    actions: {
+      login(params: LoginData) {
+        return apiLogin(params).then((response) => {
+          const { data } = response
+          if (data?.code === 200) {
+            console.log(data, 'login')
+            const info = data.result
+            if (info.userInfo.state) {  // 暂时模拟,后续删除
+            // if (!info.userInfo.state) {
+              //账号状态 0禁用,1正常
+              Modal.error({
+                title: '提示',
+                content: '您的账号已被冻结,暂时无法登录',
+                centered: true,
+                okText: '确定',
+                onOk: () => {
+                  // window.location.reload()
+                },
+              })
+            } else {
+              this.token = info.token
+              localStorage.setItem('token', this.token)
+              this.userInfo = info.userInfo
+              localStorage.setItem('userInfo', JSON.stringify(this.userInfo))
+              router.push('/dashboard')
+            }
+          }
+        })
+      },
+      register(params: ILogin) {
+        apiRegister(params).then((response: AxiosResponse) => {
+          console.log(response)
+          const { data } = response
+          if (data?.code === 200) {
+            // router.push('/accountStatus?step=0')
+            message.success('注册成功!')
+            router.push('/login')
+          }
+          // this.token = 'token'
+          // localStorage.setItem('token', this.token)
+          // this.userInfo = { name: 'test' }
+          // localStorage.setItem('userInfo', JSON.stringify(this.userInfo))
+        })
+      },
+      getUserInfo() {
+        apiUserInfo().then((response) => {
+          const data = response.data.result
+          this.token = data.token
+          localStorage.setItem('token', this.token)
+          this.userInfo = data
+          localStorage.setItem('userInfo', JSON.stringify(this.userInfo))
+        })
+      },
+      logout() {
+        apiLogout().then((response: AxiosResponse) => {
+          if (response.data.success) {
+            this.feLogout()
+          }
+        })
+      },
+      // 前端登出(清空存储)
+      feLogout() {
+        this.token = ''
+        localStorage.removeItem('token')
+        this.userInfo = {}
+        localStorage.removeItem('userInfo')
+        router.push('/login')
+      },
     },
+    // other options...
   },
-  // other options...
-},{
-  persist: {
-    enabled: true,
-  },
-})
+  {
+    persist: {
+      enabled: true,
+    },
+  }
+)
 
 export default userStore

+ 10 - 0
src/utils/validate.ts

@@ -52,6 +52,16 @@ export const validatePhoneOrEmail = async (_rule: Rule, value: string) => {
     return Promise.resolve()
   }
 }
+export const validateEmail = async (_rule: Rule, value: string) => {
+  if (value === '') {
+    return Promise.reject('Email number is required')
+  } else {
+    if (!isEmail(value)) {
+      return Promise.reject('Email format is incorrect')
+    }
+    return Promise.resolve()
+  }
+}
 export const maskedPhoneOrEmail = (value: string) => {
   if (!value) return ''
   const _val = JSON.parse(JSON.stringify(value))

+ 43 - 0
src/views/dashboard/_components/header.vue

@@ -0,0 +1,43 @@
+<template>
+    <div class="flex items-center justify-between bg-#fff p-32px b-rd-16px">
+        <div class="flex items-center">
+            <div class="bg-#EEEEEE b-rd-50% p-10px flex items-center" @click.prevent>
+                <img class="w-20 h-20 rounded-full" v-if="userInfo?.photo" :src="userInfo?.photo" alt="" />
+                <i-custom-user v-else class="w-40 h-40 c-primary" />
+            </div>
+            <div class="ml-22px">
+                <div class="text-22px fw-bold text-#223354 mb-10px">
+                    {{ userInfo?.FirstName || 'hezuo' }} {{ userInfo?.LastName || 'Partner' }}
+                </div>
+                <!-- 合作伙伴 当前账号状态-->
+                <div class="py-4px px-10px inline-block text-center b-rd-200px text-12px bg-#eee">{{ userInfo?.email ||
+                    'Review rejected' }}</div>
+            </div>
+            <div class="ml-32px py-8px px-12px b-#00DD99 text-#00DD99 text-18px b-1px b-solid b-rd-6px">
+                <!-- 合作伙伴 身份 -->
+                {{ userInfo?.email || 'sales representative' }}
+            </div>
+        </div>
+        <div class="flex items-center ">
+            <div class="mr-20px text-18px text-#767676">Last data refresh time:{{ userInfo?.last_refresh_time ||
+                '2025-03-18 12:00:56' }}</div>
+            <a-button type="primary" class="w-122px !p-unset !h-40px !b-rd-300px !text-18px text-#fff !fw-700"
+                @click="refresh" size="large">Refresh</a-button>
+        </div>
+    </div>
+</template>
+
+<script setup lang="ts">
+import useUserStore from '@/store/modules/user'
+const userStore = useUserStore()
+
+const userInfo = computed(() => {
+    return userStore.userInfo || {}
+})
+const refresh = () => {
+    console.log('refresh')
+}
+
+
+</script>
+<style scoped lang="less"></style>

+ 147 - 0
src/views/dashboard/_components/resourceSwiper.vue

@@ -0,0 +1,147 @@
+<template>
+  <a-row :gutter="24">
+    <a-col class="gutter-row" :span="24">
+      <div class="bg-#fff b-rd-10px py-24px px-32px text-center">
+        <div class="flex justify-between mb-14px">
+          <div class="text-22px fw-500 text-#223354">Resource Recommendation</div>
+          <div class="text-14px text-#767676 flex items-center cursor-pointer">
+            Details
+            <i-custom-arrow_right class="w-16px h-16px" />
+          </div>
+        </div>
+        <a-divider class="!my-0" />
+        <div class="mt-50px">
+          <div class="w-full mx-auto pos-relative px-96px">
+            <div
+              class="pos-absolute cursor-pointer left-10px top-50% transform-translate-y--50% cursor-not-allowed !cursor-pointer flex justify-center items-center"
+              @click="onClickLeft()"
+            >
+              <img src="@/assets/images/arrow_left.png" alt="" class="w-44px h-44px" srcset="" />
+            </div>
+            <div
+              class="pos-absolute cursor-pointer right-10px top-50% transform-translate-y--50% cursor-not-allowed !cursor-pointer flex justify-center items-center"
+              @click="onClickRight()"
+            >
+              <img src="@/assets/images/arrow_right.png" alt="" class="w-44px h-44px" srcset="" />
+            </div>
+            <Swiper
+              v-if="list.length"
+              :slides-per-view="4"
+              :space-between="60"
+              :modules="modules"
+              :loop="true"
+              :navigation="false"
+              :pagination="true"
+              class="pos-relative !pb-20px"
+              @swiper="onVerticalSwiper"
+            >
+              <SwiperSlide v-for="(item, index) in list" :key="index" class="slider-content b-rd-6px overflow-hidden bg-#fff">
+                <img :src="item.img" alt="" class="w-100% object-scale-down" />
+                <div class="px-20px py-16px text-left">
+                  <div class="text-#1A1A1A text-16px mb-14px fw-500 line-clamp-1">{{ item.title }}</div>
+                  <div class="text-14px text-#3D3D3D mb-20px line-clamp-2">{{ item.desc }}</div>
+                  <div class="flex justify-end">
+                    <div>
+                      <a-button class="px-14px b-rd-16px text-primary b-primary">
+                        <template #icon>
+                          <DownloadOutlined />
+                        </template>
+                        Download
+                      </a-button>
+                      <a-button class="px-14px b-rd-16px ml-10px text-primary b-primary">
+                        <template #icon>
+                          <EyeOutlined />
+                        </template>
+                        View</a-button
+                      >
+                    </div>
+                  </div>
+                </div>
+              </SwiperSlide>
+            </Swiper>
+            <div v-else class="flex justify-center items-center h-100%">
+              <div class="text-center">
+                <img src="@/assets/images/swiper_no_data.png" alt="" class="w-280px h-280px" />
+                <div class="text-14px text-#767676">No data</div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </a-col>
+  </a-row>
+</template>
+  
+  <script setup lang="ts">
+import { Swiper, SwiperSlide } from 'swiper/vue'
+import { Navigation, Pagination } from 'swiper/modules'
+import 'swiper/css'
+import 'swiper/css/navigation'
+import { DownloadOutlined, EyeOutlined } from '@ant-design/icons-vue'
+import { columns, useGetData } from '../useGetData'
+
+const modules = [Navigation, Pagination]
+const list = ref<any>([
+  //   {
+  //     img: 'https://picsum.photos/360/200',
+  //     title: 'Resource 1',
+  //     desc: 'Resource 1 description',
+  //   },
+  //   {
+  //     img: 'https://picsum.photos/360/200',
+  //     title: 'Resource 2',
+  //     desc: 'Resource 2 description',
+  //   },
+  //   {
+  //     img: 'https://picsum.photos/360/200',
+  //     title: 'Resource 1',
+  //     desc: 'Resource 1 description',
+  //   },
+  //   {
+  //     img: 'https://picsum.photos/360/200',
+  //     title: 'Resource 2',
+  //     desc: 'Resource 2 description',
+  //   },
+  //   {
+  //     img: 'https://picsum.photos/360/200',
+  //     title: 'Resource 1',
+  //     desc: 'Resource 1 description',
+  //   },
+  //   {
+  //     img: 'https://picsum.photos/360/200',
+  //     title: 'Resource 2',
+  //     desc: 'Resource 2 description',
+  //   },
+])
+const swiperVertical = ref<any>(null)
+
+const { clientList, orderList, clientLoading, orderLoading } = useGetData()
+async function getResourceList() {
+  try {
+    const params = {
+      pageNo: 1,
+      pageSize: 8,
+    }
+    // const data: any = await getResourceListApi(params)
+    // list.value = data.records
+  } catch (error) {
+    console.log('error', error)
+  }
+}
+
+function onVerticalSwiper(swiper: any) {
+  swiperVertical.value = swiper
+}
+function onClickLeft() {
+  swiperVertical.value.slidePrev()
+}
+function onClickRight() {
+  swiperVertical.value.slideNext()
+}
+</script>
+  <style scoped lang="less">
+.slider-content {
+  box-shadow: 0px 3px 8px 0px rgba(0, 0, 0, 0.16);
+}
+</style>
+  

+ 85 - 0
src/views/dashboard/_components/staticData.vue

@@ -0,0 +1,85 @@
+<template>
+  <a-row :gutter="24">
+    <a-col class="gutter-row" :span="6">
+      <div class="bg-#fff b-rd-10px py-24px px-32px h-250px text-center">
+        <div class="flex justify-between mb-14px">
+          <div class="text-22px fw-500 text-#223354">Available Sensors</div>
+          <div class="text-14px text-#767676 flex items-center cursor-pointer">
+            Details
+            <i-custom-arrow_right class="w-16px h-16px" />
+          </div>
+        </div>
+        <a-divider class="!my-0" />
+        <img src="@/assets/images/data_img.png" class="w-30px h-30px mt-16px mb-14px" alt="" srcset="" />
+        <div class="text-36px mb-14px fw-500 text-#223354">$ 6913.62</div>
+        <div class="text-14px text-#3D3D3D">Commission earned from customers and orders</div>
+      </div>
+    </a-col>
+    <a-col class="gutter-row" :span="10">
+      <div class="b-rd-10px h-250px bg-#fff py-24px px-32px">
+        <div class="mb-16px h-24px lh-24px">
+          <a-date-picker v-model:value="condition.year" picker="year" class="w-120px h-24px b-rd-4px"/>
+        </div>
+        <a-row :gutter="20">
+          <a-col class="gutter-row" :span="8">
+            <div class="p-18px b-rd-10px bg-#00DD99/7">
+              <img src="@/assets/images/people_group.png" class="w-30px h-30px" alt="" srcset="" />
+              <div title="Number of Customers" class="text-18px text-#3D3D3D my-20px fw-500 line-clamp-1">Number of Customers</div>
+              <div class="flex text-#223354 items-end">
+                <span class="text-36px fw-500">100</span>
+                <span class="text-18px fw-500 pb-5px ml-4px">people</span>
+              </div>
+            </div>
+          </a-col>
+          <a-col class="gutter-row" :span="8">
+            <div class="p-18px b-rd-10px bg-#00DD99/7">
+              <img src="@/assets/images/people_single.png" class="w-30px h-30px" alt="" srcset="" />
+              <div title="Customer Commission" class="text-18px text-#3D3D3D my-20px fw-500 line-clamp-1">Customer Commission​</div>
+              <div class="flex text-#223354 items-end">
+                <span class="text-36px fw-500">$100</span>
+              </div>
+            </div>
+          </a-col>
+          <a-col class="gutter-row" :span="8">
+            <div class="p-18px b-rd-10px bg-#00DD99/7">
+              <img src="@/assets/images/bill.png" class="w-30px h-30px" alt="" srcset="" />
+              <div title="Order commission" class="text-18px text-#3D3D3D my-20px fw-500 line-clamp-1">Order commission</div>
+              <div class="flex text-#223354 items-end">
+                <span class="text-36px fw-500">$3692</span>
+              </div>
+            </div>
+          </a-col>
+        </a-row>
+      </div>
+    </a-col>
+    <a-col class="gutter-row" :span="4">
+      <div class="bg-#fff flex items-center justify-center h-250px b-rd-10px py-24px px-32px">
+        <div class="text-center pt-20px">
+          <img src="@/assets/images/icon_intro.png" class="w-44px h-44px" alt="" srcset="" />
+          <div class="text-22px text-#223354 mt-20px mb-30px fw-500">Customer Introduction</div>
+          <div class="w-100px py-10px b-rd-300px bg-#00DD99 mx-auto cursor-pointer">
+            <img src="@/assets/images/right_white_arrow.png" class="w-30px h-15px" alt="" srcset="" />
+          </div>
+        </div>
+      </div>
+    </a-col>
+    <a-col class="gutter-row" :span="4">
+      <div class="bg-#fff flex items-center justify-center h-250px b-rd-10px py-24px px-32px">
+        <div class="text-center pt-20px">
+          <img src="@/assets/images/icon_withdrawal.png" class="w-44px h-44px" alt="" srcset="" />
+          <div class="text-22px text-#223354 mt-20px mb-30px fw-500">Customer Introduction</div>
+          <div class="w-100px py-10px b-rd-300px bg-#00DD99 mx-auto cursor-pointer">
+            <img src="@/assets/images/right_white_arrow.png" class="w-30px h-15px" alt="" srcset="" />
+          </div>
+        </div>
+      </div>
+    </a-col>
+  </a-row>
+</template>
+
+<script setup lang="ts">
+import { useGetData } from '../useGetData'
+
+const { condition } = useGetData()
+</script>
+<style scoped lang="less"></style>

+ 80 - 0
src/views/dashboard/_components/tableData.vue

@@ -0,0 +1,80 @@
+<template>
+  <a-row :gutter="24">
+    <a-col class="gutter-row" :span="12">
+      <div class="bg-#fff b-rd-10px py-24px px-32px text-center">
+        <div class="flex justify-between mb-14px">
+          <div class="text-22px fw-500 text-#223354">My Client</div>
+          <div class="text-14px text-#767676 flex items-center cursor-pointer">
+            Details
+            <i-custom-arrow_right class="w-16px h-16px" />
+          </div>
+        </div>
+        <a-divider class="!my-0" />
+        <a-table
+          class="theme-table"
+          :columns="columns"
+          :row-key="(record) => record.id"
+          :data-source="clientList"
+          :pagination="false"
+          :loading="clientLoading"
+        >
+          <template #bodyCell="{ column, value, record }">
+            <template v-if="column.dataIndex === 'billNo'">
+              <router-link class="c-primary underline" :to="{ path: '/product/detail', query: { id: record.id } }">{{ value }}</router-link>
+            </template>
+          </template>
+          <template #emptyText>
+            <div class="text-center py-100px">
+              <img src="@/assets/images/table_empty.png" alt="" class="w-70px h-46px" />
+              <div class="text-14px text-#767676">No data</div>
+            </div>
+          </template>
+        </a-table>
+      </div>
+    </a-col>
+    <a-col class="gutter-row" :span="12">
+      <div class="bg-#fff b-rd-10px py-24px px-32px text-center">
+        <div class="flex justify-between mb-14px">
+          <div class="text-22px fw-500 text-#223354">Customer Order</div>
+          <div class="text-14px text-#767676 flex items-center cursor-pointer">
+            Details
+            <i-custom-arrow_right class="w-16px h-16px" />
+          </div>
+        </div>
+        <a-divider class="!my-0" />
+        <a-table
+          class="theme-table"
+          :columns="columns"
+          :row-key="(record) => record.id"
+          :data-source="orderList"
+          :pagination="false"
+          :loading="orderLoading"
+        >
+          <template #bodyCell="{ column, value, record }">
+            <template v-if="column.dataIndex === 'billNo'">
+              <router-link class="c-primary underline" :to="{ path: '/product/detail', query: { id: record.id } }">{{ value }}</router-link>
+            </template>
+          </template>
+          <template #emptyText>
+            <div class="text-center py-100px">
+              <img src="@/assets/images/table_empty.png" alt="" class="w-70px h-46px" />
+              <div class="text-14px text-#767676">No data</div>
+            </div>
+          </template>
+        </a-table>
+      </div>
+    </a-col>
+  </a-row>
+</template>
+
+<script setup lang="ts">
+import { columns, useGetData } from '../useGetData'
+const { clientList, orderList, clientLoading, orderLoading } = useGetData()
+</script>
+<style scoped lang="less">
+.theme-table {
+  :deep(.ant-table-cell) {
+    background-color: #fff;
+  }
+}
+</style>

+ 19 - 116
src/views/dashboard/index.vue

@@ -1,125 +1,28 @@
 <template>
   <div class="page">
-    <a-row :gutter="10">
-      <a-col :span="16">
-        <RowItem class="mb-10" title="我的品牌" :number="brandTotal" desc="已创建" link="/brand">
-          <div class="px-60 h-full grid grid-cols-5 gap-60">
-            <router-link to="/brand" class="w-87 img-item fs-16 c-333" v-for="item in brandList" :key="item.id">
-              <img class="w-87 h-87 shadow hover:shadow-gray transition-all object-cover" :src="item.brandLogo" />
-              <p class="truncate w-full mt-10 text-center">{{ item.brandName }}</p>
-            </router-link>
-          </div>
-        </RowItem>
-        <RowItem class="mb-10" title="我的商品" :number="productTotal" desc="已维护" link="/brand">
-          <div class="px-60 h-full grid grid-cols-5 gap-60">
-            <router-link to="/brand" class="w-87 img-item fs-16 c-333" v-for="item in productList" :key="item.id">
-              <img class="w-87 h-87 shadow hover:shadow-gray transition-all" :src="item.masterImage" />
-              <p class="truncate w-full mt-10 text-center">{{ item.merchandiseEnglishName }}</p>
-            </router-link>
-          </div>
-        </RowItem>
-        <RowItem class="mb-10" title="我的订单" :number="orderTotal" desc="已下单" link="/order" linkText="查看所有">
-          <div class="px-60 h-full flex justify-between items-center">
-            <div class="box w-full shadow">
-              <div
-                class="head h-48 lh-48 px-20 flex justify-between items-center border-b-1 border-b-solid border-gray-1"
-                v-for="item in orderList"
-                :key="item.id"
-              >
-                <div>
-                  <span class="order-id fs-16">{{ item.billNo }}</span>
-                  <span class="order-date ml-10 fs-14 c-999">{{ item.createTime }}</span>
-                </div>
-                <div class="c-DF2525 fw-700">{{ toUSD(item.price) }}</div>
-              </div>
-              <div class="body px-20 py-15">
-                <div class="list flex items-center justify-around">
-                  <img
-                    class="w-60 h-60 mr-30 shadow"
-                    :src="item.url"
-                    v-for="(item, i) in orderInnerList.filter((item, index) => index < 5)"
-                    :key="i"
-                  />
-                  <span class="c-999 fs-14 w-100 flex">等{{ orderInnerList.length }}件产品</span>
-                </div>
-              </div>
-            </div>
-          </div>
-        </RowItem>
-
-        <img class="bottom-img w-full h-182 object-cover" src="@/assets/images/ejet_group.png" alt="" />
-      </a-col>
-      <a-col :span="8">
-        <PanelItem class="mb-10" title="公告栏" noneText="您还没有新的公告~~"></PanelItem>
-        <PanelItem class="mb-10" title="最新留言" noneText="您还没有新的留言~~"></PanelItem>
-        <PanelConnect class="mb-10" title="客服"></PanelConnect>
-      </a-col>
-    </a-row>
+    <Header />
+    <!-- <div class="overflow-y-auto h-calc"> -->
+    <img src="@/assets/images/dashboard_head_bg.png" alt="" class="w-full object-scale-down my-24px" />
+    <StaticData class="mb-24px" />
+    <TableData class="mb-24px" />
+    <ResourceSwiper />
+    <!-- </div> -->
   </div>
 </template>
 
 <script setup lang="ts">
-import RowItem from './_components/RowItem.vue'
-import PanelItem from './_components/PanelItem.vue'
-import PanelConnect from './_components/PanelConnect.vue'
-import { downloadFile } from '@/utils'
-import { apiBrandList } from '@/api/brand'
-import { apiProductList } from '@/api/product'
-import { apiOrderList, apiRecentOrder } from '@/api/order'
-import { toUSD } from '@/utils'
-import type { UnwrapRef } from 'vue'
+import Header from './_components/header.vue'
+import StaticData from './_components/staticData.vue'
+import TableData from './_components/tableData.vue'
+import ResourceSwiper from './_components/resourceSwiper.vue'
 import useUserStore from '@/store/modules/user'
 const userStore = useUserStore()
-
-const mockImg = 'https://picsum.photos/100/100'
-
-// 品牌
-let brandList: UnwrapRef<BrandItem[]> = reactive([])
-let brandTotal = ref<number>(0)
-apiBrandList({
-  pageSize: 5,
-})
-  .then(({ data }) => {
-    brandList = Object.assign(brandList, data.result.records)
-    brandTotal.value = data.result.total
-  })
-  .catch(() => {})
-
-// 商品
-let productList: UnwrapRef<ProductItem[]> = reactive([])
-let productTotal = ref<number>(0)
-apiProductList({
-  pageSize: 5,
-})
-  .then(({ data }) => {
-    productList = Object.assign(productList, data.result.records)
-    productTotal.value = data.result.total
-  })
-  .catch(() => {})
-
-// 我的订单
-let orderList: UnwrapRef<OrderItem[]> = reactive([])
-let orderTotal = ref<number>(0)
-apiOrderList({
-  pageSize: 1,
-})
-  .then(({ data }) => {
-    orderList = Object.assign(orderList, data.result.records)
-    orderTotal.value = data.result.total
-  })
-  .catch(() => {})
-
-type OrderInner = {
-  url: string
-}
-let orderInnerList: UnwrapRef<Array<OrderInner>> = reactive([])
-apiRecentOrder({
-  supplierId: userStore.userInfo.id,
-})
-  .then(({ data }) => {
-    console.log(data)
-    orderInnerList = Object.assign(orderInnerList, data.result.lastMerchandise)
-  })
-  .catch(() => {})
 </script>
-<style scoped lang="less"></style>
+<style lang="less">
+// .main-view {
+//   overflow: unset !important;
+// }
+// .h-calc{
+//   height: calc(100vh - 200px);
+// }
+</style>

+ 38 - 0
src/views/dashboard/useGetData.ts

@@ -0,0 +1,38 @@
+export const columns = [
+  {
+    title: 'Customer Name',
+    dataIndex: 'merchandiseEnglishName',
+  },
+  {
+    title: 'Clue Encoding',
+    dataIndex: 'affiliationBrandName',
+  },
+  {
+    title: 'Clue Status',
+    dataIndex: 'sellPrice',
+  },
+  {
+    title: 'Commission(USD)',
+    dataIndex: 'sellPrice',
+  },
+  {
+    title: 'Commission Status',
+    dataIndex: 'state',
+  },
+]
+export const useGetData = () => {
+  const condition = ref({
+    year: '',
+  })
+  const clientList = ref([])
+  const clientLoading = ref(false)
+  const orderList = ref([])
+  const orderLoading = ref(false)
+  return {
+    condition,
+    clientList,
+    orderList,
+    clientLoading,
+    orderLoading,
+  }
+}

+ 89 - 133
src/views/login/index.vue

@@ -1,45 +1,33 @@
 <template>
   <LoginLayout>
-    <div class="outer-page 2xl:py-90px 2xl:px-130px lg:py-40px lg:px-60px pos-relative">
-      <div class="tabs fs-24 flex c-999 select-none mb">
-        <!-- <div :class="{ active: tabType === 'password' }" class="tab mr-60px cursor-pointer" @click="changeTab('password')">密码登录</div> -->
-        <div :class="{ active: tabType === 'captcha' }" class="tab cursor-pointer" @click="changeTab('captcha')">验证码登录</div>
-      </div>
-      <!-- 注册form -->
-      <a-form class="w-376px" ref="postFormRef" :model="postForm" :rules="postFormRules" :colon="false">
-        <a-form-item class="mb-20 custom-form-item" ref="account" label="手机号/邮箱" name="account" placehplder="手机号码">
-          <a-input v-model:value="postForm.account" placeholder="请输入注册手机号/邮箱" size="large" />
-        </a-form-item>
-        <a-form-item class="custom-form-item !mb-0" ref="password" label="密码" name="password" v-if="tabType === 'password'">
-          <a-input-password v-model:value="postForm.password" placeholder="请输入密码" size="large" />
+    <div class="pos-relative w-500px">
+      <div class="text-36px fw-bold lh-50px text-#223354 mb-24px">Sign in</div>
+      <div class="lh-18px text-#767676 mb-20px w-350px">We will check if you have an account, and if not, we will help
+        you create one.</div>
+      <a-form ref="postFormRef" :model="postForm" :rules="postFormRules" :colon="false">
+        <a-form-item class="mb-20 w-full custom-form-item" ref="account" label="Email" name="account">
+          <a-input v-model:value="postForm.account" placeholder="Please enter your email" size="large" />
         </a-form-item>
-
-        <a-form-item class="custom-form-item mb-0" label="验证码" name="captcha" v-if="tabType === 'captcha'">
+        <a-form-item class="custom-form-item2 mb-0" label="Captcha" name="captcha">
           <a-input-group class="w-full flex justify-between">
-            <a-input v-model:value="postForm.captcha" style="width: calc(100% - 134px)" placeholder="请输入验证码" :maxlength="6" size="large" />
-            <a-button
-              class="!w-120px !h-50px !text-14px !b-rd-6px !b-#8692a6"
-              :disabled="seconds !== 0 || !postForm.account.length"
-              @click="onSendCaptcha"
-              size="large"
-            >
-              <span v-if="seconds === 0" class="c-666">获取验证码</span>
-              <span v-else>{{ seconds }}s后重新发送</span>
+            <a-input v-model:value="postForm.captcha" placeholder="Please enter captcha" :maxlength="6" size="large" />
+            <a-button class="!h-68px !text-18px !b-transparent" :disabled="seconds !== 0 || !postForm.account.length"
+              @click="onSendCaptcha" size="large">
+              <span v-if="seconds === 0" class="c-666">Get code</span>
+              <span v-else>{{ seconds }}s later resend</span>
             </a-button>
           </a-input-group>
         </a-form-item>
-        <a-form-item class="custom-form-item error-form-item h-22px">
-          <div class="flex justify-between mt-12px" v-if="tabType === 'password'">
-            <!-- <a-checkbox class="custom-radio" v-model:checked="rememberPsw">记住密码</a-checkbox> -->
-            <div class="flex items-center"><ToggleCircle class="mr-10px" v-model="rememberPsw" />记住密码</div>
-            <router-link class="flex justify-end text-#0068FF underline" to="/forgetPwd">忘记密码?</router-link>
-          </div></a-form-item
-        >
-        <a-form-item label="" class="mt-32px mb-22px">
-          <a-button class="w-full !bg-#0068FF !b-rd-6px text-#fff !fw-700 !h-56px !hover:text-#fff" @click="onLogin" size="large">登录</a-button>
+        <a-form-item label="" class="my-20px">
+          <a-button type="primary" class="w-full !h-68px !b-rd-12px !text-20px text-#fff !fw-700 " @click="onLogin"
+            size="large">Sign in</a-button>
         </a-form-item>
-        <div class="text-center c-666">还没有账号, <router-link to="/register" class="text-#0068FF underline">前往注册</router-link></div>
       </a-form>
+      <div class="text-14px text-#767676 lh-24px">
+        By continuing, you agree to  EJET Partner shar 
+        <a href="/" class="text-#1A1A1A decoration-none">Terms of Use</a>
+        ⁠. Read our <a href="/" class="text-#1A1A1A decoration-none">Privacy Policy⁠.</a>
+      </div>
     </div>
   </LoginLayout>
 </template>
@@ -51,44 +39,25 @@ import type { UnwrapRef } from 'vue'
 import type { Rule } from 'ant-design-vue/es/form'
 import type { CaptchaData, LoginData } from 'Types/api'
 import { apiSendCaptcha } from 'API/user'
-import { validatePhoneOrEmail, validatePassword, genValidateTrue } from 'Utils/validate'
+import { validatePhoneOrEmail, validateEmail, validatePassword, genValidateTrue } from 'Utils/validate'
 
 const postFormRef = ref()
-const rememberPsw = ref(false)
-// 当前注册阶段
 let seconds = ref(0)
-let tabType = ref('captcha')
 
 interface IRegisterForm {
   account: string
-  password: string
   captcha: string
 }
 
 const postForm: UnwrapRef<IRegisterForm> = reactive({
   account: '',
-  password: '',
   captcha: '',
 })
 const postFormRules: Record<string, Rule[]> = {
-  account: [{ required: true, validator: validatePhoneOrEmail, trigger: 'change' }],
-  password: [{ required: true, validator: validatePassword, trigger: 'change' }],
-  captcha: [{ required: true, message: '验证码未输入', trigger: 'change' }],
+  account: [{ required: true, validator: validateEmail, trigger: 'change' }],
+  captcha: [{ required: true, message: 'captcha is required', trigger: 'change' }],
 }
 
-const changeTab = (type: string) => {
-  tabType.value = type
-  if (type === 'password') {
-    const savedUser = localStorage.getItem('rememberedUser')
-    let psw = ''
-    if (savedUser) {
-      const { password: savedPassword } = JSON.parse(savedUser)
-      psw = savedPassword
-    }
-    postForm.password = psw || ''
-  }
-  postForm.captcha = ''
-}
 const onSendCaptcha = () => {
   postFormRef.value
     .validateFields(['account'])
@@ -119,124 +88,111 @@ const countSeconds = () => {
 const onLogin = async () => {
   postFormRef.value.validate().then(async () => {
     const params: Partial<typeof postForm> = toRaw(postForm)
-    if (tabType.value === 'password') {
-      delete params.captcha
-    } else {
-      delete params.password
-    }
     const userStore = useUserStore()
     await userStore.login(params as LoginData)
-    if (rememberPsw.value) {
-      localStorage.setItem(
-        'rememberedUser',
-        JSON.stringify({
-          account: postForm.account,
-          password: postForm.password,
-          rememberPsw: true,
-        })
-      )
-    } else {
-      localStorage.removeItem('rememberedUser')
-    }
   })
 }
-
-// 检查是否有保存的用户信息
-const checkSavedUser = () => {
-  const savedUser = localStorage.getItem('rememberedUser')
-  if (savedUser) {
-    const { account: savedUsername, password: savedPassword, rememberPsw: savedRememberMe } = JSON.parse(savedUser)
-    postForm.account = savedUsername
-    postForm.password = savedPassword
-    rememberPsw.value = savedRememberMe
-  }
-}
-
-// 在组件挂载时检查保存的用户信息
-checkSavedUser()
 </script>
 
 <style scoped lang="less">
 @import '@/assets/styles/variables.less';
-.page {
-  .tabs {
-    display: flex;
-    justify-content: center;
-    margin-bottom: 70px;
-    .tab {
-      padding-bottom: 10px;
-      border-bottom: 4px solid transparent;
-      transition: 0.3s;
-      &.active {
-        border-bottom: 4px solid @color-new-primary;
-        color: @color-new-primary;
-        font-weight: 700;
-      }
-    }
-  }
 
-  .outer-page {
-    background-color: #fff;
-    border-radius: 18px;
-  }
+.page {
   ::v-deep(.ant-form) {
     .ant-form-item {
       &.custom-form-item {
         .ant-form-item-row {
           display: block !important;
-          width: 376px !important;
+          width: 500px !important;
 
           .ant-form-item-label {
             color: #000000;
-            margin-bottom: 10px;
+            margin-bottom: 10px !important;
+            font-weight: normal;
+
+            label {
+              font-size: 24px;
+            }
           }
 
           .ant-form-item-control {
             .ant-input-affix-wrapper {
               border-radius: 6px;
-              width: 376px;
-              border-color: #8692a6;
+              width: 500px;
+              border-color: none !important;
+
               input {
                 height: 34px;
               }
             }
+
             input {
-              width: 376px;
-              border-radius: 6px;
-              border-color: #8692a6;
-              height: 50px;
-              line-height: 50px;
-              font-size: 16px !important;
+              width: 500px;
+              border-radius: 12px;
+              border-color: transparent !important;
+              background-color: #F1F1F1;
+              height: 68px;
+              line-height: 68px;
+              font-size: 18px !important;
+
               &.ant-input-status-error {
                 border-color: #ff4d4f;
               }
             }
+
             .ant-form-item-explain-error {
               text-align: right;
             }
           }
         }
-        &.error-form-item {
-          .ant-form-item-explain-error {
-            padding-right: 140px;
-          }
+
+        .ant-form-item-explain-error {
+          text-align: right;
+          margin-top: 10px;
         }
+
       }
-      .custom-radio {
-        .ant-checkbox {
-          margin-right: 10px;
-          &.ant-checkbox-checked {
-            .ant-checkbox-inner {
-              background-color: #0068ff;
-              border-color: #0068ff;
-            }
-            &:after {
-              border: 1px solid #0068ff;
+
+      &.custom-form-item2 {
+        .ant-form-item-row {
+          display: block !important;
+          width: 500px !important;
+
+          .ant-form-item-label {
+            color: #000000;
+            margin-bottom: 10px !important;
+            font-weight: normal;
+
+            label {
+              font-size: 24px;
             }
           }
-          .ant-checkbox-inner {
-            &:hover {
-              border-color: #0068ff !important;
+
+          .ant-form-item-control {
+            input {
+              border-radius: 12px;
+              border-color: transparent !important;
+              background-color: #F1F1F1;
+              height: 68px;
+              line-height: 68px;
+              font-size: 18px !important;
+
+              &.ant-input-status-error {
+                border-color: #ff4d4f;
+              }
+            }
+
+            .ant-btn {
+              background-color: #F1F1F1;
+              border-radius: 12px;
+              width: 200px;
+              margin-left: 20px;
+            }
+
+            .ant-form-item-explain-error {
+              margin-top: 10px;
+              text-align: right;
+              padding-right: 170px;
             }
           }
         }

+ 1 - 1
uno.config.ts

@@ -7,7 +7,7 @@ import presetRemToPx from '@unocss/preset-rem-to-px'
 export default defineConfig({
   theme: {
     colors: {
-      primary: '#2AC2B5',
+      primary: '#00DD99',
       success: '#2AC2B5',
       warning: '#F59211',
       danger: '#DF2525',