Browse Source

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

lifanagju_citu 1 year ago
parent
commit
760cd0af9c
45 changed files with 820 additions and 456 deletions
  1. 4 1
      .env.development
  2. 22 0
      package-lock.json
  3. 1 0
      package.json
  4. 8 0
      pnpm-lock.yaml
  5. 243 0
      src/components/Enterprise/hotPromoted.vue
  6. 7 0
      src/components/Position/item.vue
  7. 11 18
      src/components/VerificationCode/index.vue
  8. 1 1
      src/config/axios/config.js
  9. 13 12
      src/config/axios/service.js
  10. 33 0
      src/hooks/web/useNProgress.js
  11. 25 0
      src/hooks/web/useTitle.js
  12. 11 8
      src/layout/index.vue
  13. 1 1
      src/layout/personal/footer.vue
  14. 6 6
      src/layout/personal/navBar.vue
  15. 54 0
      src/layout/personal/slider.vue
  16. 26 0
      src/locales/en.js
  17. 27 1
      src/locales/zh-CN.js
  18. 2 1
      src/main.js
  19. 42 0
      src/permission.js
  20. 11 11
      src/plugins/snackbar/index.js
  21. 10 0
      src/router/index.js
  22. 0 13
      src/router/modules/remaining.js
  23. 20 0
      src/store/app.js
  24. 43 0
      src/store/dictionaries.js
  25. 15 17
      src/store/user.js
  26. 1 0
      src/styles/index.css
  27. 1 1
      src/styles/index.min.css
  28. 1 0
      src/styles/index.scss
  29. 1 24
      src/styles/personal/navBar.css
  30. 1 1
      src/styles/personal/navBar.min.css
  31. 1 20
      src/styles/personal/navBar.scss
  32. 13 6
      src/utils/auth.js
  33. 117 0
      src/utils/is.js
  34. 0 130
      src/utils/request.js
  35. 4 6
      src/views/Home/personal/account/dynamic/editPassword.vue
  36. 4 1
      src/views/Home/personal/components/hotPromotedPositions.vue
  37. 12 0
      src/views/Home/personal/components/popularEnterprises.vue
  38. 1 1
      src/views/Home/personal/components/search.vue
  39. 2 0
      src/views/Home/personal/index.vue
  40. 4 4
      src/views/enterprise/components/register.vue
  41. 3 3
      src/views/login/components/passwordPage.vue
  42. 0 140
      src/views/login/components/phonePage.vue
  43. 11 11
      src/views/login/components/privacyPolicy.vue
  44. 1 1
      src/views/login/components/userAgreement.vue
  45. 6 17
      src/views/login/index.vue

+ 4 - 1
.env.development

@@ -29,4 +29,7 @@ VITE_OUT_DIR = 'dist'
 VITE_MALL_H5_DOMAIN = 'http://mall.yudao.iocoder.cn'
 VITE_MALL_H5_DOMAIN = 'http://mall.yudao.iocoder.cn'
 
 
 # 验证码的开关
 # 验证码的开关
-VITE_APP_CAPTCHA_ENABLE = false
+VITE_APP_CAPTCHA_ENABLE = false
+
+#租户开关
+VITE_APP_TENANT_ENABLE = true

+ 22 - 0
package-lock.json

@@ -11,8 +11,10 @@
         "@mdi/font": "7.0.96",
         "@mdi/font": "7.0.96",
         "axios": "^1.6.8",
         "axios": "^1.6.8",
         "js-cookie": "^3.0.5",
         "js-cookie": "^3.0.5",
+        "nprogress": "^0.2.0",
         "pinia": "^2.1.7",
         "pinia": "^2.1.7",
         "pinia-plugin-persistedstate": "^3.2.1",
         "pinia-plugin-persistedstate": "^3.2.1",
+        "pnpm": "^9.1.0",
         "qs": "^6.12.1",
         "qs": "^6.12.1",
         "roboto-fontface": "*",
         "roboto-fontface": "*",
         "vue": "^3.4.0",
         "vue": "^3.4.0",
@@ -2308,6 +2310,11 @@
         "node": ">=0.10.0"
         "node": ">=0.10.0"
       }
       }
     },
     },
+    "node_modules/nprogress": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz",
+      "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="
+    },
     "node_modules/nth-check": {
     "node_modules/nth-check": {
       "version": "2.1.1",
       "version": "2.1.1",
       "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
       "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
@@ -2479,6 +2486,21 @@
         "pinia": "^2.0.0"
         "pinia": "^2.0.0"
       }
       }
     },
     },
+    "node_modules/pnpm": {
+      "version": "9.1.1",
+      "resolved": "https://registry.npmmirror.com/pnpm/-/pnpm-9.1.1.tgz",
+      "integrity": "sha512-FOkVdZwR936sB/q6TQGcGT7IY3Ip5i7Jnu+3zzw7dcZER4grfEhRQkUe46a0CAWc37e3+gNBuXXxLQ92KccRlQ==",
+      "bin": {
+        "pnpm": "bin/pnpm.cjs",
+        "pnpx": "bin/pnpx.cjs"
+      },
+      "engines": {
+        "node": ">=18.12"
+      },
+      "funding": {
+        "url": "https://opencollective.com/pnpm"
+      }
+    },
     "node_modules/postcss": {
     "node_modules/postcss": {
       "version": "8.4.38",
       "version": "8.4.38",
       "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz",
       "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz",

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "@mdi/font": "7.0.96",
     "@mdi/font": "7.0.96",
     "axios": "^1.6.8",
     "axios": "^1.6.8",
     "js-cookie": "^3.0.5",
     "js-cookie": "^3.0.5",
+    "nprogress": "^0.2.0",
     "pinia": "^2.1.7",
     "pinia": "^2.1.7",
     "pinia-plugin-persistedstate": "^3.2.1",
     "pinia-plugin-persistedstate": "^3.2.1",
     "pnpm": "^9.1.0",
     "pnpm": "^9.1.0",

+ 8 - 0
pnpm-lock.yaml

@@ -17,6 +17,9 @@ importers:
       js-cookie:
       js-cookie:
         specifier: ^3.0.5
         specifier: ^3.0.5
         version: 3.0.5
         version: 3.0.5
+      nprogress:
+        specifier: ^0.2.0
+        version: 0.2.0
       pinia:
       pinia:
         specifier: ^2.1.7
         specifier: ^2.1.7
         version: 2.1.7(vue@3.4.25)
         version: 2.1.7(vue@3.4.25)
@@ -936,6 +939,9 @@ packages:
     resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
     resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
     engines: {node: '>=0.10.0'}
     engines: {node: '>=0.10.0'}
 
 
+  nprogress@0.2.0:
+    resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
+
   nth-check@2.1.1:
   nth-check@2.1.1:
     resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
     resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
 
 
@@ -2206,6 +2212,8 @@ snapshots:
 
 
   normalize-path@3.0.0: {}
   normalize-path@3.0.0: {}
 
 
+  nprogress@0.2.0: {}
+
   nth-check@2.1.1:
   nth-check@2.1.1:
     dependencies:
     dependencies:
       boolbase: 1.0.0
       boolbase: 1.0.0

+ 243 - 0
src/components/Enterprise/hotPromoted.vue

@@ -0,0 +1,243 @@
+<template>
+  <div class="hot-box">
+    <div class="sub-li" v-for="(item, index) in list" :key="index">
+      <!-- 公司信息 -->
+      <div class="company-info-top">
+        <div class="float-left">
+          <v-img :src="item.enterpriseImg" :alt="item.enterpriseName" :width="40" style="height: 40px;border-radius: 4px;"/>
+        </div>
+        <div class="company-info">
+          <h3>{{ item.enterpriseName }}</h3>
+          <p>{{ item.enterpriseNature }}<span class="vline"></span>{{ item.scale }}<span class="vline"></span>{{ item.industry }}</p>
+        </div>
+      </div>
+      <!-- 职位列表 -->
+      <ul class="company-job-list">
+        <li class="company-job-item" v-for="(k, i) in item.positions" :key="i">
+          <div class="job-info">
+            <div class="mb-2 d-flex">
+              <p class="name">{{ k.recruitName }}</p>
+              <v-icon size="20" class="message">mdi-message-processing</v-icon>
+              <span class="salary">{{ k.salary }}</span>
+            </div>
+            <v-chip size="x-small" label class="mr-1" color="#666" v-for="j in k.tags" :key="j">{{ j }}</v-chip>
+          </div>
+        </li>
+      </ul>
+      <div class="text-center">
+        <v-btn class="buttons" color="primary" variant="outlined">查看更多职位</v-btn>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup name="hotPromoted">
+const list = [
+  {
+    enterpriseImg: 'https://img.bosszhipin.com/beijin/upload/com/workfeel/20231212/7bf6f160950405e90671d208831c3be24a7230ff916830a84f48ca3d2af0604795d2582973667498.png',
+    enterpriseName: '博彦科技',
+    enterpriseNature: '私营企业',
+    scale: '100000人以上',
+    industry: '计算机软件',
+    positions: [
+      {
+        recruitName: '跨境电商运营跨境电商运营跨境电商运营跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      },
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      },
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      }
+    ]
+  },
+  {
+    enterpriseImg: 'https://img.bosszhipin.com/beijin/upload/com/workfeel/20231212/7bf6f160950405e90671d208831c3be24a7230ff916830a84f48ca3d2af0604795d2582973667498.png',
+    enterpriseName: '博彦科技',
+    enterpriseNature: '私营企业',
+    scale: '100000人以上',
+    industry: '计算机软件',
+    positions: [
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      },
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      },
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      }
+    ]
+  },
+  {
+    enterpriseImg: 'https://img.bosszhipin.com/beijin/upload/com/workfeel/20231212/7bf6f160950405e90671d208831c3be24a7230ff916830a84f48ca3d2af0604795d2582973667498.png',
+    enterpriseName: '博彦科技',
+    enterpriseNature: '私营企业',
+    scale: '100000人以上',
+    industry: '计算机软件',
+    positions: [
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      },
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      },
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      }
+    ]
+  },
+  {
+    enterpriseImg: 'https://img.bosszhipin.com/beijin/upload/com/workfeel/20231212/7bf6f160950405e90671d208831c3be24a7230ff916830a84f48ca3d2af0604795d2582973667498.png',
+    enterpriseName: '博彦科技',
+    enterpriseNature: '私营企业',
+    scale: '100000人以上',
+    industry: '计算机软件',
+    positions: [
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      },
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      },
+      {
+        recruitName: '跨境电商运营',
+        salary: '7-9K',
+        tags: ['广州番禺南浦', '本科', '1-3年']
+      }
+    ]
+  }
+]
+</script>
+
+<style lang="scss" scoped>
+.hot-box {
+  display: flex;
+  flex-wrap: wrap;
+}
+.sub-li {
+  width: calc((100% - 24px) / 3);
+  min-width: calc((100% - 24px) / 3);
+  max-width: calc((100% - 24px) / 3);
+  margin: 0 12px 12px 0;
+  height: 388px;
+  border-radius: 12px;
+  padding: 0;
+  overflow: hidden;
+  transition: all .2s linear;
+  background-color: #fff;
+  cursor: pointer;
+  &:nth-child(3n) {
+    margin-right: 0;
+  }
+}
+.company-info {
+  float: left;
+  margin-left: 16px;
+  width: 282px;
+}
+.company-info-top {
+  display: flex;
+  height: 76px;
+  padding: 16px 20px;
+  overflow: hidden;
+  background: linear-gradient(90deg, #f2fafa 0, #fcfbfa 100%);
+}
+.company-info h3 {
+  height: 22px;
+  font-size: 16px;
+  font-weight: 400;
+  color: #222;
+  line-height: 22px;
+  margin: 0 0 4px 0;
+  padding: 0;
+  max-width: 100%;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+.company-info p {
+  height: 18px;
+  font-size: 13px;
+  font-weight: 400;
+  color: #999;
+  line-height: 18px;
+}
+.vline {
+  display: inline-block;
+  width: 1px;
+  height: 10px;
+  vertical-align: middle;
+  background-color: #e0e0e0;
+  margin: 0 10px;
+}
+.company-job-list {
+  display: block;
+  padding: 4px 20px 12px;
+  margin-right: -16px;
+  overflow: hidden;
+}
+ul li {
+  list-style: none
+}
+.company-job-item {
+  display: block;
+  height: auto;
+  width: 344px;
+  padding: 12px 0;
+  margin: 0;
+}
+.salary {
+  font-size: 16px;
+  float: right;
+  font-weight: 700;
+  color: #fe574a;
+  line-height: 22px;
+  max-width: none;
+  text-align: right;
+  flex: 1;
+}
+.name {
+  position: relative;
+  max-width: 200px;
+  line-height: 22px;
+  font-weight: 500;
+  color: #222;
+  margin-right: 8px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  transition: all linear .2s;
+  &:hover {
+    color: var(--v-primary-base);
+  }
+}
+.message {
+  color: #bfc1c9;
+  &:hover {
+    color: var(--v-primary-base);
+  }
+}
+</style>

+ 7 - 0
src/components/Position/item.vue

@@ -93,11 +93,15 @@ const list = [
   border-radius: 12px;
   border-radius: 12px;
   padding: 0;
   padding: 0;
   overflow: hidden;
   overflow: hidden;
+  cursor: pointer;
   transition: all .2s linear;
   transition: all .2s linear;
   background-color: #fff;
   background-color: #fff;
   &:nth-child(3n) {
   &:nth-child(3n) {
     margin-right: 0;
     margin-right: 0;
   }
   }
+  &:hover {
+    box-shadow: 0 16px 40px 0 rgba(153, 153, 153, .3);
+  }
 }
 }
 .job-info {
 .job-info {
   padding: 16px 20px;
   padding: 16px 20px;
@@ -140,6 +144,9 @@ const list = [
   text-overflow: ellipsis;
   text-overflow: ellipsis;
   white-space: nowrap;
   white-space: nowrap;
   font-weight: 600;
   font-weight: 600;
+  &:hover {
+    color: var(--v-primary-base);
+  }
 }
 }
 .sub-li-bottom {
 .sub-li-bottom {
   background: linear-gradient(90deg, #f5fcfc 0, #fcfbfa 100%);
   background: linear-gradient(90deg, #f5fcfc 0, #fcfbfa 100%);

+ 11 - 18
src/components/VerificationCode/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
   <div>
   <div>
     <v-form @submit.prevent ref="phoneForm">
     <v-form @submit.prevent ref="phoneForm">
-      <v-text-field v-model="loginData.phone" placeholder="请输入手机号" color="#00897B" variant="outlined" density="compact" :rules="phoneRules" validate-on="input">
+      <v-text-field v-model="loginData.phone" placeholder="请输入手机号" color="primary" variant="outlined" density="compact" :rules="phoneRules" validate-on="input">
         <template v-slot:prepend-inner>
         <template v-slot:prepend-inner>
           <span class="d-flex">
           <span class="d-flex">
             <v-icon icon="mdi-cellphone" size="20"></v-icon>
             <v-icon icon="mdi-cellphone" size="20"></v-icon>
@@ -19,14 +19,13 @@
           </span>
           </span>
         </template>
         </template>
       </v-text-field>
       </v-text-field>
-      <v-text-field v-model="loginData.code" placeholder="请输入验证码" color="#00897B" variant="outlined" density="compact" prepend-inner-icon="mdi-security" :rules="[v=> !!v || '请填写验证码']">
+      <v-text-field v-model="loginData.code" placeholder="请输入验证码" color="primary" variant="outlined" density="compact" prepend-inner-icon="mdi-security" :rules="[v=> !!v || '请填写验证码']">
         <template #append-inner>
         <template #append-inner>
           <span v-if="showCode" class="login-code" @click="handleCode">获取验证码</span>
           <span v-if="showCode" class="login-code" @click="handleCode">获取验证码</span>
           <span v-else class="disable">重新获取{{ count }}s</span>
           <span v-else class="disable">重新获取{{ count }}s</span>
         </template>
         </template>
       </v-text-field>
       </v-text-field>
     </v-form>
     </v-form>
-    <v-snackbar v-model="tips.show" :color="tips.color" :timeout="1500" location="top">{{ tips.text }}</v-snackbar>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -35,6 +34,7 @@ defineOptions({ name: 'verification-code' })
 import { ref, reactive, defineExpose } from 'vue'
 import { ref, reactive, defineExpose } from 'vue'
 import { setCodeTime } from '@/utils/code'
 import { setCodeTime } from '@/utils/code'
 import { sendSmsCode } from '@/api/common/index'
 import { sendSmsCode } from '@/api/common/index'
+import Snackbar from '@/plugins/snackbar'
 
 
 const phoneRules = ref([
 const phoneRules = ref([
   value => {
   value => {
@@ -47,12 +47,6 @@ const phoneRules = ref([
   }
   }
 ])
 ])
 
 
-const tips = reactive({
-  show: false,
-  color: '',
-  text: ''
-})
-
 // 手机号区域
 // 手机号区域
 const currentArea = ref('0086')
 const currentArea = ref('0086')
 const items = [
 const items = [
@@ -68,9 +62,7 @@ const count = ref(0)
 const timer = ref(null)
 const timer = ref(null)
 const handleCode = () => {
 const handleCode = () => {
   if (!loginData.phone) {
   if (!loginData.phone) {
-    tips.text = '请输入手机号码'
-    tips.color = 'warning'
-    tips.show = true
+    Snackbar.warning('请输入手机号码')
     return
     return
   }
   }
   count.value = 60
   count.value = 60
@@ -82,11 +74,12 @@ const getSmsCode = async () => {
     phone: loginData.phone,
     phone: loginData.phone,
     scene: 30
     scene: 30
   }
   }
-  try {
-    await sendSmsCode(query)
-  } catch (error) {
-    console.log(error, 'error')
-  }
+  // try {
+  await sendSmsCode(query)
+  Snackbar.success('发送成功,请注意查收')
+  // } catch (error) {
+  //   Snackbar.error(error.msg)
+  // }
 }
 }
 const setTime = () => {
 const setTime = () => {
   showCode.value = false
   showCode.value = false
@@ -111,7 +104,7 @@ autoTimer()
 
 
 const loginData = reactive({
 const loginData = reactive({
   phone: '13229740091',
   phone: '13229740091',
-  code: ''
+  code: '123456'
 })
 })
 
 
 const phoneForm = ref()
 const phoneForm = ref()

+ 1 - 1
src/config/axios/config.js

@@ -2,7 +2,7 @@ const config = {
   /**
   /**
    * api请求基础路径
    * api请求基础路径
    */
    */
-  base_url: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL,
+  base_url: import.meta.env.VITE_BASE_URL,
   /**
   /**
    * 接口成功返回状态码
    * 接口成功返回状态码
    */
    */

+ 13 - 12
src/config/axios/service.js

@@ -3,7 +3,7 @@ import Snackbar from '@/plugins/snackbar'
 import Confirm from '@/plugins/confirm'
 import Confirm from '@/plugins/confirm'
 import qs from 'qs'
 import qs from 'qs'
 import { config } from '@/config/axios/config'
 import { config } from '@/config/axios/config'
-import { getToken, getRefreshToken, getTenantId, setToken } from '@/utils/auth'
+import { getToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
 // import { getToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
 // import { getToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
 import errorCode from './errorCode'
 import errorCode from './errorCode'
 
 
@@ -23,7 +23,7 @@ const ignoreMsgs = [
   '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
   '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
 ]
 ]
 // 是否显示重新登录
 // 是否显示重新登录
-export const isRelogin = { show: false }
+export const isReLogin = { show: false }
 // Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
 // Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
 // 请求队列
 // 请求队列
 let requestList = []
 let requestList = []
@@ -55,7 +55,7 @@ service.interceptors.request.use(
     }
     }
     // 设置租户
     // 设置租户
     if (tenantEnable && tenantEnable === 'true') {
     if (tenantEnable && tenantEnable === 'true') {
-      const tenantId = getTenantId()
+      const tenantId = import.meta.env.VITE_TENANTCODE
       if (tenantId) (config).headers['tenant-id'] = tenantId
       if (tenantId) (config).headers['tenant-id'] = tenantId
     }
     }
     const params = config.params || {}
     const params = config.params || {}
@@ -127,7 +127,7 @@ service.interceptors.response.use(
         try {
         try {
           const refreshTokenRes = await refreshToken()
           const refreshTokenRes = await refreshToken()
           // 2.1 刷新成功,则回放队列的请求 + 当前请求
           // 2.1 刷新成功,则回放队列的请求 + 当前请求
-          setToken((await refreshTokenRes).data.data)
+          setToken(refreshTokenRes.data.data)
           config.headers.Authorization = 'Bearer ' + getToken()
           config.headers.Authorization = 'Bearer ' + getToken()
           requestList.forEach((cb) => {
           requestList.forEach((cb) => {
             cb()
             cb()
@@ -165,10 +165,11 @@ service.interceptors.response.use(
       if (msg === '无效的刷新令牌') {
       if (msg === '无效的刷新令牌') {
         // hard coding:忽略这个提示,直接登出
         // hard coding:忽略这个提示,直接登出
         console.log(msg)
         console.log(msg)
-      } else {
-        Snackbar.error({ title: msg })
       }
       }
-      return Promise.reject('error')
+       else {
+        Snackbar.error(msg)
+      }
+      return Promise.reject(msg)
     } else {
     } else {
       return data
       return data
     }
     }
@@ -193,17 +194,17 @@ const refreshToken = async () => {
   return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
   return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
 }
 }
 const handleAuthorized = () => {
 const handleAuthorized = () => {
-  if (!isRelogin.show) {
+  if (!isReLogin.show) {
     // 如果已经到重新登录页面则不进行弹窗提示
     // 如果已经到重新登录页面则不进行弹窗提示
     if (window.location.href.includes('login?redirect=')) {
     if (window.location.href.includes('login?redirect=')) {
       return
       return
     }
     }
-    isRelogin.show = true
-    Confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle')).then(() => {
+    isReLogin.show = true
+    Confirm(t('common.confirmTitle'), t('sys.api.timeoutMessage')).then(() => {
       // resetRouter() // 重置静态路由表
       // resetRouter() // 重置静态路由表
       // deleteUserCache() // 删除用户缓存
       // deleteUserCache() // 删除用户缓存
-      // removeToken()
-      isRelogin.show = false
+      removeToken()
+      isReLogin.show = false
       // 干掉token后再走一次路由让它过router.beforeEach的校验
       // 干掉token后再走一次路由让它过router.beforeEach的校验
       location.reload()
       location.reload()
     })
     })

+ 33 - 0
src/hooks/web/useNProgress.js

@@ -0,0 +1,33 @@
+import { nextTick } from 'vue'
+// import { useCssVar } from '@vueuse/core'
+import NProgress from 'nprogress'
+import 'nprogress/nprogress.css'
+
+// const primaryColor = useCssVar('--el-color-primary', document.documentElement)
+
+export const useNProgress = () => {
+  NProgress.configure({ showSpinner: false })
+
+  const initColor = async () => {
+    await nextTick()
+    const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0]
+    if (bar) {
+      bar.style.background = 'primary'
+    }
+  }
+
+  initColor()
+
+  const start = () => {
+    NProgress.start()
+  }
+
+  const done = () => {
+    NProgress.done()
+  }
+
+  return {
+    start,
+    done
+  }
+}

+ 25 - 0
src/hooks/web/useTitle.js

@@ -0,0 +1,25 @@
+import { watch, ref } from 'vue'
+import { isString } from '@/utils/is'
+import { useAppStore } from '@/store/app'
+import { useI18n } from './useI18n'
+
+
+export const useTitle = (newTitle) => {
+  const appStore = useAppStore()
+  const { t } = useI18n()
+  const title = ref(
+    newTitle ? `${appStore.title} - ${t(newTitle)}` : appStore.title
+  )
+
+  watch(
+    title,
+    (n, o) => {
+      if (isString(n) && n !== o && document) {
+        document.title = n
+      }
+    },
+    { immediate: true }
+  )
+
+  return title
+}

+ 11 - 8
src/layout/index.vue

@@ -4,25 +4,28 @@
     <div>
     <div>
       <router-view></router-view>
       <router-view></router-view>
     </div>
     </div>
-    <!-- <Footers class="footer"></Footers> -->
+    <Footers class="mt-10"></Footers>
+    <Slider class="slider"></Slider>
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
 import Headers from './personal/navBar.vue'
 import Headers from './personal/navBar.vue'
-// import Footers from './personal/footer.vue'
+import Footers from './personal/footer.vue'
+import Slider from './personal/slider.vue'
 defineOptions({ name: 'layout-index' })
 defineOptions({ name: 'layout-index' })
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
 .parent {
 .parent {
-  background-color: #eaf4fe;
-  height: 100vh;
+  background-color: var(--default-bgc);
   position: relative;
   position: relative;
 }
 }
-.footer {
-  position: absolute;
-  bottom: 0;
-  left: 0;
+.slider {
+  position: fixed;
+  bottom: 50%;
+  right: 24px;
+  translate: 0 50%;
+  z-index: 999;
 }
 }
 </style>
 </style>

+ 1 - 1
src/layout/personal/footer.vue

@@ -19,7 +19,7 @@
       </div>
       </div>
       <div class="right size d-flex">
       <div class="right size d-flex">
         <div>
         <div>
-          <v-img :width="100" cover aspect-ratio="16/9" src="https://minio.citupro.com/dev/static/qrcode.png" style="height: 100px;"></v-img>
+          <v-img :width="100" cover aspect-ratio="16/9" src="https://minio.citupro.com/dev/static/mendunerCode.jpg" style="height: 100px;"></v-img>
           <div class="mt-2" style="text-align: center;">微信公众号</div>
           <div class="mt-2" style="text-align: center;">微信公众号</div>
         </div>
         </div>
         <div class="ml-5">
         <div class="ml-5">

+ 6 - 6
src/layout/personal/navBar.vue

@@ -3,7 +3,7 @@
     <div
     <div
       class="banner"
       class="banner"
       density="compact"
       density="compact"
-      style="padding-left: 0px;background-color: #fff;height: 50px;font-size: 14px;"
+      style="padding-left: 0px;height: 50px;font-size: 14px;"
     >
     >
       <div class="innerBox">
       <div class="innerBox">
         <div class="nav-logo">
         <div class="nav-logo">
@@ -11,7 +11,7 @@
         </div>
         </div>
         <div class="nav-city">
         <div class="nav-city">
           <p class="nav-city-box">
           <p class="nav-city-box">
-            <v-icon color="#00897B">mdi-map-marker</v-icon>
+            <v-icon color="primary">mdi-map-marker</v-icon>
             <span class="nav-city-selected">广州</span>
             <span class="nav-city-selected">广州</span>
             <span class="switchover-city nav-city-selected">[切换城市]</span>
             <span class="switchover-city nav-city-selected">[切换城市]</span>
           </p>
           </p>
@@ -94,12 +94,12 @@ const changeRole = () => {
 // 退出登录
 // 退出登录
 const userStore = userLocaleStore()
 const userStore = userLocaleStore()
 const handleLogout = async () => {
 const handleLogout = async () => {
-  try {
+  // try {
     await userStore.userLogout()
     await userStore.userLogout()
     router.push({ path: '/login' })
     router.push({ path: '/login' })
-  } catch (error) {
-    console.log(error, 'error')
-  }
+  // } catch (error) {
+  //   console.log(error, 'error')
+  // }
 }
 }
 
 
 const items = ref([
 const items = ref([

+ 54 - 0
src/layout/personal/slider.vue

@@ -0,0 +1,54 @@
+<template>
+  <div class="slider-box">
+    <div v-for="item in list" :key="item.mdi" class="slider-box-item">
+      <v-tooltip :text="item.tips" location="start">
+        <template v-slot:activator="{ props }">
+          <v-icon size="30" class="icons" v-bind="props">{{ item.mdi }}</v-icon>
+        </template>
+        <template #default>
+          <div v-if="item.showImg" class="ma-3" style="text-align: center">
+            <v-img cover aspect-ratio="1/1" src="https://minio.citupro.com/dev/static/mendunerCode.jpg" :width="170" style="height: 170px;"></v-img>
+            <span class="tips-text">关注门墩儿直聘微信公众号</span>
+          </div>
+          <span v-else>{{ item.tips }}</span>
+        </template>
+      </v-tooltip>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'personalSlider' })
+const list = [
+  { mdi: 'mdi-arrow-up-bold', tips: '返回顶部' },
+  { mdi: 'mdi-qrcode', tips: '微信公众号', showImg: 'https://minio.citupro.com/dev/static/mendunerCode.jpg' },
+  { mdi: 'mdi-face-agent', tips: '客服' },
+  { mdi: 'mdi-list-box-outline', tips: '个人简历' }
+]
+</script>
+
+<style lang="scss" scoped>
+.slider-box {
+  width: 44px;
+  height: 180px;
+  background-color: #fff;
+  border-radius: 22px;
+  box-shadow: 0 4px 20px 0 rgba(0,0,0,.06);
+}
+.slider-box-item {
+  width: 44px;
+  height: 44px;
+  padding: 6px 0;
+  text-align: center;
+}
+.icons {
+  color: #ccc;
+  &:hover {
+    color: var(--v-primary-base);
+  }
+}
+.tip-text {
+  font-size: 14px;
+  color: #222;
+}
+</style>

+ 26 - 0
src/locales/en.js

@@ -1,5 +1,31 @@
 export default {
 export default {
   common: {},
   common: {},
+  sys: {
+    api: {
+      operationFailed: 'Operation failed',
+      errorTip: 'Error Tip',
+      errorMessage: 'The operation failed, the system is abnormal!',
+      timeoutMessage: 'Login timed out, please log in again!',
+      apiTimeoutMessage: 'The interface request timed out, please refresh the page and try again!',
+      apiRequestFailed: 'The interface request failed, please try again later!',
+      networkException: 'network anomaly',
+      networkExceptionMsg:
+        'Please check if your network connection is normal! The network is abnormal',
+
+      errMsg401: 'The user does not have permission (token, user name, password error)!',
+      errMsg403: 'The user is authorized, but access is forbidden!',
+      errMsg404: 'Network request error, the resource was not found!',
+      errMsg405: 'Network request error, request method not allowed!',
+      errMsg408: 'Network request timed out!',
+      errMsg500: 'Server error, please contact the administrator!',
+      errMsg501: 'The network is not implemented!',
+      errMsg502: 'Network Error!',
+      errMsg503: 'The service is unavailable, the server is temporarily overloaded or maintained!',
+      errMsg504: 'Network timeout!',
+      errMsg505: 'The http version does not support the request!',
+      errMsg901: 'Demo mode, no write operations are possible!'
+    }
+  },
   login: {
   login: {
     username: 'Username',
     username: 'Username',
     password: 'Password',
     password: 'Password',

+ 27 - 1
src/locales/zh-CN.js

@@ -1,5 +1,31 @@
 export default {
 export default {
-  common: {},
+  common: {
+    confirmTitle: '系统提示'
+  },
+  sys: {
+    api: {
+      operationFailed: '操作失败',
+      errorTip: '错误提示',
+      errorMessage: '操作失败,系统异常!',
+      timeoutMessage: '登录超时,请重新登录!',
+      apiTimeoutMessage: '接口请求超时,请刷新页面重试!',
+      apiRequestFailed: '请求出错,请稍候重试',
+      networkException: '网络异常',
+      networkExceptionMsg: '网络异常,请检查您的网络连接是否正常!',
+      errMsg401: '用户没有权限(令牌、用户名、密码错误)!',
+      errMsg403: '用户得到授权,但是访问是被禁止的。!',
+      errMsg404: '网络请求错误,未找到该资源!',
+      errMsg405: '网络请求错误,请求方法未允许!',
+      errMsg408: '网络请求超时!',
+      errMsg500: '服务器错误,请联系管理员!',
+      errMsg501: '网络未实现!',
+      errMsg502: '网络错误!',
+      errMsg503: '服务不可用,服务器暂时过载或维护!',
+      errMsg504: '网络超时!',
+      errMsg505: 'http版本不支持该请求!',
+      errMsg901: '演示模式,无法进行写操作!'
+    }
+  },
   login: {
   login: {
     username: '用户名',
     username: '用户名',
     password: '密码',
     password: '密码',

+ 2 - 1
src/main.js

@@ -17,6 +17,8 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' // pinia 持
 
 
 import router from './router'
 import router from './router'
 
 
+import './permission'
+
 const pinia = createPinia()
 const pinia = createPinia()
 
 
 pinia.use(piniaPluginPersistedstate)
 pinia.use(piniaPluginPersistedstate)
@@ -24,7 +26,6 @@ pinia.use(piniaPluginPersistedstate)
 const app = createApp(App)
 const app = createApp(App)
 
 
 app.use(pinia)
 app.use(pinia)
-
 app.use(router)
 app.use(router)
 
 
 registerPlugins(app)
 registerPlugins(app)

+ 42 - 0
src/permission.js

@@ -0,0 +1,42 @@
+import router from './router'
+import { useNProgress } from '@/hooks/web/useNProgress'
+import { useTitle } from '@/hooks/web/useTitle'
+import { getToken } from '@/utils/auth'
+
+const { start, done } = useNProgress()
+// 路由不重定向白名单
+const whiteList = [
+  '/',
+  '/login',
+  '/social-login',
+  '/auth-redirect',
+  '/bind',
+  '/register',
+  '/privacyPolicy',
+  '/userAgreement'
+]
+
+// 路由守卫
+router.beforeEach(async (to, from, next) => {
+  start()
+  // loadStart()
+  if (getToken()) {
+    if (to.path === '/login') {
+      next({ path: '/' })
+    } else {
+      next()
+    }
+  } else {
+    if (whiteList.indexOf(to.path) !== -1) {
+      next()
+    } else {
+      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
+    }
+  }
+})
+
+router.afterEach((to) => {
+  useTitle(to?.meta?.title)
+  done() // 结束Progress
+  // loadDone()
+})

+ 11 - 11
src/plugins/snackbar/index.js

@@ -2,7 +2,7 @@ import { createApp } from 'vue'
 import ToastMessage from './message.vue'
 import ToastMessage from './message.vue'
 import vuetify from '@/plugins/vuetify'
 import vuetify from '@/plugins/vuetify'
 
 
-var timeId
+// var timeId
 const toastMessage  = (options)  => {
 const toastMessage  = (options)  => {
   const rootNode = document.createElement("div")
   const rootNode = document.createElement("div")
   document.querySelector('.v-application').appendChild(rootNode)
   document.querySelector('.v-application').appendChild(rootNode)
@@ -10,23 +10,23 @@ const toastMessage  = (options)  => {
   app.use(vuetify)
   app.use(vuetify)
   app.mount(rootNode)
   app.mount(rootNode)
   const { timeout } = options || {}
   const { timeout } = options || {}
-  clearTimeout(timeId)
-  timeId = setTimeout(() => {
+  // clearTimeout(timeId)
+  setTimeout(() => {
       app.unmount()
       app.unmount()
       rootNode.remove()
       rootNode.remove()
   }, timeout || 3000)
   }, timeout || 3000)
 }
 }
-toastMessage.success = (message, variant) => {
-  toastMessage({ message, color: 'success', variant })
+toastMessage.success = (message, variant, timeout) => {
+  toastMessage({ message, color: 'success', variant, timeout })
 }
 }
-toastMessage.error = (message, variant) => {
-  toastMessage({ message, color: 'error', variant })
+toastMessage.error = (message, variant, timeout) => {
+  toastMessage({ message, color: 'error', variant, timeout })
 }
 }
-toastMessage.info = (message, variant) => {
-  toastMessage({ message, color: 'info', variant })
+toastMessage.info = (message, variant, timeout) => {
+  toastMessage({ message, color: 'info', variant, timeout })
 }
 }
-toastMessage.warning = (message, variant) => {
-  toastMessage({ message, color: 'warning', variant })
+toastMessage.warning = (message, variant, timeout) => {
+  toastMessage({ message, color: 'warning', variant, timeout })
 }
 }
 // 注册插件app.use()会自动执行install函数
 // 注册插件app.use()会自动执行install函数
 toastMessage.install = (app) => {
 toastMessage.install = (app) => {

+ 10 - 0
src/router/index.js

@@ -8,4 +8,14 @@ const router = createRouter({
   scrollBehavior: () => ({ left: 0, top: 0 })
   scrollBehavior: () => ({ left: 0, top: 0 })
 })
 })
 
 
+// export const resetRouter = () => {
+//   const resetWhiteNameList = ['Redirect', 'Login', 'NoFind', 'Root']
+//   router.getRoutes().forEach((route) => {
+//     const { name } = route
+//     if (name && !resetWhiteNameList.includes(name)) {
+//       router.hasRoute(name) && router.removeRoute(name)
+//     }
+//   })
+// }
+
 export default router
 export default router

+ 0 - 13
src/router/modules/remaining.js

@@ -9,10 +9,6 @@ const routeArray = [
 ]
 ]
 const items = routeArray[type]
 const items = routeArray[type]
 const remainingRouter = [
 const remainingRouter = [
-  // {
-  //   path: '',
-  //   redirect: '/home'
-  // },
   {
   {
     path: '/login',
     path: '/login',
     component: () => import('@/views/login/index'),
     component: () => import('@/views/login/index'),
@@ -40,15 +36,6 @@ const remainingRouter = [
       title: '隐私政策'
       title: '隐私政策'
     }
     }
   },
   },
-  // {
-  //   path: '/home',
-  //   component: () => import('@/views/Home/index'),
-  //   name: 'home',
-  //   meta: {
-  //     hidden: true,
-  //     title: '首页'
-  //   }
-  // },
   {
   {
     path: '',
     path: '',
     component: Layout,
     component: Layout,

+ 20 - 0
src/store/app.js

@@ -0,0 +1,20 @@
+import { defineStore } from 'pinia'
+import { ref } from 'vue' 
+
+
+export const useAppStore = defineStore('app', 
+  () => {
+    const title = ref(import.meta.env.VITE_APP_TITLE) // 标题
+
+    const setTitle = (txt) => {
+      title.value = txt
+    }
+    return {
+      title,
+      setTitle
+    }
+  },
+  {
+    persist: true, // ref() 持久化响应
+  }
+)

+ 43 - 0
src/store/dictionaries.js

@@ -0,0 +1,43 @@
+/**
+ * 获取字典信息 配置api过期自动读取
+ */
+import { defineStore } from 'pinia'
+import { ref } from 'vue' 
+
+const DICT_CITY_API = {
+  DICT_CITY: Promise.resolve()
+}
+
+export const useDictionariesStore = defineStore('dictionaries', 
+  () => {
+
+    const DICT_CITY = ref({})
+
+    const setDict = (type, val, cacheTime) => {
+      [type].value = {
+        data: val,
+        expire: Date.now() + cacheTime
+      }
+    }
+
+    const getDict = (type) => {
+      return new Promise((resolve) => {
+        if ([type].value.expire && (Date.now() <= [type].value.expire)) {
+          return resolve([type].value.data)
+        }
+        DICT_CITY_API.DICT_CITY({ type }).then(({data}) => {
+          setDict(type, data, Date.now())
+          resolve(data)
+        })
+      })
+    }
+
+    return {
+      getDict,
+      setDict
+    }
+  },
+  {
+    persist: true, // ref() 持久化响应
+  }
+)

+ 15 - 17
src/store/user.js

@@ -1,9 +1,10 @@
 import { defineStore } from 'pinia'
 import { defineStore } from 'pinia'
 import { reactive } from 'vue'
 import { reactive } from 'vue'
-import { setToken, deleteToken } from '@/utils/auth'
+import { setToken, removeToken } from '@/utils/auth'
 import { smsLogin, passwordLogin } from '@/api/common/index'
 import { smsLogin, passwordLogin } from '@/api/common/index'
 import { logout } from '@/api/common/index'
 import { logout } from '@/api/common/index'
 import { getUserInfo } from '@/api/personal/user'
 import { getUserInfo } from '@/api/personal/user'
+import Snackbar from '@/plugins/snackbar'
 
 
 export const userLocaleStore = defineStore('user',
 export const userLocaleStore = defineStore('user',
   () => {
   () => {
@@ -14,12 +15,11 @@ export const userLocaleStore = defineStore('user',
     const handleSmsLogin = async (data) => {
     const handleSmsLogin = async (data) => {
       return new Promise((resolve, reject) => {
       return new Promise((resolve, reject) => {
         smsLogin(data).then(res => {
         smsLogin(data).then(res => {
-          const { data } = res
-          console.log(data, 'res-login')
-          setToken(data.accessToken)
-          accountInfo = data
-          localStorage.setItem('accountInfo', JSON.stringify(data))
-          localStorage.setItem('expiresTime', data.expiresTime) // token过期时间
+          console.log(res, 'login-phone')
+          setToken(res.accessToken)
+          accountInfo = res
+          localStorage.setItem('accountInfo', JSON.stringify(res))
+          localStorage.setItem('expiresTime', res.expiresTime) // token过期时间
           getUserInfos()
           getUserInfos()
           resolve()
           resolve()
         }).catch(err => { reject(err) })
         }).catch(err => { reject(err) })
@@ -30,12 +30,11 @@ export const userLocaleStore = defineStore('user',
     const handlePasswordLogin = async (data) => {
     const handlePasswordLogin = async (data) => {
       return new Promise((resolve, reject) => {
       return new Promise((resolve, reject) => {
         passwordLogin(data).then(res => {
         passwordLogin(data).then(res => {
-          const { data } = res
-          console.log(data, 'res-login-password')
-          setToken(data.accessToken)
-          accountInfo = data
-          localStorage.setItem('accountInfo', JSON.stringify(data))
-          localStorage.setItem('expiresTime', data.expiresTime) // token过期时间
+          console.log(res, 'login-password')
+          setToken(res.accessToken)
+          accountInfo = res
+          localStorage.setItem('accountInfo', JSON.stringify(res))
+          localStorage.setItem('expiresTime', res.expiresTime) // token过期时间
           getUserInfos()
           getUserInfos()
           resolve()
           resolve()
         }).catch(err => { reject(err) })
         }).catch(err => { reject(err) })
@@ -45,19 +44,18 @@ export const userLocaleStore = defineStore('user',
     // 获取当前登录账户信息
     // 获取当前登录账户信息
     const getUserInfos = async () => {
     const getUserInfos = async () => {
       try {
       try {
-        const { data } = await getUserInfo({ id: accountInfo.userId })
+        const data = await getUserInfo({ id: accountInfo.userId })
         userInfo = data
         userInfo = data
         localStorage.setItem('userInfo', JSON.stringify(data))
         localStorage.setItem('userInfo', JSON.stringify(data))
       } catch (error) {
       } catch (error) {
-        console.log(error, 'get-user-info')
-        alert(error.msg)
+        Snackbar.error(error.msg)
       }
       }
     }
     }
 
 
     // 退出登录
     // 退出登录
     const userLogout = async () => {
     const userLogout = async () => {
       await logout()
       await logout()
-      deleteToken()
+      removeToken()
       userInfo = {}
       userInfo = {}
       accountInfo = {}
       accountInfo = {}
       localStorage.clear()
       localStorage.clear()

+ 1 - 0
src/styles/index.css

@@ -1,5 +1,6 @@
 :root {
 :root {
   --zIndex-dialog: 9999;
   --zIndex-dialog: 9999;
+  --default-bgc: #eaf4fe;
   --v-primary-base: #00897B;
   --v-primary-base: #00897B;
   --v-primary-lighten1: #26A69A;
   --v-primary-lighten1: #26A69A;
   --v-primary-lighten2: #4DB6AC;
   --v-primary-lighten2: #4DB6AC;

+ 1 - 1
src/styles/index.min.css

@@ -1 +1 @@
-:root{--zIndex-dialog:9999;--v-primary-base:#00897B;--v-primary-lighten1:#26A69A;--v-primary-lighten2:#4DB6AC;--v-primary-lighten3:#80CBC4;--v-primary-lighten4:#B2DFDB;--default-text:#666}.buttons{height:36px;width:224px}.half-button{height:36px;width:88px}.default-width{width:1184px;min-width:1184px;max-width:1184px;margin:0 auto;align-items:center}
+:root{--zIndex-dialog:9999;--default-bgc:#eaf4fe;--v-primary-base:#00897B;--v-primary-lighten1:#26A69A;--v-primary-lighten2:#4DB6AC;--v-primary-lighten3:#80CBC4;--v-primary-lighten4:#B2DFDB;--default-text:#666}.buttons{height:36px;width:224px}.half-button{height:36px;width:88px}.default-width{width:1184px;min-width:1184px;max-width:1184px;margin:0 auto;align-items:center}

+ 1 - 0
src/styles/index.scss

@@ -1,5 +1,6 @@
 :root {
 :root {
   --zIndex-dialog: 9999;
   --zIndex-dialog: 9999;
+  --default-bgc: #eaf4fe;
   --v-primary-base: #00897B;
   --v-primary-base: #00897B;
   --v-primary-lighten1: #26A69A;
   --v-primary-lighten1: #26A69A;
   --v-primary-lighten2: #4DB6AC;
   --v-primary-lighten2: #4DB6AC;

+ 1 - 24
src/styles/personal/navBar.css

@@ -1,26 +1,3 @@
-.header-login-btn {
-  position: relative;
-}
-
-.header-login-btn:hover .header-login-desc {
-  display: block;
-}
-
-.header-login-btn:hover:after {
-  content: " ";
-  position: absolute;
-  left: 0;
-  right: 0;
-  bottom: -12px;
-  height: 12px;
-  z-index: 1;
-  background: transparent;
-}
-
-.user-nav .header-login-btn {
-  vertical-align: middle;
-}
-
 .user-nav .nav-resume-tools {
 .user-nav .nav-resume-tools {
   display: inline-block;
   display: inline-block;
   vertical-align: middle;
   vertical-align: middle;
@@ -145,7 +122,7 @@
 .user-nav .btn {
 .user-nav .btn {
   display: inline-block;
   display: inline-block;
   box-sizing: content-box;
   box-sizing: content-box;
-  border: 1px solid #00897B;
+  border: 1px solid var(--v-primary-base);
   letter-spacing: 1px;
   letter-spacing: 1px;
   cursor: pointer;
   cursor: pointer;
 }
 }

+ 1 - 1
src/styles/personal/navBar.min.css

@@ -1 +1 @@
-.header-login-btn{position:relative}.header-login-btn:hover .header-login-desc{display:block}.header-login-btn:hover:after{content:" ";position:absolute;left:0;right:0;bottom:-12px;height:12px;z-index:1;background:transparent}.user-nav .header-login-btn{vertical-align:middle}.user-nav .nav-resume-tools{display:inline-block;vertical-align:middle}.user-nav .nav-resume-tools>a{display:inline-block;font-weight:500;color:var(--v-primary-base);line-height:20px;height:auto;padding:3px 7px;vertical-align:middle;border-radius:4px;margin-right:8px;border:1px solid transparent}.banner{z-index:var(--zIndex-nav) !important;color:#fff}.banner .left{height:100%;display:flex;align-items:center;font-size:20px;cursor:pointer}.hover:hover{cursor:pointer;background:rgba(0,0,0,0.03)}.innerBox{position:relative;width:1184px;max-width:1184px;margin:0 auto;align-items:center}.nav-logo{float:left}.nav-city{float:left;position:relative;cursor:pointer;height:49px;line-height:49px;color:var(--v-primary-base);margin-left:50px}.nav-city-selected{display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:84px;vertical-align:middle}.switchover-city{color:var(--v-primary-base);margin-left:10px}.nav{font-size:0;float:left;margin-left:50px;height:49px;line-height:49px}.nav ul{display:flex;white-space:nowrap;vertical-align:top}.nav ul li{text-align:center;font-size:14px;margin:0 5px;list-style:none}.nav li a{display:block;padding:0 5px;position:relative;text-decoration:none;color:var(--v-primary-base)}.user-nav{position:absolute;right:0;color:var(--v-primary-base);height:49px;line-height:49px}.user-nav .btns .btn-outline{border-radius:8px;text-align:center;font-size:14px;height:26px;line-height:26px;min-width:0;padding:0 11px;margin-left:14px;background-color:transparent}.user-nav .btn{display:inline-block;box-sizing:content-box;border:1px solid #00897B;letter-spacing:1px;cursor:pointer}.user-nav a{text-decoration:none}
+.user-nav .nav-resume-tools{display:inline-block;vertical-align:middle}.user-nav .nav-resume-tools>a{display:inline-block;font-weight:500;color:var(--v-primary-base);line-height:20px;height:auto;padding:3px 7px;vertical-align:middle;border-radius:4px;margin-right:8px;border:1px solid transparent}.banner{z-index:var(--zIndex-nav) !important;color:#fff}.banner .left{height:100%;display:flex;align-items:center;font-size:20px;cursor:pointer}.hover:hover{cursor:pointer;background:rgba(0,0,0,0.03)}.innerBox{position:relative;width:1184px;max-width:1184px;margin:0 auto;align-items:center}.nav-logo{float:left}.nav-city{float:left;position:relative;cursor:pointer;height:49px;line-height:49px;color:var(--v-primary-base);margin-left:50px}.nav-city-selected{display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:84px;vertical-align:middle}.switchover-city{color:var(--v-primary-base);margin-left:10px}.nav{font-size:0;float:left;margin-left:50px;height:49px;line-height:49px}.nav ul{display:flex;white-space:nowrap;vertical-align:top}.nav ul li{text-align:center;font-size:14px;margin:0 5px;list-style:none}.nav li a{display:block;padding:0 5px;position:relative;text-decoration:none;color:var(--v-primary-base)}.user-nav{position:absolute;right:0;color:var(--v-primary-base);height:49px;line-height:49px}.user-nav .btns .btn-outline{border-radius:8px;text-align:center;font-size:14px;height:26px;line-height:26px;min-width:0;padding:0 11px;margin-left:14px;background-color:transparent}.user-nav .btn{display:inline-block;box-sizing:content-box;border:1px solid var(--v-primary-base);letter-spacing:1px;cursor:pointer}.user-nav a{text-decoration:none}

+ 1 - 20
src/styles/personal/navBar.scss

@@ -1,22 +1,3 @@
-.header-login-btn {
-  position: relative;
-}
-.header-login-btn:hover .header-login-desc {
-  display: block;
-}
-.header-login-btn:hover:after {
-  content: " ";
-  position: absolute;
-  left: 0;
-  right: 0;
-  bottom: -12px;
-  height: 12px;
-  z-index: 1;
-  background: transparent;
-}
-.user-nav .header-login-btn {
-  vertical-align: middle;
-}
 .user-nav .nav-resume-tools {
 .user-nav .nav-resume-tools {
   display: inline-block;
   display: inline-block;
   vertical-align: middle;
   vertical-align: middle;
@@ -127,7 +108,7 @@
 .user-nav .btn {
 .user-nav .btn {
   display: inline-block;
   display: inline-block;
   box-sizing: content-box;
   box-sizing: content-box;
-  border: 1px solid #00897B;
+  border: 1px solid var(--v-primary-base);
   letter-spacing: 1px;
   letter-spacing: 1px;
   cursor: pointer;
   cursor: pointer;
 }
 }

+ 13 - 6
src/utils/auth.js

@@ -1,20 +1,27 @@
 // 获取token
 // 获取token
 export const getToken = () => {
 export const getToken = () => {
-  return localStorage.getItem('access_token')
+  return localStorage.getItem('ACCESS_TOKEN')
 }
 }
 
 
 // 设置token
 // 设置token
 export const setToken = (token) => {
 export const setToken = (token) => {
-  return localStorage.setItem('access_token', token)
+  return localStorage.setItem('ACCESS_TOKEN', token)
 }
 }
 
 
 // 清除token
 // 清除token
-export const deleteToken = () => {
-  return localStorage.removeItem('access_token')
+export const removeToken = () => {
+  return localStorage.removeItem('ACCESS_TOKEN')
 }
 }
 // ?
 // ?
-export const getRefreshToken = () => {}
+export const getRefreshToken = () => {
+  return localStorage.getItem('REFRESH_TOKEN')
+}
 
 
 // 租户ID
 // 租户ID
-export const getTenantId = () => {}
+export const getTenantId = () => {
+  return localStorage.getItem('tenantId')
+}
 
 
+export const setTenantId = (username) => {
+  localStorage.setItem('tenantId', username)
+}

+ 117 - 0
src/utils/is.js

@@ -0,0 +1,117 @@
+// copy to vben-admin
+
+const toString = Object.prototype.toString
+
+export const is = (val, type) => {
+  return toString.call(val) === `[object ${type}]`
+}
+
+export const isDef = (val) => {
+  return typeof val !== 'undefined'
+}
+
+export const isUnDef = (val) => {
+  return !isDef(val)
+}
+
+export const isObject = (val) => {
+  return val !== null && is(val, 'Object')
+}
+
+export const isEmpty = (val) => {
+  if (val === null) {
+    return true
+  }
+  if (isArray(val) || isString(val)) {
+    return val.length === 0
+  }
+
+  if (val instanceof Map || val instanceof Set) {
+    return val.size === 0
+  }
+
+  if (isObject(val)) {
+    return Object.keys(val).length === 0
+  }
+
+  return false
+}
+
+export const isDate = (val) => {
+  return is(val, 'Date')
+}
+
+export const isNull = (val) => {
+  return val === null
+}
+
+export const isNullAndUnDef = (val) => {
+  return isUnDef(val) && isNull(val)
+}
+
+export const isNullOrUnDef = (val) => {
+  return isUnDef(val) || isNull(val)
+}
+
+export const isNumber = (val) => {
+  return is(val, 'Number')
+}
+
+export const isPromise = (val) => {
+  return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
+}
+
+export const isString = (val) => {
+  return is(val, 'String')
+}
+
+export const isFunction = (val) => {
+  return typeof val === 'function'
+}
+
+export const isBoolean = (val) => {
+  return is(val, 'Boolean')
+}
+
+export const isRegExp = (val) => {
+  return is(val, 'RegExp')
+}
+
+export const isArray = (val) => {
+  return val && Array.isArray(val)
+}
+
+export const isWindow = (val) => {
+  return typeof window !== 'undefined' && is(val, 'Window')
+}
+
+export const isElement = (val) => {
+  return isObject(val) && !!val.tagName
+}
+
+export const isMap = (val) => {
+  return is(val, 'Map')
+}
+
+export const isServer = typeof window === 'undefined'
+
+export const isClient = !isServer
+
+// export const isUrl = (path) => {
+//   const reg =
+//     /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
+//   return reg.test(path)
+// }
+
+export const isDark = () => {
+  return window.matchMedia('(prefers-color-scheme: dark)').matches
+}
+
+// 是否是图片链接
+export const isImgPath = (path) => {
+  return /(https?:\/\/|data:image\/).*?\.(png|jpg|jpeg|gif|svg|webp|ico)/gi.test(path)
+}
+
+export const isEmptyVal = (val) => {
+  return val === '' || val === null || val === undefined
+}

+ 0 - 130
src/utils/request.js

@@ -1,130 +0,0 @@
-import axios from 'axios'
-import { blobToJson } from '@/utils'
-import { getToken } from '@/utils/auth'
-import qs from 'qs'
-
-// create an axios instance
-const service = axios.create({
-  baseURL: import.meta.env.VITE_BASE_URL,
-  timeout: 120000 // request timeout
-})
-
-// request interceptor
-// 发送请求拦截器
-service.interceptors.request.use(
-  config => {
-    config.headers['tenant-id'] = import.meta.env.VITE_TENANTCODE
-    if (getToken()) {
-      config.headers.token = `Bearer ${getToken()}`
-      config.headers.Authorization = `Bearer ${getToken()}`
-    }
-    config.headers.token = `Bearer ${getToken()}`
-    return config
-  },
-  error => {
-    console.error(error)
-    return Promise.reject(error)
-  }
-)
-
-// 请求返回之后的拦截器
-service.interceptors.response.use(
-  async response => {
-    const res = response.data
-
-    if (response.request.responseType === 'blob') {
-      // 返回的文件流当报错时转化成json
-      if (response.headers['content-type'] === 'application/json') {
-        try {
-          const result = await blobToJson(res)
-          return Promise.reject(result.msg)
-        } catch (error) {
-          return Promise.reject(error)
-        }
-      }
-      const name = response.headers['content-disposition']
-      return {
-        data: res,
-        name: name ? decodeURI(name.replace('attachment;filename=', '')) : '未命名'
-      }
-    }
-
-    // if ([50008, 50012, 50014, 402000, 401].includes(res.code)) {
-    //   // 登录过期
-    //   return Promise.reject(res.msg)
-    // }
-    // // 登录验证码过期
-    // if (res.code === 60902) {
-    //   return Promise.reject(res)
-    // }
-    if (res.code !== 0) {
-      if (res.data) {
-        return Promise.reject(res)
-      }
-      return Promise.reject(res.msg)
-    }
-    return res
-  },
-  error => {
-    console.error(error)
-    return Promise.reject(error)
-  }
-)
-
-// 请求方法
-const http = {
-  post (url, params) {
-    return service.post(url, params, {
-      transformRequest: [(params) => {
-        return JSON.stringify(params)
-      }],
-      headers: {
-        'Content-Type': 'application/json'
-      }
-    })
-  },
-  get (url, params) {
-    return service.get(url, {
-      params: params,
-      paramsSerializer: (params) => {
-        return qs.stringify(params)
-      }
-    })
-  },
-  put(url, params) {
-    return service.put(url, params, {
-      transformRequest: [(params) => {
-        return JSON.stringify(params)
-      }],
-      headers: {
-        'Content-Type': 'application/json'
-      }
-    })
-  },
-  formData (url, params) {
-    return service.post(url, params, {
-      timeout: 600000,
-      headers: {
-        'Content-Type': 'application/x-www-form-urlencoded'
-      }
-    })
-  },
-  upload (url, params) {
-    return service.post(url, params, {
-      timeout: 600000,
-      headers: {
-        'Content-Type': 'multipart/form-data'
-      }
-    })
-  },
-  download (url, params) {
-    return service.post(url, params, {
-      timeout: 10000,
-      headers: {
-        'Content-Type': 'application/json'
-      },
-      responseType: 'blob'
-    })
-  }
-}
-export default http

+ 4 - 6
src/views/Home/personal/account/dynamic/editPassword.vue

@@ -11,7 +11,7 @@
           placeholder="请输入密码" 
           placeholder="请输入密码" 
           variant="outlined" 
           variant="outlined" 
           density="compact"
           density="compact"
-          color="#00897B"
+          color="primary"
           prepend-inner-icon="mdi-lock-outline" 
           prepend-inner-icon="mdi-lock-outline" 
           :append-inner-icon="passwordType ? 'mdi-eye-outline' : 'mdi-eye-off-outline'"
           :append-inner-icon="passwordType ? 'mdi-eye-outline' : 'mdi-eye-off-outline'"
           :type="passwordType ? 'text' : 'password'"
           :type="passwordType ? 'text' : 'password'"
@@ -23,7 +23,7 @@
           placeholder="请再次输入密码" 
           placeholder="请再次输入密码" 
           variant="outlined" 
           variant="outlined" 
           density="compact"
           density="compact"
-          color="#00897B"
+          color="primary"
           prepend-inner-icon="mdi-lock-outline" 
           prepend-inner-icon="mdi-lock-outline" 
           :append-inner-icon="show ? 'mdi-eye-outline' : 'mdi-eye-off-outline'"
           :append-inner-icon="show ? 'mdi-eye-outline' : 'mdi-eye-off-outline'"
           :type="show ? 'text' : 'password'"
           :type="show ? 'text' : 'password'"
@@ -42,6 +42,7 @@
 import PhonePage from '@/components/VerificationCode'
 import PhonePage from '@/components/VerificationCode'
 import { updatePassword } from '@/api/common/index'
 import { updatePassword } from '@/api/common/index'
 import { ref, reactive, computed } from 'vue'
 import { ref, reactive, computed } from 'vue'
+import Snackbar from '@/plugins/snackbar'
 
 
 const phoneRef = ref()
 const phoneRef = ref()
 const passwordRef = ref()
 const passwordRef = ref()
@@ -66,10 +67,7 @@ const handleSubmit = async () => {
   loading.value = true
   loading.value = true
   try {
   try {
     await updatePassword({ password: query.password, code: phoneRef.value.loginData.code })
     await updatePassword({ password: query.password, code: phoneRef.value.loginData.code })
-    console.log('success')
-  } catch (error) {
-    console.log(error, 'error')
-    alert(error.msg)
+    Snackbar.success('修改成功')
   } finally {
   } finally {
     loading.value = false
     loading.value = false
   }
   }

+ 4 - 1
src/views/Home/personal/components/hotPromotedPositions.vue

@@ -1,6 +1,6 @@
 <template>
 <template>
   <div>
   <div>
-    <v-tabs v-model="tab" align-tabs="start" color="#00897B" bg-color="#fff">
+    <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#fff">
       <v-tab :value="1">推荐职位</v-tab>
       <v-tab :value="1">推荐职位</v-tab>
       <v-tab :value="2">最新职位</v-tab>
       <v-tab :value="2">最新职位</v-tab>
       <v-tab :value="3">急聘职位</v-tab>
       <v-tab :value="3">急聘职位</v-tab>
@@ -16,6 +16,9 @@
         <PositionCard></PositionCard>
         <PositionCard></PositionCard>
       </v-window-item>
       </v-window-item>
     </v-window>
     </v-window>
+    <div class="text-center">
+      <v-btn class="buttons" color="primary">查看更多职位</v-btn>
+    </div>
   </div>
   </div>
 </template>
 </template>
 
 

+ 12 - 0
src/views/Home/personal/components/popularEnterprises.vue

@@ -0,0 +1,12 @@
+<template>
+  <div>
+    <HotPromoted></HotPromoted>
+    <div class="text-center">
+      <v-btn class="buttons" color="primary">查看更多企业</v-btn>
+    </div>
+  </div>
+</template>
+
+<script setup name="popularEnterprises">
+import HotPromoted from '@/components/Enterprise/hotPromoted.vue'
+</script>

+ 1 - 1
src/views/Home/personal/components/search.vue

@@ -11,7 +11,7 @@
     <v-text-field
     <v-text-field
       v-model="value"
       v-model="value"
       placeholder="搜索职位/公司"
       placeholder="搜索职位/公司"
-      color="#00897B"
+      color="primary"
       variant="plain"
       variant="plain"
       density="compact"
       density="compact"
       :hide-details="true"
       :hide-details="true"

+ 2 - 0
src/views/Home/personal/index.vue

@@ -4,6 +4,7 @@
     <headSearch></headSearch>
     <headSearch></headSearch>
     <div class="default-width mt-3">
     <div class="default-width mt-3">
       <hotPromotedPositions></hotPromotedPositions>
       <hotPromotedPositions></hotPromotedPositions>
+      <PopularEnterprises class="mt-10"></PopularEnterprises>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
@@ -12,6 +13,7 @@
 import headCarousel from './components/headCarousel.vue'
 import headCarousel from './components/headCarousel.vue'
 import headSearch from './components/search.vue'
 import headSearch from './components/search.vue'
 import hotPromotedPositions from './components/hotPromotedPositions.vue'
 import hotPromotedPositions from './components/hotPromotedPositions.vue'
+import PopularEnterprises from './components/popularEnterprises.vue'
 defineOptions({ name:'personal-index'})
 defineOptions({ name:'personal-index'})
 </script>
 </script>
 
 

+ 4 - 4
src/views/enterprise/components/register.vue

@@ -3,7 +3,7 @@
   <div>
   <div>
     <!-- (输入提示框-> 如果是选择了某个公司,带出公司信息,展开预览信息,点击加入) -->
     <!-- (输入提示框-> 如果是选择了某个公司,带出公司信息,展开预览信息,点击加入) -->
     <!-- 多个招聘专员注册多个boss账号,但这些账号只能认证一个公司主体。新建的需要门墩后台审核,加入的由公司主体账户审核 -->
     <!-- 多个招聘专员注册多个boss账号,但这些账号只能认证一个公司主体。新建的需要门墩后台审核,加入的由公司主体账户审核 -->
-      <v-tabs v-model="tab" align-tabs="center" color="#00897B">
+      <v-tabs v-model="tab" align-tabs="center" color="primary">
         <v-tab :value="1">加入公司</v-tab>
         <v-tab :value="1">加入公司</v-tab>
         <v-tab :value="2">新建公司</v-tab>
         <v-tab :value="2">新建公司</v-tab>
       </v-tabs>
       </v-tabs>
@@ -16,7 +16,7 @@
               v-model="addInForm.username"
               v-model="addInForm.username"
               label="姓名"
               label="姓名"
               placeholder="请输入姓名" 
               placeholder="请输入姓名" 
-              color="#00897B" 
+              color="primary" 
               variant="outlined" 
               variant="outlined" 
               density="compact" 
               density="compact" 
               :rules="[v=> !!v || '请输入姓名']"
               :rules="[v=> !!v || '请输入姓名']"
@@ -25,7 +25,7 @@
               v-model="addInForm.company"
               v-model="addInForm.company"
               label="公司名称"
               label="公司名称"
               placeholder="请输入公司名称" 
               placeholder="请输入公司名称" 
-              color="#00897B" 
+              color="primary" 
               variant="outlined" 
               variant="outlined" 
               density="compact" 
               density="compact" 
               return-object
               return-object
@@ -39,7 +39,7 @@
               placeholder="请输入职务" 
               placeholder="请输入职务" 
               variant="outlined" 
               variant="outlined" 
               density="compact"
               density="compact"
-              color="#00897B"
+              color="primary"
               :rules="[v=> !!v || '请输入职务']"
               :rules="[v=> !!v || '请输入职务']"
             ></v-text-field>
             ></v-text-field>
           </v-form>
           </v-form>

+ 3 - 3
src/views/login/components/passwordPage.vue

@@ -1,6 +1,6 @@
 <template>
 <template>
   <v-form ref="passwordForm" @submit.prevent>
   <v-form ref="passwordForm" @submit.prevent>
-    <v-text-field v-model="loginData.phone" placeholder="请输入手机号" color="#00897B" variant="outlined" density="compact" :rules="phoneRules" validate-on="input">
+    <v-text-field v-model="loginData.phone" placeholder="请输入手机号" color="primary" variant="outlined" density="compact" :rules="phoneRules" validate-on="input">
         <template v-slot:prepend-inner>
         <template v-slot:prepend-inner>
           <span class="d-flex">
           <span class="d-flex">
             <v-icon icon="mdi-cellphone" size="20"></v-icon>
             <v-icon icon="mdi-cellphone" size="20"></v-icon>
@@ -23,7 +23,7 @@
       placeholder="请输入密码" 
       placeholder="请输入密码" 
       variant="outlined" 
       variant="outlined" 
       density="compact"
       density="compact"
-      color="#00897B"
+      color="primary"
       prepend-inner-icon="mdi-lock-outline" 
       prepend-inner-icon="mdi-lock-outline" 
       :append-inner-icon="passwordType ? 'mdi-eye-outline' : 'mdi-eye-off-outline'"
       :append-inner-icon="passwordType ? 'mdi-eye-outline' : 'mdi-eye-off-outline'"
       :type="passwordType ? 'text' : 'password'"
       :type="passwordType ? 'text' : 'password'"
@@ -60,7 +60,7 @@ const handleChangeCurrentArea = (e) => {
 
 
 const loginData = reactive({
 const loginData = reactive({
   phone: '13229740091',
   phone: '13229740091',
-  password: ''
+  password: '1111'
 })
 })
 
 
 const passwordForm = ref()
 const passwordForm = ref()

+ 0 - 140
src/views/login/components/phonePage.vue

@@ -1,140 +0,0 @@
-<template>
-  <div>
-    <v-form @submit.prevent ref="phoneForm">
-      <v-text-field v-model="loginData.phone" placeholder="请输入手机号" color="#00897B" variant="outlined" density="compact" :rules="phoneRules" validate-on="input">
-        <template v-slot:prepend-inner>
-          <span class="d-flex">
-            <v-icon icon="mdi-cellphone" size="20"></v-icon>
-            <span class="d-flex" id="menu-activator">
-              <span class="phone-number">{{ currentArea }}</span>
-              <v-icon size="20">mdi-chevron-down</v-icon>
-            </span>
-            <v-menu activator="#menu-activator">
-              <v-list>
-                <v-list-item v-for="(item, index) in items" :key="index" :value="index" @click="handleChangeCurrentArea(item)">
-                  <v-list-item-title>{{ item.label }}</v-list-item-title>
-                </v-list-item>
-              </v-list>
-            </v-menu>
-          </span>
-        </template>
-      </v-text-field>
-      <v-text-field v-model="loginData.code" placeholder="请输入验证码" color="#00897B" variant="outlined" density="compact" prepend-inner-icon="mdi-security" :rules="[v=> !!v || '请填写验证码']">
-        <template #append-inner>
-          <span v-if="showCode" class="login-code" @click="handleCode">获取验证码</span>
-          <span v-else class="disable">重新获取{{ count }}s</span>
-        </template>
-      </v-text-field>
-    </v-form>
-    <v-snackbar v-model="tips.show" :color="tips.color" :timeout="1500" location="top">{{ tips.text }}</v-snackbar>
-  </div>
-</template>
-
-<script setup name="phoneIndex">
-import { ref, reactive, defineExpose } from 'vue'
-import { setCodeTime } from '@/utils/code'
-import { sendSmsCode } from '@/api/common/index'
-
-const phoneRules = ref([
-  value => {
-    if (value) return true
-    return '请输入手机号'
-  },
-  value => {
-    if (value?.length <= 11 && /^1[3456789]\d{9}$/.test(value)) return true
-    return '请输入正确的手机号码'
-  }
-])
-
-const tips = reactive({
-  show: false,
-  color: '',
-  text: ''
-})
-
-// 手机号区域
-const currentArea = ref('0086')
-const items = [
-  { label: '中国大陆-0086', value: '0086' }
-]
-const handleChangeCurrentArea = (e) => {
-  currentArea.value = e.value
-}
-
-// 获取验证码
-const showCode = ref(true)
-const count = ref(0)
-const timer = ref(null)
-const handleCode = () => {
-  if (!loginData.phone) {
-    tips.text = '请输入手机号码'
-    tips.color = 'warning'
-    tips.show = true
-    return
-  }
-  count.value = 60
-  setTime()
-  getSmsCode()
-}
-const getSmsCode = async () => {
-  const query = {
-    phone: loginData.phone,
-    scene: 30
-  }
-  try {
-    await sendSmsCode(query)
-  } catch (error) {
-    console.log(error, 'error')
-  }
-}
-const setTime = () => {
-  showCode.value = false
-  timer.value = setInterval(() => {
-    let number = count.value
-    if (number > 0 && number <= 60) {
-      count.value--
-      setCodeTime(number - 1)
-    } else {
-      showCode.value = true
-      clearInterval(timer.value)
-      timer.value = null
-    }
-  }, 1000)
-}
-const autoTimer = () => {
-  count.value = 0
-  if(!count.value) return
-  setTime()
-}
-autoTimer()
-
-const loginData = reactive({
-  phone: '13229740091',
-  code: ''
-})
-
-const phoneForm = ref()
-
-defineExpose({
-  loginData, 
-  phoneForm
-})
-</script>
-
-<style lang="scss" scoped>
-.login-code {
-  width: 62px;
-  color: var(--v-primary-base); 
-  font-size: 12px; 
-  cursor: pointer;
-}
-.disable {
-  width: 72px;
-  color: grey;
-  font-size: 12px;
-}
-.phone-number {
-  width: 34px;
-  font-size: 12px;
-}
-</style>

+ 11 - 11
src/views/login/components/privacyPolicy.vue

@@ -28,11 +28,11 @@
         <p>十、 如何联系我们</p>
         <p>十、 如何联系我们</p>
         <p>十一、 争议解决方式</p>
         <p>十一、 争议解决方式</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>一、引言</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>一、引言</strong></span></p>
         <p>为切实保护门墩儿直聘用户隐私权,优化用户体验,门墩儿直聘根据现行法规及政策,制定本《隐私保护政策》。本《隐私保护政策》将详细说明门墩儿直聘在获取、管理及保护用户个人信息方面的政策及措施。本《隐私保护政策》适用于门墩儿直聘向您提供的所有服务,无论您是通过计算机设备、移动终端或其他设备获得的门墩儿直聘服务。</p>
         <p>为切实保护门墩儿直聘用户隐私权,优化用户体验,门墩儿直聘根据现行法规及政策,制定本《隐私保护政策》。本《隐私保护政策》将详细说明门墩儿直聘在获取、管理及保护用户个人信息方面的政策及措施。本《隐私保护政策》适用于门墩儿直聘向您提供的所有服务,无论您是通过计算机设备、移动终端或其他设备获得的门墩儿直聘服务。</p>
         <p>本《隐私保护政策》旨在帮助您了解我们会收集哪些数据、为什么收集这些数据,会利用这些数据做些什么及如何保护这些数据。我们希望您在使用门墩儿直聘服务前仔细阅读并明确您已经充分理解、接受本《隐私保护政策》的内容,希望您可以根据自己的理解做出合适的选择。您一旦开始使用门墩儿直聘平台服务,即表示您认同我们在本《隐私保护政策》中所述内容。在我们更新本《隐私保护政策》后,您继续使用我们的产品与/或服务,即意味着您同意本《隐私保护政策》(含更新版本)内容,并且同意我们按照本《隐私保护政策》收集、使用、保存和共享您的相关信息。此外,在将您的信息用于本《隐私保护政策》未涵盖的用途时,我们会事先征求您的同意。</p>
         <p>本《隐私保护政策》旨在帮助您了解我们会收集哪些数据、为什么收集这些数据,会利用这些数据做些什么及如何保护这些数据。我们希望您在使用门墩儿直聘服务前仔细阅读并明确您已经充分理解、接受本《隐私保护政策》的内容,希望您可以根据自己的理解做出合适的选择。您一旦开始使用门墩儿直聘平台服务,即表示您认同我们在本《隐私保护政策》中所述内容。在我们更新本《隐私保护政策》后,您继续使用我们的产品与/或服务,即意味着您同意本《隐私保护政策》(含更新版本)内容,并且同意我们按照本《隐私保护政策》收集、使用、保存和共享您的相关信息。此外,在将您的信息用于本《隐私保护政策》未涵盖的用途时,我们会事先征求您的同意。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><strong><span style="color: #00897B;">二、我们如何收集和使用您的个人信息</span></strong></p>
+        <p><strong><span style="color: --var(--v-primary-base);">二、我们如何收集和使用您的个人信息</span></strong></p>
         <p>我们深知个人信息对您的重要性,也深知为您的信息提供有效保护是我们业务健康可持续发展的基石。感谢您对门墩儿直聘平台的使用和信任!我们致力于维持您对我们的信任,恪守适用法律和我们对您的承诺,尽全力保证您的个人信息安全和合理使用。我们郑重承诺,我们已按业界成熟的安全标准,采取相应的安全保护措施来保护您的个人信息。我们承诺将坚持合法、正当、“最小必要化”原则收集、使用、存储和传输用户信息,不会收集与业务无关的信息或采取不正当的方式收集信息,并通过用户协议和隐私保护政策告知您相关信息的使用目的和范围。</p>
         <p>我们深知个人信息对您的重要性,也深知为您的信息提供有效保护是我们业务健康可持续发展的基石。感谢您对门墩儿直聘平台的使用和信任!我们致力于维持您对我们的信任,恪守适用法律和我们对您的承诺,尽全力保证您的个人信息安全和合理使用。我们郑重承诺,我们已按业界成熟的安全标准,采取相应的安全保护措施来保护您的个人信息。我们承诺将坚持合法、正当、“最小必要化”原则收集、使用、存储和传输用户信息,不会收集与业务无关的信息或采取不正当的方式收集信息,并通过用户协议和隐私保护政策告知您相关信息的使用目的和范围。</p>
         <p><br></p>
         <p><br></p>
         <p><strong>(一)个人信息的定义</strong></p>
         <p><strong>(一)个人信息的定义</strong></p>
@@ -241,10 +241,10 @@
         <p>&nbsp;</p>
         <p>&nbsp;</p>
         <p>如果您对我们收集和使用您的个人信息有任何疑问或需要提供进一步的信息,请通过本《隐私保护政策》公布的联系方式与我们联系。</p>
         <p>如果您对我们收集和使用您的个人信息有任何疑问或需要提供进一步的信息,请通过本《隐私保护政策》公布的联系方式与我们联系。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>三、我们如何使用Cookie和同类技术</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>三、我们如何使用Cookie和同类技术</strong></span></p>
         <p>为确保网站正常运转,我们会在您的计算机或移动设备上存储名为 Cookie 的小数据文件。Cookie 通常包含用户身份标识符、城市名称以及一些字符。Cookie主要的功能是能够存储您的偏好,便于您使用网站产品和服务,以及帮助网站统计独立访客数量。运用Cookie技术,我们能够为您提供更加周到的服务。我们不会将 Cookie 用于本《隐私保护政策》所述目的之外的任何用途。您可根据自己的偏好管理或删除 Cookie。有关详情,请参见 【AboutCookies.org】。您可以清除计算机上保存的所有 Cookie,大部分网络浏览器都设有阻止 Cookie 的功能。但如果您这么做,则需要在每一次访问我们的网站时亲自更改用户设置,但您可能因为该修改,无法登录或使用依赖于Cookie的百度提供的服务或功能。 您可以通过更改您的浏览器设置限制门墩儿直聘对Cookie的使用。以【Chrome】浏览器为例,您可以在【Chrome】浏览器右上方的下拉菜单的“浏览器设置”中,通过“【设置-高级-清楚浏览数据】”,选择清除您的Cookie。</p>
         <p>为确保网站正常运转,我们会在您的计算机或移动设备上存储名为 Cookie 的小数据文件。Cookie 通常包含用户身份标识符、城市名称以及一些字符。Cookie主要的功能是能够存储您的偏好,便于您使用网站产品和服务,以及帮助网站统计独立访客数量。运用Cookie技术,我们能够为您提供更加周到的服务。我们不会将 Cookie 用于本《隐私保护政策》所述目的之外的任何用途。您可根据自己的偏好管理或删除 Cookie。有关详情,请参见 【AboutCookies.org】。您可以清除计算机上保存的所有 Cookie,大部分网络浏览器都设有阻止 Cookie 的功能。但如果您这么做,则需要在每一次访问我们的网站时亲自更改用户设置,但您可能因为该修改,无法登录或使用依赖于Cookie的百度提供的服务或功能。 您可以通过更改您的浏览器设置限制门墩儿直聘对Cookie的使用。以【Chrome】浏览器为例,您可以在【Chrome】浏览器右上方的下拉菜单的“浏览器设置”中,通过“【设置-高级-清楚浏览数据】”,选择清除您的Cookie。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>四、我们如何共享、转让、公开披露您的个人信息</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>四、我们如何共享、转让、公开披露您的个人信息</strong></span></p>
         <p>我们不会向任何第三方共享、转让您的个人信息,除非经过您本人事先授权同意,或者共享、转让的个人信息是去标识化处理后的信息,且共享第三方无法重新识别此类信息的自然人主体。</p>
         <p>我们不会向任何第三方共享、转让您的个人信息,除非经过您本人事先授权同意,或者共享、转让的个人信息是去标识化处理后的信息,且共享第三方无法重新识别此类信息的自然人主体。</p>
         <p>1.共享</p>
         <p>1.共享</p>
         <p><br></p>
         <p><br></p>
@@ -279,7 +279,7 @@
         <p>4.9 法律法规规定的其他情形。</p>
         <p>4.9 法律法规规定的其他情形。</p>
         <p>根据法律规定,共享、转让经去标识化处理的个人信息,且确保数据接收方无法复原并重新识别个人信息主体的,不属于个人信息的对外共享、转让及公开披露行为,对此类数据的保存及处理将无需另行向您通知并征得您的同意。</p>
         <p>根据法律规定,共享、转让经去标识化处理的个人信息,且确保数据接收方无法复原并重新识别个人信息主体的,不属于个人信息的对外共享、转让及公开披露行为,对此类数据的保存及处理将无需另行向您通知并征得您的同意。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>五、我们如何保存及保护您的个人信息</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>五、我们如何保存及保护您的个人信息</strong></span></p>
         <p>1.保存期限</p>
         <p>1.保存期限</p>
         <p>您在使用门墩儿直聘产品及服务期间,我们将持续为您保存您的个人信息。如果您注销帐户或主动删除上述信息,我们将依据网络安全法等法律法规规定保存您的信息。在您注销帐户或主动删除上述信息后,我们不会再对您的个人信息进行商业化使用,但我们可能会对您的个人信息进行删除或匿名化处理后使用。</p>
         <p>您在使用门墩儿直聘产品及服务期间,我们将持续为您保存您的个人信息。如果您注销帐户或主动删除上述信息,我们将依据网络安全法等法律法规规定保存您的信息。在您注销帐户或主动删除上述信息后,我们不会再对您的个人信息进行商业化使用,但我们可能会对您的个人信息进行删除或匿名化处理后使用。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
@@ -308,7 +308,7 @@
         <p>5.3 在不幸发生个人信息安全事件后,我们将按照法律法规的要求,及时通过软件内部提醒、邮件、短信或电话等方式向您告知安全事件的基本情况和可能的影响、我们已采取或将要采取的处理措施、您可自主防范和降低的风险的建议、对您的补救措施等。我们将及时将事件相关情况通过上述方式告知您,难以逐一告知时我们会采取合理、有效的方式发布公告。同时,我们还将按照监管部门要求,主动上报个人信息安全事件的处置情况。</p>
         <p>5.3 在不幸发生个人信息安全事件后,我们将按照法律法规的要求,及时通过软件内部提醒、邮件、短信或电话等方式向您告知安全事件的基本情况和可能的影响、我们已采取或将要采取的处理措施、您可自主防范和降低的风险的建议、对您的补救措施等。我们将及时将事件相关情况通过上述方式告知您,难以逐一告知时我们会采取合理、有效的方式发布公告。同时,我们还将按照监管部门要求,主动上报个人信息安全事件的处置情况。</p>
         <p>我们将不定期更新并公开安全风险、个人信息安全影响评估报告等有关内容,您可通过门墩儿直聘公告方式获得。</p>
         <p>我们将不定期更新并公开安全风险、个人信息安全影响评估报告等有关内容,您可通过门墩儿直聘公告方式获得。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>六、您控制个人信息的权利</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>六、您控制个人信息的权利</strong></span></p>
         <p>按照中国相关的法律、法规、标准,以及其他国家、地区的通行做法,我们保障您对自己的个人信息行使以下权利:1. 访问和修改您的个人信息<br>(1) 访问修改姓名</p>
         <p>按照中国相关的法律、法规、标准,以及其他国家、地区的通行做法,我们保障您对自己的个人信息行使以下权利:1. 访问和修改您的个人信息<br>(1) 访问修改姓名</p>
         <p>可以在【我的门墩儿直聘&gt;编辑简历&gt;基本信息】中显示和修改</p>
         <p>可以在【我的门墩儿直聘&gt;编辑简历&gt;基本信息】中显示和修改</p>
         <p>(2) 访问和修改性别</p>
         <p>(2) 访问和修改性别</p>
@@ -357,16 +357,16 @@
         <p>(5)响应个人信息主体的请求将导致您或其他个人、组织的合法权益受到严重损害的;</p>
         <p>(5)响应个人信息主体的请求将导致您或其他个人、组织的合法权益受到严重损害的;</p>
         <p>(6)涉及商业秘密的。</p>
         <p>(6)涉及商业秘密的。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>七、我们如何处理未成年人的个人信息</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>七、我们如何处理未成年人的个人信息</strong></span></p>
         <p>门墩儿直聘非常重视对未成年人信息的保护。</p>
         <p>门墩儿直聘非常重视对未成年人信息的保护。</p>
         <p>基于我们的产品、网站和服务的性质,我们不向16周岁以下的未成年人提供服务,因此无法收集相关信息。若您是已满16周岁不满18周岁的未成年人,建议您请您的监护人仔细阅读本《隐私保护政策》,并在征得您的监护人同意的前提下使用我们的产品和/或服务或向我们提供信息。</p>
         <p>基于我们的产品、网站和服务的性质,我们不向16周岁以下的未成年人提供服务,因此无法收集相关信息。若您是已满16周岁不满18周岁的未成年人,建议您请您的监护人仔细阅读本《隐私保护政策》,并在征得您的监护人同意的前提下使用我们的产品和/或服务或向我们提供信息。</p>
         <p>如您的监护人不同意您按照本《隐私保护政策》使用我们的服务或向我们提供信息,请您立即终止使用我们的服务并及时通知我们,以便我们采取相应的措施。</p>
         <p>如您的监护人不同意您按照本《隐私保护政策》使用我们的服务或向我们提供信息,请您立即终止使用我们的服务并及时通知我们,以便我们采取相应的措施。</p>
         <p>如果监护人发现我们在未获监护人同意的情况下收集了未成年人的个人信息,请通过本《隐私保护政策》公布的联系方式联系我们,我们会尽快删除相关数据。</p>
         <p>如果监护人发现我们在未获监护人同意的情况下收集了未成年人的个人信息,请通过本《隐私保护政策》公布的联系方式联系我们,我们会尽快删除相关数据。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>八、您的个人信息如何进行跨境转移</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>八、您的个人信息如何进行跨境转移</strong></span></p>
         <p>原则上,我们在中国境内收集和产生的个人信息仅存储在中国境内。如部分产品或服务涉及跨境,我们需要向境外传输您的个人信息,我们会严格按照法律法规的规定执行,并保证您的个人信息安全。</p>
         <p>原则上,我们在中国境内收集和产生的个人信息仅存储在中国境内。如部分产品或服务涉及跨境,我们需要向境外传输您的个人信息,我们会严格按照法律法规的规定执行,并保证您的个人信息安全。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>九、本《隐私保护政策》如何更新</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>九、本《隐私保护政策》如何更新</strong></span></p>
         <p>我们可能根据国家法律法规变化或服务运营需要适时修订本《隐私保护政策》内容。如该等变更会导致您在本《隐私保护政策》项下权利的实质变化,我们将在变更生效前,通过【推送站内通知、电子邮件、短信或网站公告】等方式通知您。在此期间,您应不时关注相关公告、提示信息及协议、规则等相关内容的变动。</p>
         <p>我们可能根据国家法律法规变化或服务运营需要适时修订本《隐私保护政策》内容。如该等变更会导致您在本《隐私保护政策》项下权利的实质变化,我们将在变更生效前,通过【推送站内通知、电子邮件、短信或网站公告】等方式通知您。在此期间,您应不时关注相关公告、提示信息及协议、规则等相关内容的变动。</p>
         <p>若您不同意该等变更应停止使用门墩儿直聘平台产品和服务,或通过本《隐私保护政策》公布的联系方式要求暂时封停、注销您的帐号,平台将停止收集您的相关个人信息;若您继续使用我们的产品和/或服务,即表示您同意受修订后的本《隐私保护政策》的约束。</p>
         <p>若您不同意该等变更应停止使用门墩儿直聘平台产品和服务,或通过本《隐私保护政策》公布的联系方式要求暂时封停、注销您的帐号,平台将停止收集您的相关个人信息;若您继续使用我们的产品和/或服务,即表示您同意受修订后的本《隐私保护政策》的约束。</p>
         <p><br></p>
         <p><br></p>
@@ -392,12 +392,12 @@
           </li>
           </li>
         </ol>
         </ol>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>十、如何联系我们</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>十、如何联系我们</strong></span></p>
         <p>如果您对本《隐私保护政策》及对您个人信息的处理有任何疑问、意见、建议或投诉,您可以通过产品的在线反馈功能或如下方式同我们联系:</p>
         <p>如果您对本《隐私保护政策》及对您个人信息的处理有任何疑问、意见、建议或投诉,您可以通过产品的在线反馈功能或如下方式同我们联系:</p>
         <p>电话:400-826-0101&nbsp;(工作日 9:30-18:00)</p>
         <p>电话:400-826-0101&nbsp;(工作日 9:30-18:00)</p>
         <p>邮箱:service@veryeast.com,在一般情况下,我们会在15日内对您的请求予以答复。</p>
         <p>邮箱:service@veryeast.com,在一般情况下,我们会在15日内对您的请求予以答复。</p>
         <p>&nbsp;</p>
         <p>&nbsp;</p>
-        <p><span style="color: #00897B;"><strong>十一、争议解决</strong></span></p>
+        <p><span style="color: --var(--v-primary-base);"><strong>十一、争议解决</strong></span></p>
         <ol
         <ol
           class=" list-paddingleft-2"
           class=" list-paddingleft-2"
           style="list-style-type: decimal;"
           style="list-style-type: decimal;"

+ 1 - 1
src/views/login/components/userAgreement.vue

@@ -11,7 +11,7 @@
           rel="noopener noreferrer"
           rel="noopener noreferrer"
           target="_blank"
           target="_blank"
           class="subtitle"
           class="subtitle"
-          style="cursor: pointer; text-decoration: underline; color: #00897B; line-height: 30px; font-family: 宋体, SimSun;"
+          style="cursor: pointer; text-decoration: underline; color: --var(--v-primary-base); line-height: 30px; font-family: 宋体, SimSun;"
         ></a><span class="text-font">)来获知最新版本。当您继续使用本网站及相关服务,则视为您接受协议的变更,否则您应当停止访问网站及使用服务。<br></span><a
         ></a><span class="text-font">)来获知最新版本。当您继续使用本网站及相关服务,则视为您接受协议的变更,否则您应当停止访问网站及使用服务。<br></span><a
           target="_blank"
           target="_blank"
           href="http://www.dfwsgroup.com"
           href="http://www.dfwsgroup.com"

+ 6 - 17
src/views/login/index.vue

@@ -15,7 +15,7 @@
       </div>
       </div>
       <div class="login-content-box mt-5">
       <div class="login-content-box mt-5">
         <div v-if="!isPhone" class="login-tab">
         <div v-if="!isPhone" class="login-tab">
-          <v-tabs v-model="tab" align-tabs="center" color="#00897B">
+          <v-tabs v-model="tab" align-tabs="center" color="primary">
             <v-tab :value="1">短信登录</v-tab>
             <v-tab :value="1">短信登录</v-tab>
             <v-tab :value="2">密码登录</v-tab>
             <v-tab :value="2">密码登录</v-tab>
           </v-tabs>
           </v-tabs>
@@ -44,20 +44,18 @@
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
-    <v-snackbar v-model="tips.show" :color="tips.color" :timeout="1500" location="top">{{ tips.text }}</v-snackbar>
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import { ref, reactive } from 'vue'
+import { ref } from 'vue'
 import passwordFrom from './components/passwordPage.vue'
 import passwordFrom from './components/passwordPage.vue'
 import phoneFrom from '@/components/VerificationCode'
 import phoneFrom from '@/components/VerificationCode'
 import qrCode from './components/qrCode.vue'
 import qrCode from './components/qrCode.vue'
 
 
 import { userLocaleStore } from '@/store/user'
 import { userLocaleStore } from '@/store/user'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
-// import Confirm from '@/plugins/confirm'
-// import Snackbar from '@/plugins/snackbar'
+import Snackbar from '@/plugins/snackbar'
 defineOptions({ name: 'login-index' })
 defineOptions({ name: 'login-index' })
 
 
 const router = useRouter()
 const router = useRouter()
@@ -76,11 +74,6 @@ const phoneRef = ref()
 const passRef = ref()
 const passRef = ref()
 const loginLoading = ref(false)
 const loginLoading = ref(false)
 const userStore = userLocaleStore()
 const userStore = userLocaleStore()
-const tips = reactive({
-  show: false,
-  color: '',
-  text: ''
-})
 const handleLogin = async () => {
 const handleLogin = async () => {
   const { valid } = tab.value === 1 ? await phoneRef.value.phoneForm.validate() : await passRef.value.passwordForm.validate()
   const { valid } = tab.value === 1 ? await phoneRef.value.phoneForm.validate() : await passRef.value.passwordForm.validate()
   if (!valid) return
   if (!valid) return
@@ -91,14 +84,10 @@ const handleLogin = async () => {
     } else {
     } else {
       await userStore.handlePasswordLogin(passRef.value.loginData)
       await userStore.handlePasswordLogin(passRef.value.loginData)
     }
     }
-    tips.color = 'success'
-    tips.text = '登录成功'
-    tips.show = true
+    Snackbar.success('登录成功')
     router.push({ path: '/home' })
     router.push({ path: '/home' })
-  } catch (error) {
-    console.log(error, 'error-login')
-    alert(error.msg)
-  } finally {
+  }
+  finally {
     loginLoading.value = false
     loginLoading.value = false
   }
   }
 }
 }