Funny 3 年 前
コミット
bc67494b8a

+ 5 - 0
package-lock.json

@@ -1791,6 +1791,11 @@
         "yallist": "^4.0.0"
       }
     },
+    "mitt": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
+      "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ=="
+    },
     "mkdirp": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "ant-design-vue": "^3.2.2",
     "axios": "^0.27.2",
     "js-md5": "^0.7.3",
+    "mitt": "^3.0.0",
     "node-sass": "^7.0.1",
     "sass": "^1.49.9",
     "sass-loader": "^7.3.1",

+ 66 - 12
src/App.vue

@@ -1,14 +1,25 @@
 <script setup lang="ts">
-import { ref, watch } from 'vue'
+import { nextTick, onMounted, provide, ref, watch } from 'vue'
 import { useRouter } from 'vue-router';
+import emitter from '@/util/eventBus'
 const router = useRouter()
+let nickName = ref<string>('')
+let avatar = ref<string>('')
 let isTop = ref(true)
 let activeIndex = ref(0)
 let menuList = ref(['首页', '优巨引擎官网', '文档中心', '管理中心'])
 const container = ref()
 let currentRouteName = ref()
+let visible = ref<boolean>(false)
+let isRouterAlive = ref(true);
+const reload = () => {
+  isRouterAlive.value = false;
+  nextTick(() => {
+    isRouterAlive.value = true
+  })
+}
+provide("reload", reload);
 watch(() => router.currentRoute.value.name, (newValue, oldValue) => {
-  console.log(typeof newValue);
   currentRouteName.value = newValue
   if (newValue !== 'home') {
     if (newValue === 'login') {
@@ -18,13 +29,34 @@ watch(() => router.currentRoute.value.name, (newValue, oldValue) => {
     }
     window.removeEventListener('scroll', handleScrollY, true)
   } else {
+    isTop.value = true
     window.addEventListener('scroll', handleScrollY, true)
   }
 
 })
+onMounted(() => {
+  let userInfo = localStorage.getItem('userInfo')
+  if (userInfo) {
+    let info = JSON.parse(userInfo)
+    nickName.value = info.nickname
+    avatar.value = info.avatar
+  }
+  emitter.on('userInfo', (e: any) => {
+    let userInfo = JSON.parse(e)
+    console.log('userInfo:', userInfo);
+    nickName.value = userInfo.nickname
+    avatar.value = userInfo.avatar
+  })
+})
 function handleScrollY() {
   isTop.value = !container.value.getBoundingClientRect().top
 }
+const loginOut = () => {
+  localStorage.clear()
+  visible.value = false
+  nickName.value = ''
+  avatar.value = ''
+}
 </script>
 
 <template>
@@ -42,20 +74,41 @@ function handleScrollY() {
           <div @click="activeIndex = i" class="btn" v-for="(v, i) in menuList" :key="i"
             :class="activeIndex === i && !isTop ? 'active-btn' : ''">
             {{ v }}</div>
-          <div v-if="currentRouteName === 'login'" class="login" @click="router.push('/register')">注册</div>
-          <div v-else class="login" @click="router.push('/login')">登录</div>
+          <template v-if="nickName">
+            <a-tooltip placement="bottom" color="white">
+              <template #title>
+                <div class="flex items-center px-10px py-20px cursor-pointer hover:bg-gray-300">
+                  <img class="w-18px h-18px mr-10px" src="@/assets/images/18-1.png" alt="">
+                  <div class="text-14px font-500 text-dark-400">通知</div>
+                </div>
+                <div style="height: 2px; background-color: #F0F0F0"></div>
+                <div @click="visible = true" class="flex items-center px-10px py-20px cursor-pointer hover:bg-gray-300">
+                  <img class="w-18px h-18px mr-10px" src="@/assets/images/18-2.png" alt="">
+                  <div class="text-14px font-500 text-dark-400">账号退出</div>
+                </div>
+              </template>
+              <div class="flex items-center cursor-pointer">
+                <img class="w-30px h-30px rounded-1/2 mr-10px" :src="avatar" alt="">
+                <div class="text-18">{{ nickName }}</div>
+              </div>
+            </a-tooltip>
+          </template>
+          <template v-else>
+            <div v-if="currentRouteName === 'login'" class="login" @click="router.push('/register')">注册</div>
+            <div v-else class="login" @click="router.push('/login')">登录</div>
+          </template>
         </div>
       </div>
     </a-layout-header>
     <!-- 内容 -->
-    <a-layout-content class="">
+    <a-layout-content>
       <div ref="container" class="lb-container">
-        <router-view></router-view>
+        <router-view v-if="isRouterAlive"></router-view>
       </div>
     </a-layout-content>
     <!-- 底部 -->
     <a-layout-footer class="footer">
-      <div class="name">优巨引擎</div>
+      <!-- <div class="name">优巨引擎</div> -->
       <div class="agreement">
         <div>开发者服务协议</div>
         <div>&ensp;|&ensp;</div>
@@ -64,7 +117,7 @@ function handleScrollY() {
         <div>联系我们</div>
       </div>
       <div class="record-number">
-        备案号:苏ICP备2021010118号
+        优巨引擎&ensp;&ensp;&ensp;备案号:苏ICP备2021010118号
       </div>
     </a-layout-footer>
   </a-layout>
@@ -96,6 +149,9 @@ function handleScrollY() {
       </div>
     </a-tooltip>
   </div>
+  <a-modal centered v-model:visible="visible" ok-text="确认" cancel-text="取消" title="提示" @ok="loginOut">
+    <p>此操作将退出当前登录用户, 是否继续?</p>
+  </a-modal>
 </template>
 
 <style lang="scss" scoped>
@@ -124,8 +180,6 @@ function handleScrollY() {
     width: 100%;
     min-width: 600px;
 
-
-
     .left {
       display: flex;
       align-items: center;
@@ -213,12 +267,12 @@ function handleScrollY() {
 }
 
 .lb-container {
-  min-height: calc(100vh - 110px);
+  min-height: calc(100vh - 80px);
   background: white;
 }
 
 .footer {
-  height: 110px;
+  height: 80px;
   padding: 10px 0;
   display: flex;
   flex-direction: column;

+ 18 - 1
src/api/index.ts

@@ -4,11 +4,28 @@ import { get, post, postJson } from '@/util/http';
 export const sendSms = (params: any) => {
   return post('app/send/sms', params)
 }
+//获取验证码
+export const loginSendSms = (params: any) => {
+  return post('app/login/send/sms', params)
+}
 // 校验验证码
 export const verification = (params: any) => {
   return post('app/verification', params)
 }
+// 验证码登录
+export const loginVerification = (params: any) => {
+  return post('app/login/verification', params)
+}
+// 密码登录
+export const loginPassword = (params: any) => {
+  return post('app/login/password', params)
+}
 // 申请成为开发者
 export const applyDevelop = (params: any) => {
   return postJson('app/applyDevelop', params)
-}
+}
+// 获取用户信息
+export const userInfo = () => {
+  return get('app/member/detail')
+}
+

BIN
src/assets/images/11-1.png


BIN
src/assets/images/11-2.png


BIN
src/assets/images/11-3.png


BIN
src/assets/images/11-4.png


BIN
src/assets/images/17-1.png


BIN
src/assets/images/17-2.png


BIN
src/assets/images/17-3.png


BIN
src/assets/images/17-4.png


BIN
src/assets/images/18-1.png


BIN
src/assets/images/18-2.png


BIN
src/assets/images/4-1.png


+ 0 - 1
src/main.ts

@@ -6,5 +6,4 @@ import router from './router'
 import store from './store'
 import 'virtual:windi.css'
 const app = createApp(App);
-
 app.use(Antd).use(router).use(store).mount('#app')

+ 3 - 0
src/util/eventBus.ts

@@ -0,0 +1,3 @@
+import mitt from "mitt";
+const emitter = mitt();
+export default emitter;

+ 3 - 2
src/views/home/index.vue

@@ -100,7 +100,7 @@ const toRegister = () => {
       <div class="flex justify-center items-center                                     mt-50px">
         <div class="flex items-center" v-for="(v, i) in steps" :key="i">
           <div class="flex flex-shrink-0 flex-col items-center w-200px h-207px !bg-cover"
-            :style="`background: url(${getAssetsFile(`11-2.png`)}) no-repeat`">
+            :style="`background: url(${getAssetsFile(`11-${i + 1}.png`)}) no-repeat`">
             <div class="text-20px text-white mt-25px">{{ i + 1 }}</div>
             <div class="text-16px font-500 text-dark-100 mt-90px">{{ v.tip }}</div>
           </div>
@@ -122,7 +122,7 @@ const toRegister = () => {
       </div>
     </div>
     <!-- 合作伙伴 -->
-    <div class="pt-70px partner" >
+    <div class="pt-70px partner">
       <div class="flex flex-col justify-center">
         <div class="flex justify-center items-center">
           <img class="w-93px h-21px" src="@/assets/images/10 (1).png" alt="" srcset="">
@@ -156,6 +156,7 @@ const toRegister = () => {
     background: url(@/assets/images/13.png) no-repeat;
     background-position-x: center;
   }
+
   .partner {
     background: #f4f4f4;
   }

+ 159 - 5
src/views/login/index.vue

@@ -4,19 +4,173 @@ export default defineComponent({
 })
 </script>
 <script setup lang='ts'>
-import { defineComponent, onMounted, ref } from 'vue'
-
+import { loginSendSms, loginVerification, loginPassword, userInfo } from '@/api';
+import router from '@/router';
+import util from '@/util';
+import { message } from 'ant-design-vue';
+import { defineComponent, onMounted, reactive, ref, UnwrapRef } from 'vue'
+import emitter from '@/util/eventBus'
+let tabs = ref(['密码登录', '短信码登录'])
+let verifyText = ref<string>('获取验证码')
+let currentIdx = ref(0)
+let time = ref<number>(60)
+let timer = ref<number>()
+let canSendSms = ref<boolean>(true)
+interface FormData {
+  mobile: string,
+  password: string
+  code?: number
+}
+let formData: UnwrapRef<FormData> = reactive({
+  mobile: '',
+  password: ''
+})
 onMounted(() => {
 
 })
+const doSendSms = () => {
+  if (!util.mobile(formData.mobile)) {
+    return message.error('请填写正确格式的手机号!')
+  }
+  loginSendSms({ mobile: formData.mobile, type: 1 }).then((res: any) => {
+    console.log(res);
+    if (res.code === 200) {
+      message.success('发送成功,请查收短信验证码!')
+      timer.value = setInterval(() => {
+        canSendSms.value = false
+        time.value--
+        verifyText.value = `重新获取 (${time.value})`
+        if (!time.value) {
+          verifyText.value = `重新获取`
+          canSendSms.value = true
+          clearInterval(timer.value)
+          time.value = 60
+        }
+      }, 1000)
+    } else {
+      message.error(res.msg)
+    }
+  })
+}
+const getUserInfo = () => {
+  userInfo().then((res: any) => {
+    if (res.code === 200) {
+      localStorage.setItem("userInfo", JSON.stringify(res.data))
+      emitter.emit('userInfo', JSON.stringify(res.data))
+      router.push("/");
+    }
+  })
+}
+const loginPw = () => {
+  loginPassword({ mobile: formData.mobile, password: formData.password, platform: 4 }).then((res: any) => {
+    console.log(res);
+    if (res.code == 200) {
+      localStorage.setItem("token", res.data.token);
+      getUserInfo()
+    } else {
+      return message.error(res.msg);
+    }
+  })
+}
+const loginCode = () => {
+  loginVerification({ mobile: formData.mobile, code: formData.code, platform: 4 }).then((res: any) => {
+    if (res.code == 200) {
+      localStorage.setItem("token", res.data.token);
+      getUserInfo()
+    } else {
+      return message.error(res.msg);
+    }
+  })
+}
 </script>
 <template>
-  <div class="login-bg !bg-center"></div>
+  <div class="flex items-center justify-center login-bg !bg-center">
+    <div class="relative login-item !bg-center mx-200px mt-60px w-full rounded-10px bg-white">
+      <div class="absolute top-60px right-190px flex flex-col items-center">
+        <div class="text-32px font-500 text-dark-600 leading-38px">优巨引擎开发平台</div>
+        <div class="w-400px mt-28px">
+          <div class="flex w-full text-18px font-500">
+            <div class="w-1/2 cursor-pointer" v-for="(v, i) in tabs" :key="i" @click="currentIdx = i">
+              <div class="text-center leading-47px" :class="currentIdx === i ? 'text-blue-400' : 'text-blue-200'">{{ v
+              }}
+              </div>
+              <div class="h-2px" :class="currentIdx === i ? 'bg-blue-400' : 'bg-blue-200'"></div>
+            </div>
+          </div>
+          <div class="flex flex-col items-center w-full mt-30px">
+            <a-form :model="formData" class="w-400px" v-if="!currentIdx">
+              <a-form-item>
+                <a-input v-model:value="formData.mobile" class="h-50px text-16px" :maxlength="11" allowClear
+                  placeholder="请输入手机号">
+                  <template #prefix>
+                    <img class="w-28px h-28px" src="@/assets/images/17-1.png" alt="">
+                  </template>
+                </a-input>
+              </a-form-item>
+              <a-form-item>
+                <a-input-password v-model:value="formData.password" autocomplete class="h-50px" allowClear
+                  placeholder="请输入登录密码">
+                  <template #prefix>
+                    <img class="w-28px h-28px" src="@/assets/images/17-2.png" alt="">
+                  </template>
+                </a-input-password>
+              </a-form-item>
+              <a-form-item>
+                <div class="text-gray-400 cursor-pointer text-right">忘记密码</div>
+              </a-form-item>
+              <a-form-item>
+                <a-button @click="loginPw" :disabled="!formData.mobile || !formData.password" class="w-full h-50px"
+                  type="primary">登 录
+                </a-button>
+              </a-form-item>
+            </a-form>
+            <a-form :model="formData" class="w-400px" v-else>
+              <a-form-item>
+                <a-input v-model:value="formData.mobile" class="h-50px text-16px" :maxlength="11" allowClear
+                  placeholder="请输入手机号">
+                  <template #prefix>
+                    <img class="w-28px h-28px" src="@/assets/images/17-1.png" alt="">
+                  </template>
+                </a-input>
+              </a-form-item>
+              <a-form-item>
+                <a-input-group compact>
+                  <a-input v-model:value="formData.code" allowClear placeholder="请输入验证码" class="h-50px"
+                    style="width: calc(100% - 130px)">
+                    <template #prefix>
+                      <img class="w-28px h-28px" src="@/assets/images/17-4.png" alt="">
+                    </template>
+                  </a-input>
+                  <a-button @click="doSendSms" :disabled="!canSendSms" class="w-110px h-50px ml-20px" type="primary">{{
+                      verifyText
+                  }}
+                  </a-button>
+                </a-input-group>
+              </a-form-item>
+              <a-form-item>
+                <a-button @click="loginCode" :disabled="!formData.mobile || !formData.code" class="w-full h-50px"
+                  type="primary">登 录
+                </a-button>
+              </a-form-item>
+            </a-form>
+            <div class="flex text-16px text-dark-500">
+              <div>还没有账号,去</div>
+              <div class="text-blue-500 cursor-pointer">注册</div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
 </template>
 <style lang='scss' scoped>
 .login-bg {
-  width: 100vw;
-  height: calc(100vh - 110px);
+  height: calc(100vh - 80px);
   background: url(@/assets/images/4.png) no-repeat;
+
+  .login-item {
+    background-image: url(@/assets/images/4-1.png);
+    height: calc(100vh - 280px);
+  }
 }
 </style>

+ 1 - 1
src/views/register/index.vue

@@ -29,7 +29,7 @@ let firstDisabled = ref<Boolean>(true)
 let submitDisabled = ref<Boolean>(true)
 let loading = ref<boolean>(false);
 let fileList = ref([]);
-let time = ref<number>(10)
+let time = ref<number>(60)
 let timer = ref<number>()
 let canSendSms = ref<boolean>(true)
 let labelCol = ref<object>({ style: { width: '200px' } })