zhoujump 1 month ago
parent
commit
85e7945f67
4 changed files with 418 additions and 32 deletions
  1. 180 0
      src/views/components/expoHead.vue
  2. 2 2
      src/views/login/third-login.vue
  3. 12 24
      src/views/user/form.vue
  4. 224 6
      src/views/user/register.vue

+ 180 - 0
src/views/components/expoHead.vue

@@ -0,0 +1,180 @@
+<script lang="ts">
+import Vue from 'vue'
+import { getMyExpoInfo } from '@/api/expo'
+export default Vue.extend({
+  name: "expoPopover",
+  data() {
+    return {
+      ossUrl: process.env.VUE_APP_OSS_DOMAIN
+    }
+  },
+  props: {
+    popover_data: Object,
+  },
+  mounted() {
+    setTimeout(()=>{
+      console.log(this.popover_data)
+    },2000)
+  },
+  methods: {
+    goto(url) {
+      window.open(url, '_blank')
+    },
+  }
+})
+</script>
+
+<template>
+  <div class="expo-info">
+    <div class="cover loading">
+      <img v-if="popover_data.images" :src="ossUrl+popover_data.images[0]">
+      <div v-permission="'exhibitor.copyLink'" v-if="popover_data.form_template_id && popover_data.urla" @click="copyUrl()" class="button">复制表单地址</div>
+    </div>
+    <div class="info-body">
+      <div class="avatar-name">
+        <div class="avatar loading">
+          <img v-if="popover_data.logo" :src="ossUrl+popover_data.logo">
+        </div>
+        <div class="name-cont">
+          <div :class="['name',popover_data.expo_name!==undefined?'':'loading']">{{ popover_data.expo_name }}</div>
+          <div :class="['sub-name',popover_data.organizer!==undefined?'':'loading']">{{ popover_data.organizer }}</div>
+          <div class="contact">
+            <div class="phone">
+              <i class="icon el-icon-mobile-phone" />
+              <div :class="['phone-num link',popover_data.contact_phone!==undefined?'':'loading']" @click="goto('tel:'+popover_data.contact_phone)">{{ popover_data.contact_phone }}</div>
+            </div>
+            <div class="email">
+              <i class="icon el-icon-message" />
+              <div :class="['email-addr link',popover_data.contact_email!==undefined?'':'loading']" @click="goto('mailto:'+popover_data.contact_email)">{{ popover_data.contact_email }}</div>
+            </div>
+            <div class="date">
+              <i class="icon el-icon-date" />
+              <div :class="['date-range',popover_data.start_date!==undefined?'':'loading']">{{ popover_data.start_date }} - {{ popover_data.end_date }}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div :class="['desc',popover_data.content!==undefined?'':'loading']">
+        {{ popover_data.content }}
+      </div>
+      <div v-if="popover_data.social_links" class="social_links">
+        <div class="social-title">社交媒体</div>
+        <div v-if="popover_data.social_links.facebook" class="social-item">
+          <span>facebook:</span><span class="link" @click="goto(popover_data.social_links.facebook)">{{ popover_data.social_links.facebook }}</span>
+        </div>
+        <div v-if="popover_data.social_links.twitter" class="social-item">
+          <span>twitter:</span><span class="link" @click="goto(popover_data.social_links.twitter)">{{ popover_data.social_links.twitter }}</span>
+        </div>
+        <div v-if="popover_data.social_links.linkedin" class="social-item">
+          <span>linkedin:</span><span class="link" @click="goto(popover_data.social_links.linkedin)">{{ popover_data.social_links.linkedin }}</span>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.expo-info{
+  position: relative;
+  width: 100%;
+  .link{
+    cursor: pointer;
+    text-decoration: underline;
+  }
+  .info-body{
+    margin: auto;
+    max-width: 800px;
+    padding: 16px;
+    .social_links{
+      margin-top: 12px;
+      .social-title{
+        font-weight: bold;
+      }
+      .social-item{
+
+      }
+    }
+    .desc{
+      margin-top: 12px;
+      &.loading{
+        height: 60px;
+        width: 100%;
+      }
+    }
+    .avatar-name{
+      display: flex;
+      grid-gap: 8px;
+      .name-cont{
+        flex: 1;
+        .name{
+          font-size: 20px;
+          font-weight: bold;
+          &.loading{
+            height: 26px;
+            width: 120px;
+          }
+        }
+        .sub-name{
+          color: gray;
+          &.loading{
+            margin-top: 8px;
+            margin-bottom: 8px;
+            height: 20px;
+            width: 160px;
+          }
+        }
+        .contact{
+          flex-wrap: wrap;
+          display: flex;
+          grid-gap: 0 12px;
+          .phone,.email,.date{
+            display: flex;
+            align-items: center;
+            grid-gap: 4px;
+            div{
+              &.loading{
+                height: 16px;
+                width: 120px;
+              }
+            }
+          }
+        }
+      }
+      .avatar{
+        width: 70px;
+        height: 70px;
+        border-radius: 50%;
+        overflow: hidden;
+        img{
+          display: block;
+          object-fit: cover;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+  }
+  .cover{
+    position: relative;
+    width: 100%;
+    height: 300px;
+    .button{
+      cursor: pointer;
+      background: #00000044;
+      padding: 4px 12px;
+      border-radius: 24px;
+      border: 2px solid white;
+      color: white;
+      position: absolute;
+      right: 16px;
+      top: 16px;
+    }
+    img{
+      display: block;
+      object-fit: cover;
+      width: 100%;
+      height: 100%;
+    }
+  }
+}
+</style>

+ 2 - 2
src/views/login/third-login.vue

@@ -130,7 +130,7 @@ export default Vue.extend({
       getGoogleLogin().then(res => {
         const google_info = res.data
         const url = new URL(google_info.url)
-        url.searchParams.set('redirect_uri', location.origin + '/login')
+        url.searchParams.set('redirect_uri', location.href)
         location.href = url
       })
     },
@@ -138,7 +138,7 @@ export default Vue.extend({
       getLinkedinLogin().then(res => {
         const linkedin_info = res.data
         const url = new URL(linkedin_info.url)
-        url.searchParams.set('redirect_uri', location.origin + '/login')
+        url.searchParams.set('redirect_uri', location.href)
         location.href = url
       })
     },

+ 12 - 24
src/views/user/form.vue

@@ -1,6 +1,7 @@
 <script lang="ts">
 import Vue from 'vue'
 import { getFormInfo, submitForm, getLocationList, getJobList } from '@/api/form'
+import expoHead from '../components/expoHead.vue'
 import { getExpoInfo } from '@/api/expo'
 import countryCode from '@/lib/countryCode.json'
 import login from '@/views/login/index.vue'
@@ -26,6 +27,9 @@ export default Vue.extend({
       isOk: false
     }
   },
+  components: {
+    expoHead
+  },
   computed: {
     user() { return this.$store.state.user.user },
     token() { return this.$store.state.user.token }
@@ -43,6 +47,8 @@ export default Vue.extend({
       } else {
         getExpoInfo(this.expo_key).then(res => {
           this.from_data = res.data
+          this.from_data.images = JSON.parse(res.data.images)
+          this.from_data.social_links = JSON.parse(res.data.social_links)
           this.from_data.form_fields.forEach(item => {
             if (item.field_type === 'checkbox') {
               this.$set(item, 'value', [])
@@ -194,15 +200,8 @@ export default Vue.extend({
     <div class="info">此链接无效,请向管理员咨询。</div>
   </div>
   <div v-else class="body">
-    <div class="head">
-      <div class="form-name">
-        {{ from_data.expo_name }}
-      </div>
-    </div>
+    <expo-head :popover_data="from_data"></expo-head>
     <div class="form">
-      <div class="desc">
-        {{ from_data.content }}
-      </div>
       <div v-if="isOk" class="form-body ok">
         <div class="icon el-icon-success" />
         <div class="text">表单提交完成</div>
@@ -388,15 +387,14 @@ export default Vue.extend({
   }
 }
 .body{
-  margin: 36px auto;
-  max-width: 800px;
   width: 100%;
   .foot{
-    padding: 0 12px;
+    margin: 16px auto 0;
+    max-width: 800px;
+    padding: 0 12px 36px;
     display: flex;
     justify-content: space-between;
     align-items: center;
-    margin-top: 16px;
     .power-by,a{
       font-size: 16px;
       color: grey;
@@ -437,8 +435,9 @@ export default Vue.extend({
     }
   }
   .form{
+    max-width: 800px;
     border-radius: 8px;
-    margin-top: 16px;
+    margin: 16px auto 0;
     padding: 24px;
     box-shadow: 0 0 8px 0 #00000008;
     background: white;
@@ -499,17 +498,6 @@ export default Vue.extend({
       }
     }
   }
-  .head{
-    padding: 0 8px;
-    display: flex;
-    gap: 16px;
-    align-items: flex-end;
-    .form-name{
-      margin-bottom: 8px;
-      font-size: 24px;
-      font-weight: 600;
-    }
-  }
 }
 </style>
 <style>

+ 224 - 6
src/views/user/register.vue

@@ -1,10 +1,14 @@
 <script lang="ts">
 import Vue from 'vue'
 import countryCode from '@/lib/countryCode.json'
-import { sendSmsCode, sentEmailCode, register, confirmEmail } from '@/api/user'
+import { sendSmsCode, sentEmailCode, register, confirmEmail, getGoogleStatus, getLinkedinStatus } from '@/api/user'
 import { getExpoInfo } from '@/api/expo'
+import thirdLogin from '@/views/login/third-login.vue'
 export default Vue.extend({
   name: 'Index',
+  components: {
+    thirdLogin
+  },
   data() {
     return {
       regWay: 0, // 0:手机注册 1:邮箱注册
@@ -28,7 +32,12 @@ export default Vue.extend({
       expo_key: '',
       subTimer: '',
       subTime: '',
-      isLogin: false
+      isLogin: false,
+
+      third_login: '',
+      third_token: '',
+      need_bind: false,
+      is_back: false
     }
   },
   computed: {
@@ -62,6 +71,88 @@ export default Vue.extend({
         })
       }
     },
+    tokenLogin(token) {
+      this.$store.dispatch('tokenLogin', { token: token }).then(res => {
+        this.gotoForm()
+      }).catch(err => {
+        this.$notify({
+          title: '提示',
+          message: '使用本地令牌登录失败'+err,
+          type: 'error'
+        })
+        this.$message.error(err.message)
+      })
+    },
+    openThirdLogin(type) {
+      this.is_back = false
+      this.third_login = type
+    },
+    closeThirdLogin() {
+      this.is_back = false
+      this.need_bind = false
+      this.third_login = ''
+      this.third_token = ''
+    },
+    parseQuery() {
+      const url = new URL(window.location.href.replace('#', '?'))
+      function showError() {
+        this.$notify({
+          title: '登录失败',
+          message: '登录失败,请稍后重试。',
+          type: 'error'
+        })
+        this.third_login = ''
+        this.is_back = false
+      }
+      if (url.searchParams.get('code')) {
+        this.is_back = true
+        this.third_login = 'linkin'
+        getLinkedinStatus(
+          url.searchParams.get('state'),
+          url.searchParams.get('code')
+        ).then(res => {
+          if (res.data.api_token) {
+            this.tokenLogin(res.data.api_token)
+          } else if (res.data.third_token) {
+            this.third_token = res.data.third_token
+            this.need_bind = true
+          } else {
+            showError()
+          }
+        }).catch(err => {
+          this.$notify({
+            title: '提示',
+            message: '领英授权登录失败'+err,
+            type: 'error'
+          })
+          showError()
+        })
+      }
+      if (url.searchParams.get('access_token')) {
+        this.is_back = true
+        this.third_login = 'google'
+        getGoogleStatus(
+          url.searchParams.get('state'),
+          url.searchParams.get('access_token')
+        ).then(res => {
+          if (res.data.api_token) {
+            this.tokenLogin(res.data.api_token)
+          } else if (res.data.third_token) {
+            this.third_token = res.data.third_token
+            this.need_bind = true
+          } else {
+            showError()
+          }
+        }).catch(err => {
+          this.$notify({
+            title: '提示',
+            message: '谷歌授权登录失败'+err,
+            type: 'error'
+          })
+          showError()
+        })
+      }
+    },
     sentSms() {
       if (this.loginInfo.phone === '') {
         this.$message.error('请填写电话号码!')
@@ -292,6 +383,20 @@ export default Vue.extend({
       </div>
       <el-button type="primary" @click="submitLogin()">登录</el-button>
       <el-button type="text" @click="isLogin=false">没有账号?去注册。</el-button>
+      <div class="third-login">
+        <div @click="openThirdLogin('wechat')" class="button wechat">
+          <img class="icon" src="/static/image/icon/wechat.png">
+          微信扫码登录
+        </div>
+        <div @click="openThirdLogin('google')" class="button google">
+          <img class="icon" src="/static/image/icon/google.png">
+          谷歌授权登录
+        </div>
+        <div @click="openThirdLogin('linkin')" class="button linkin">
+          <img class="icon" src="/static/image/icon/linkin.png">
+          领英授权登录
+        </div>
+      </div>
     </div>
     <div v-else class="register-body">
       <div class="tab">
@@ -340,6 +445,20 @@ export default Vue.extend({
       </div>
       <el-button type="primary" @click="register()">注册</el-button>
       <el-button type="text" @click="isLogin=true">已有账号?去登陆。</el-button>
+      <div class="third-login">
+        <div @click="openThirdLogin('wechat')" class="button wechat">
+          <img class="icon" src="/static/image/icon/wechat.png">
+          微信扫码登录
+        </div>
+        <div @click="openThirdLogin('google')" class="button google">
+          <img class="icon" src="/static/image/icon/google.png">
+          谷歌授权登录
+        </div>
+        <div @click="openThirdLogin('linkin')" class="button linkin">
+          <img class="icon" src="/static/image/icon/linkin.png">
+          领英授权登录
+        </div>
+      </div>
     </div>
     <el-dialog
       title="设置初始密码"
@@ -360,22 +479,118 @@ export default Vue.extend({
         <el-button type="primary" @click="checkPassword()">确 定</el-button>
       </span>
     </el-dialog>
+    <div :class="['mask',third_login?'show':'']">
+      <div class="third-body">
+        <span class="el-icon-close" @click="closeThirdLogin()" />
+        <third-login
+          v-if="third_login"
+          :is_back="is_back"
+          :need_bind.sync="need_bind"
+          :third_token.sync="third_token"
+          :login_type="third_login"
+          @tokenLogin="tokenLogin"
+        />
+      </div>
+    </div>
   </div>
 </template>
 <style scoped lang="scss">
 .body{
-  margin: 36px auto;
-  max-width: 800px;
   width: 100%;
+  height: 100%;
+  overflow: hidden;
+  overflow-y: auto;
+  width: 100%;
+  .mask{
+    transition-duration: 300ms;
+    width: 100%;
+    height: 100%;
+    background-color: #00000044;
+    left: 0;
+    top: 0;
+    position: fixed;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    pointer-events: none;
+    opacity: 0;
+    .third-body{
+      position: relative;
+      transition-duration: 300ms;
+      background-color: white;
+      transform: translateY(40px);
+      width: 460px;
+      height: 600px;
+      .el-icon-close{
+        position: absolute;
+        right: 8px;
+        top: 8px;
+        border-radius: 4px;
+        padding: 8px;
+        transition-duration: 300ms;
+        cursor: pointer;
+        &:hover{
+          background-color: #00000011;
+        }
+      }
+    }
+    &.show{
+      pointer-events: all;
+      opacity: 1;
+      .third-body{
+        transform: translateY(0);
+      }
+    }
+  }
   .register-body{
+    margin: 48px auto 64px;
+    max-width: 800px;
     display: flex;
     flex-direction: column;
     gap: 16px;
-    margin-top: 32px;
     padding: 40px 50px;
     border-radius: 4px;
     box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);
     background: white;
+    .third-login{
+      display: grid;
+      grid-gap: 16px;
+      grid-template-columns: 1fr 1fr 1fr;
+      .button{
+        transition-duration: 300ms;
+        cursor: pointer;
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        justify-content: center;
+        padding: 8px 16px;
+        font-size: 16px;
+        color: black;
+        background-color: #FFFFFF;
+        .icon{
+          transition-duration: 300ms;
+          width: 20px;
+          height: 20px;
+          object-fit: contain;
+          filter: brightness(1);
+        }
+        &:hover{
+          color: white;
+          .icon{
+            filter: brightness(100);
+          }
+          &.wechat{
+            background: #0E932E;
+          }
+          &.google{
+            background: #EA4335;
+          }
+          &.linkin{
+            background: #0B66C1;
+          }
+        }
+      }
+    }
     .piracy{
       color: #6B7280;
       display: flex;
@@ -415,7 +630,8 @@ export default Vue.extend({
     }
   }
   .time-counter{
-    margin-top: 32px;
+    max-width: 800px;
+    margin: 32px auto 0;
     background: #4DA9FF22;
     padding: 24px;
     border-radius: 8px;
@@ -445,6 +661,8 @@ export default Vue.extend({
     }
   }
   .head{
+    margin: 48px auto 0;
+    max-width: 800px;
     .title{
       text-align: center;
       font-size: 24px;