From 54ef52797ea21cc61a372b14e4b1912ae8a87e43 Mon Sep 17 00:00:00 2001
From: yujialong <479214531@qq.com>
Date: Tue, 3 Dec 2024 17:49:22 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AD=A6=E7=94=9F=E5=BE=AE=E4=BF=A1=E7=99=BB?=
 =?UTF-8?q?=E5=BD=95=E7=AD=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api/index.js                     |   1 +
 src/assets/img/exts/excel.png        | Bin 0 -> 432 bytes
 src/assets/img/wechat.svg            |   1 +
 src/pages/account/login/index.vue    |  33 ++-
 src/pages/account/redirect/index.vue | 289 +++++++++++++++++++++++++++
 src/pages/station/preview/index.vue  |   2 +
 src/router/routes.js                 |   8 +
 src/setting.js                       |   2 +-
 8 files changed, 334 insertions(+), 2 deletions(-)
 create mode 100644 src/assets/img/exts/excel.png
 create mode 100644 src/assets/img/wechat.svg
 create mode 100644 src/pages/account/redirect/index.vue

diff --git a/src/api/index.js b/src/api/index.js
index 30efd85..0f5ff99 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -33,6 +33,7 @@ export default {
   updateRealSchoolId: `competition/competition/registration/updateRealSchoolId`,
   checkUserNameOrWorkNumber: `users/users/batchProcessing/checkUserNameOrWorkNumber`,
   updateUserNameOrWorkNumber: `users/users/batchProcessing/updateUserNameOrWorkNumber`,
+  loginByUnionid: `users/loginByUnionid`,
 
   // 阿里云文件/视频管理
   getPlayAuth: `nakadai/nakadai/oss/getPlayAuth`, // 获取播放凭证
diff --git a/src/assets/img/exts/excel.png b/src/assets/img/exts/excel.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e0482e63d8a90242328abfe200e5853b6e3a72b
GIT binary patch
literal 432
zcmV;h0Z;ykP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B00001b5ch_0Itp)
z=>Px$YDq*vR5(v#WPpLaWwRI<7*gSUEDTW<`8TiSvqiC~VFU~AE$e4sU?{+62uMy?
zNrr)ek@4wUg<KhQwJ?L;GB7X*5n~V=Hy6XNZ{INtgBf(6fq_ARkU>HU(hTgp++bzs
zhLL0t8xI$Qup+`&$cB+*5XdELTpSEsLIQBdGqEr~e5ahRK(awty#Ii~xl3^f7-Y#W
z&#+J{jN!_c7YrenmoRt=t25-w+Az3Xn8WbYx*F`W!rPk}R=v6e^Cfn#feh+Y_GDP}
z;vB=AXQvnr8)q`CeRY{Zl$D#|-JkCa$38v;8*m&-GcYjVHmFkGk-_iM0tOytc7~k>
z$qaYDzhyZ8`6+`Ohakg~pC1|iGyG@hdvutvi@H?27+(MS$`F2KDT6+*BtwFP2}9?@
z{R}5ea=@CUkG3L5-XM=PqB1Rxy!ej6xl5R&Vg!dl6BwMk3LyqT$|QmX8y16RGdOpp
afy4pgN|h7m@wLPN0000<MNUMnLSTXj8MDX$

literal 0
HcmV?d00001

diff --git a/src/assets/img/wechat.svg b/src/assets/img/wechat.svg
new file mode 100644
index 0000000..583d4f8
--- /dev/null
+++ b/src/assets/img/wechat.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733123764466" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5187" width="20" height="20" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M683.058 364.695c11 0 22 1.016 32.943 1.976C686.564 230.064 538.896 128 370.681 128c-188.104 0.66-342.237 127.793-342.237 289.226 0 93.068 51.379 169.827 136.725 229.256L130.72 748.43l119.796-59.368c42.918 8.395 77.37 16.79 119.742 16.79 11 0 21.46-0.48 31.914-1.442a259.168 259.168 0 0 1-10.455-71.358c0.485-148.002 128.744-268.297 291.403-268.297l-0.06-0.06z m-184.113-91.992c25.99 0 42.913 16.79 42.913 42.575 0 25.188-16.923 42.579-42.913 42.579-25.45 0-51.38-16.85-51.38-42.58 0-25.784 25.93-42.574 51.38-42.574z m-239.544 85.154c-25.384 0-51.374-16.85-51.374-42.58 0-25.784 25.99-42.574 51.374-42.574 25.45 0 42.918 16.79 42.918 42.575 0 25.188-16.924 42.579-42.918 42.579z m736.155 271.655c0-135.647-136.725-246.527-290.983-246.527-162.655 0-290.918 110.88-290.918 246.527 0 136.128 128.263 246.587 290.918 246.587 33.972 0 68.423-8.395 102.818-16.85l93.809 50.973-25.93-84.677c68.907-51.93 120.286-119.815 120.286-196.033z m-385.275-42.58c-16.923 0-34.452-16.79-34.452-34.179 0-16.79 17.529-34.18 34.452-34.18 25.99 0 42.918 16.85 42.918 34.18 0 17.39-16.928 34.18-42.918 34.18z m188.165 0c-16.984 0-33.972-16.79-33.972-34.179 0-16.79 16.927-34.18 33.972-34.18 25.93 0 42.913 16.85 42.913 34.18 0 17.39-16.983 34.18-42.913 34.18z" fill="#09BB07" p-id="5188"></path></svg>
\ No newline at end of file
diff --git a/src/pages/account/login/index.vue b/src/pages/account/login/index.vue
index b767773..e53364a 100644
--- a/src/pages/account/login/index.vue
+++ b/src/pages/account/login/index.vue
@@ -67,6 +67,11 @@
             <!-- 未登录的赛事点击报名进入后的注册 -->
             <el-link v-else-if="toMatch" :underline="false" type="primary"
               @click="accountApplyVisible = true">暂无账号?点击申请</el-link>
+
+            <div class="wechat" @click="toWechat">
+              <img class="icon" src="@/assets/img/wechat.svg" alt="">
+              <span>学生微信登录</span>
+            </div>
           </div>
           <el-button class="submit" type="primary" @click="submit">登录</el-button>
         </el-form>
@@ -298,6 +303,8 @@ export default {
         rePassword: [{ required: true, message: "请再次输入密码", trigger: "blur" }],
         code: [{ required: true, message: "请输入验证码", trigger: "blur" }]
       },
+
+      qrcode: '',
     };
   },
   computed: {
@@ -311,6 +318,7 @@ export default {
   mounted () {
     localStorage.removeItem('opened')
     this.getVerImg()
+    this.getQr()
     // 页面离开的时候销毁手机和邮箱验证码定时器
     this.$once("hook:beforeDestroy", function () {
       clearInterval(this.phoneTimer)
@@ -330,6 +338,11 @@ export default {
       this.form.random = Math.floor(Math.random() * 999999999);
       this.verificationIMG = this.api.verification + "?random=" + `${this.form.random}`;
     },
+    // 获取微信扫码二维码
+    async getQr () {
+      const res = await this.$get(`https://edu.occupationlab.com/users/getQRCodeUrl`)
+      this.qrcode = res.message
+    },
     // 重置验证规则
     handleRule () {
       this.rules.account[0].message = this.verCodeLogin ?
@@ -559,6 +572,11 @@ export default {
       Util.successMsg(message)
       this.phoneCountdown()
     },
+    // 微信登录
+    toWechat () {
+      // window.open(this.qrcode)
+      location.href = this.qrcode
+    },
 
     // 账号申请
     toAccount () {
@@ -899,10 +917,23 @@ export default {
     color: #ffa94e;
   }
 
+  .wechat {
+    display: flex;
+    align-items: center;
+    margin-top: 5px;
+    font-size: 14px;
+    color: #333;
+    cursor: pointer;
+
+    .icon {
+      margin-right: 5px;
+    }
+  }
+
   .submit {
     width: 100%;
     height: 48px;
-    margin-top: 30px;
+    margin-top: 20px;
     line-height: 48px;
     padding: 0;
     font-size: 20px;
diff --git a/src/pages/account/redirect/index.vue b/src/pages/account/redirect/index.vue
new file mode 100644
index 0000000..db7e015
--- /dev/null
+++ b/src/pages/account/redirect/index.vue
@@ -0,0 +1,289 @@
+<template>
+  <div>
+    <!-- 多个账号 -->
+    <el-dialog title="请选择您要登录的用户" :visible.sync="userVisible" :close-on-click-modal="false" :show-close="false"
+      custom-class="user-dia" width="500px">
+      <p class="tips">已为你展示该微信关联的账号</p>
+      <ul class="users">
+        <li :class="{ isEnable: !user.isEnable }" v-for="(user, i) in users" :key="i" @click="chooseUser(user)">
+          <span>{{ user.typeName }},{{ user.schoolName }},{{ user.userName }},{{ user.workNumber }}{{
+            user.isEnable
+              ? ''
+              : '(已禁用)' }}</span>
+          <i class="el-icon-right"></i>
+        </li>
+      </ul>
+    </el-dialog>
+
+    <!-- 新用户 -->
+    <el-dialog title="" :visible.sync="newVisible" :close-on-click-modal="false" :show-close="false"
+      custom-class="new-dia" width="400px">
+      <el-form v-if="addAccount" :model="form" :rules="rules" ref="form">
+        <h2 class="bind-tips">绑定账号</h2>
+        <el-form-item class="flex-1" prop="account">
+          <label class="label account"></label>
+          <el-input v-model.trim="form.account" placeholder="请输入账号" autocomplete="new-password"
+            @keyup.enter.native="bindSubmit"></el-input>
+        </el-form-item>
+        <el-form-item class="relative" prop="password">
+          <label class="password label"></label>
+          <el-input type="password" placeholder="请输入密码" v-model.trim="form.password" autocomplete="new-password"
+            @keyup.enter.native="bindSubmit" />
+        </el-form-item>
+        <el-button class="submit" type="primary" @click="bindSubmit">绑定</el-button>
+      </el-form>
+
+      <div v-else>
+        <p class="suc">微信授权成功</p>
+        <i class="el-icon-success icon"></i>
+        <p class="tips">您的微信暂时没有已绑定的账号</p>
+        <p class="bind">
+          <el-link :underline="false" type="primary" @click="addAccount = true">绑定已有学生账号</el-link>
+        </p>
+        <el-link :underline="false" type="primary" @click="toLogin">返回账号登录</el-link>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapMutations } from 'vuex'
+import Util from '@/libs/util'
+import Setting from '@/setting'
+import CryptoJS from 'crypto-js'
+import JSEncrypt from 'jsencrypt'
+export default {
+  data: function () {
+    return {
+      token: this.$route.query.token || '',
+      unionid: this.$route.query.unionid,
+      curUser: {},
+      userVisible: false,
+      users: [],
+
+      newVisible: true,
+      addAccount: false,
+      form: {
+        account: '',
+        password: '',
+      },
+      rules: {
+        account: [{ required: true, message: "请输入账号", trigger: "blur" }],
+        password: [{ required: true, message: "请输入密码", trigger: "blur" }],
+      },
+    };
+  },
+  mounted () {
+    localStorage.removeItem('opened')
+    // this.setLogin()
+  },
+  methods: {
+    async setLogin () {
+      const res = await this.$post(this.api.loginByUnionid)
+
+      return
+      Util.local.set(Setting.tokenKey, this.token, Setting.tokenExpires)
+      this.getOss()
+      Util.cookies.remove('serverLogin')
+      this.reloadIndex()
+      Util.successMsg('登录成功')
+    },
+    // 刷新官网
+    reloadIndex () {
+      try {
+        window.opener && window.opener.location.reload()
+      } catch (e) { }
+    },
+    // 前往登录
+    toLogin () {
+      this.$router.replace('/login')
+    },
+    // 绑定账号
+    bindSubmit () {
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          const form = JSON.parse(JSON.stringify(this.form))
+          if (this.verCodeLogin) form.distinguish = 2
+          this.$post(this.api.logins, form).then(({ status, data, message }) => {
+            // 未绑定手机号,则弹框去绑定
+            if (status == 30001) {
+              this.phoneVisible = true
+              this.getVerImg()
+              form.code = ''
+            } else if (status == 200) {
+              const accounts = data.userAccounts
+              // 如果返回的是数组,则弹框给用户选择登录哪个用户,否则,直接登录
+              if (accounts instanceof Array) {
+                this.users = accounts
+                this.userVisible = true
+              } else {
+                this.token = data.token
+                // 如果是客户,则需要选择登录的端
+                if (data.customer) {
+                  Util.cookies.set('customerName', data.customerName)
+                  this.SET_CUSTOMERNAME(data.customerName)
+                  this.selectVisible = true
+                } else {
+                  data.typeName === '学生端' ? this.setLogin() : this.toMang()
+                }
+              }
+            } else {
+              form.code = ''
+              Util.errorMsg(message)
+            }
+          }).catch(res => {
+            form.code = ''
+            this.getVerImg()
+          })
+        }
+      });
+    },
+    async getOss () {
+      const A = (key, encryptedData) => {
+        const keyHex = CryptoJS.enc.Base64.parse(key)
+        const decrypted = CryptoJS.AES.decrypt(encryptedData, keyHex, {
+          mode: CryptoJS.mode.ECB,
+          padding: CryptoJS.pad.Pkcs7
+        })
+        return decrypted.toString(CryptoJS.enc.Utf8)
+      }
+
+      const R = (encryptedKey, privateKey) => {
+        const decrypt = new JSEncrypt()
+        decrypt.setPrivateKey(privateKey)
+        const decryptedKey = decrypt.decrypt(encryptedKey)
+        return decryptedKey
+      }
+
+      const res = await this.$get(this.api.encrypt)
+      const RE = A(R(res.encryptedKey, res.privateKey), res.encryptedData).split('/')
+      localStorage.setItem('osc', JSON.stringify(RE))
+    },
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+/deep/.user-dia {
+  .tips {
+    margin-bottom: 20px;
+    text-align: center;
+    color: #666;
+  }
+
+  .users {
+    li {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 0 15px;
+      margin-bottom: 10px;
+      line-height: 40px;
+      font-size: 14px;
+      background-color: #ebeef5;
+      cursor: pointer;
+
+      &.isEnable {
+        color: #c0c4cc;
+        background-color: #f5f7fa;
+        cursor: not-allowed;
+      }
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      &:hover {
+        background-color: #d3e0ff;
+      }
+
+      i {
+        font-size: 16px;
+      }
+    }
+  }
+}
+
+/deep/.new-dia {
+  text-align: center;
+
+  .el-dialog__header {
+    display: none;
+  }
+
+  .suc {
+    font-size: 16px;
+    font-weight: 600;
+    color: #333;
+  }
+
+  .icon {
+    margin: 30px 0;
+    font-size: 50px;
+    color: #52c41a;
+  }
+
+  .tips {
+    color: #8f8f8f;
+  }
+
+  .bind {
+    margin: 30px 0 10px;
+  }
+
+  .bind-tips {
+    margin-bottom: 20px;
+    font-size: 20px;
+    font-weight: 600;
+    color: #333;
+    text-align: left;
+  }
+
+
+  .el-form-item {
+    margin-bottom: 20px;
+  }
+
+  .el-input__inner {
+    position: relative;
+    height: 40px;
+    padding: 0 20px 0 34px;
+    line-height: 40px;
+    background-color: #fbfbfb;
+    border-color: #e1e6f2;
+    border-radius: 4px !important;
+
+    &:focus {
+      border-color: $main-color;
+      box-shadow: 0 0 2px #4486e9;
+    }
+  }
+
+
+  .label {
+    z-index: 1;
+    position: absolute;
+    top: 11px;
+    left: 11px;
+    width: 18px;
+    height: 18px;
+    background: url(../../../assets/img/login/account.png) 0 0/100% 100% no-repeat;
+  }
+
+  .password {
+    background-image: url(../../../assets/img/login/password.png);
+  }
+
+  .submit {
+    width: 100%;
+    height: 40px;
+    margin-top: 20px;
+    line-height: 40px;
+    padding: 0;
+    font-size: 16px;
+    background-color: $main-color;
+    border-radius: 4px;
+    border: 0;
+  }
+}
+</style>
\ No newline at end of file
diff --git a/src/pages/station/preview/index.vue b/src/pages/station/preview/index.vue
index 38f84c5..20a485e 100644
--- a/src/pages/station/preview/index.vue
+++ b/src/pages/station/preview/index.vue
@@ -76,6 +76,8 @@
                       <img v-else-if="section.fileType === 'mp4'" src="@/assets/img/exts/video.png" alt="">
                       <img v-else-if="section.fileType === 'doc' || section.fileType === 'docx'"
                         src="@/assets/img/exts/word.png" alt="">
+                      <img v-else-if="section.fileType === 'xlsx' || section.fileType === 'xls'"
+                        src="@/assets/img/exts/excel.png" alt="">
                       <img v-else-if="section.fileType === 'txt'" src="@/assets/img/exts/txt.png" alt="">
                       <img v-else-if="section.fileType === 'pdf'" src="@/assets/img/exts/pdf.png" alt="">
                       <img v-else src="@/assets/img/exts/pic.png" alt="">
diff --git a/src/router/routes.js b/src/router/routes.js
index 366924a..980457b 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -28,6 +28,14 @@ const frameOut = [
     },
     component: () => import("@/pages/account/login")
   },
+  {
+    path: "/redirect",
+    name: "redirect",
+    meta: {
+      title: "登录"
+    },
+    component: () => import("@/pages/account/redirect")
+  },
   {
     path: '/join',
     component: () => import('@/pages/join'),
diff --git a/src/setting.js b/src/setting.js
index 7828a39..7107741 100644
--- a/src/setting.js
+++ b/src/setting.js
@@ -67,7 +67,7 @@ const Setting = {
   /**
    * 路由白名单
    * */
-  whiteList: ['/login', '/index/list', '/index/zxy', '/product/list', '/cityPartner/list', '/devPlatform/list', '/log/list', '/touristMatch/list', '/touristMatch/details', '/touristMatch/noticeDetail', '/preCourse/list', '/preCourse/details', '/preInfo/list', '/preInfo/details', '/screen', '/screenShow', '/screenShowPro', '/join', '/join/success'],
+  whiteList: ['/login', '/redirect', '/index/list', '/index/zxy', '/product/list', '/cityPartner/list', '/devPlatform/list', '/log/list', '/touristMatch/list', '/touristMatch/details', '/touristMatch/noticeDetail', '/preCourse/list', '/preCourse/details', '/preInfo/list', '/preInfo/details', '/screen', '/screenShow', '/screenShowPro', '/join', '/join/success'],
   /**
    * 平台列表
    * */