فهرست منبع

初步完成pc登录流程

jwyu 2 سال پیش
والد
کامیت
7653942e39

+ 6 - 1
.env.development

@@ -1,5 +1,10 @@
+# 接口地址
 VITE_APP_API_URL="http://8.136.199.33:8612/api"
+# 路由根地址
 VITE_APP_BASE_URL="/"
+# 打包输入文件名
 VITE_APP_OUTDIR="dist"
+# 扫码登录appid
 VITE_APP_APPID="wx615472d6874eeb7f"
-VITE_APP_REDIRECT_URI="https://hzreport.hzinsights.com/index"
+# 扫码登录回跳地址
+VITE_APP_REDIRECT_URI="https://ybpctest.hzinsights.com/report/index"

+ 0 - 5
.env.product

@@ -1,5 +0,0 @@
-VITE_APP_API_URL="https://yanbao.hzinsights.com/api"
-VITE_APP_BASE_URL="/"
-VITE_APP_OUTDIR="hongze_yb_pc"
-VITE_APP_APPID=""
-VITE_APP_REDIRECT_URI="https://ybpc.hzinsights.com/report/index"

+ 10 - 0
.env.production

@@ -0,0 +1,10 @@
+# 接口地址
+VITE_APP_API_URL="https://yanbao.hzinsights.com/api"
+# 路由根地址
+VITE_APP_BASE_URL="/"
+# 打包输入文件名
+VITE_APP_OUTDIR="hongze_yb_pc"
+# 扫码登录appid
+VITE_APP_APPID="wx4da95782cfc8c5eb"
+# 扫码登录回跳地址
+VITE_APP_REDIRECT_URI="https://ybpc.hzinsights.com"

+ 5 - 0
.env.test

@@ -1,5 +1,10 @@
+# 接口地址
 VITE_APP_API_URL="https://ybpctest.hzinsights.com/api"
+# 路由根地址
 VITE_APP_BASE_URL="/"
+# 打包输入文件名
 VITE_APP_OUTDIR="hongze_yb_pc"
+# 扫码登录appid
 VITE_APP_APPID=""
+# 扫码登录回跳地址
 VITE_APP_REDIRECT_URI="https://ybpctest.hzinsights.com/report/index"

+ 2 - 1
package.json

@@ -4,11 +4,12 @@
   "version": "0.0.0",
   "scripts": {
     "dev": "vite --port 3003",
-    "build": "vite build --mode product",
+    "build": "vite build --mode production",
     "build.test": "vite build --mode test",
     "preview": "vite preview --port 3001"
   },
   "dependencies": {
+    "@element-plus/icons-vue": "^2.0.0-beta.0",
     "axios": "^0.26.0",
     "element-plus": "^2.0.2",
     "lodash": "^4.17.21",

+ 36 - 0
src/api/common.js

@@ -18,4 +18,40 @@ export const apiGetPermissionList=()=>{
  */
 export const apiGetWechatQRCode=params=>{
     return post('/pc/getSunCode',params)
+}
+
+/**
+ * web端微信登录
+ */
+export const apiWxLoginInWeb=params=>{
+    return get('/pc/wechatLogin',params)
+}
+
+/**
+ * 获取手机验证码
+ * @param mobile 手机号
+ * @param area_num 手机号区号
+ */
+ export const apiGetSMSCode=params=>{
+    return get('/user/get_sms_code',params)
+}
+
+/**
+ * 获取邮箱验证码
+ * @param email 邮箱
+ */
+export const apiGetEmailCode=params=>{
+    return get('/user/get_email_code',params)
+}
+
+/**
+ * 手机号/邮箱登录
+ * @param area_num 手机号区号
+ * @param bind_type 1手机号 2邮箱
+ * @param email
+ * @param mobile
+ * @param verify_code
+ */
+export const apiUserLogin=params=>{
+	return post('/user/login',params)
 }

+ 5 - 4
src/api/http.js

@@ -3,6 +3,7 @@ import axios from "axios";
 import store from '@/store'
 import { ElMessage } from 'element-plus'
 import CryptoJS from './crypto'
+import {webLogin} from '@/utils/webLogin'
 
 // Full config:  https://github.com/axios/axios#request-config
 // axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
@@ -34,20 +35,20 @@ _axios.interceptors.response.use(
   function (response) {
     // Do something with response data
     let data
-    if(import.meta.env.MODE==='product'){
+    if(import.meta.env.MODE==='production'){
       data=JSON.parse(CryptoJS.Des3Decrypt(response.data));//解密
     }else{
       data=response.data
     }
     if(data.code===401){//token失效
-      // ElMessage.error('请重新登录')
+      console.log('请重新登录')
       console.log('miniprogram:',window.__wxjs_environment)
       if(window.__wxjs_environment === 'miniprogram'){
+        // 如果在小程序中 则回跳到小程序首页进行更新token 再带回到项目中 
         wx.miniProgram.switchTab({url: '/pages/report/report'})
       }else{
-        store.commit('showLogin')
+        webLogin()
       }
-      
     }
     if(data.code!==200&&data.code!==403&&data.code!==4001&&data.code!==401){
       ElMessage.error(data.msg)

BIN
src/assets/icon-login-email.png


BIN
src/assets/icon-login-phone.png


+ 229 - 15
src/components/LoginPop.vue

@@ -1,30 +1,194 @@
 <script setup>
-import { onMounted} from 'vue';
+import { onMounted,onUnmounted,reactive,ref} from 'vue';
+import { ElMessage } from 'element-plus';
+import {ArrowDown} from '@element-plus/icons-vue'
+import {apiUserLogin,apiGetEmailCode,apiGetSMSCode} from '@/api/common'
+import store from '../store';
+
+let sendCodeInterval=null
+
+let phoneAreaList=ref([
+    {
+        name: "大陆+86",
+        value: "86",
+    },
+    {
+        name: "香港+852",
+        value: "852",
+    },
+    {
+        name: "台湾+886",
+        value: "886",
+    },
+    {
+        name: "美国+1",
+        value: "1",
+    },
+    {
+        name: "新加坡+65",
+        value: "65",
+    }
+])
+let selectArea=ref({name: "大陆+86",value:'86'})
+let form=reactive({
+    type:'phone',
+    mobile:'',
+    code:'',
+    email:''
+})
+
+const typeChange=(e)=>{
+    form.type=e
+    form.mobile=''
+    form.code=''
+    form.email=''
+    clearTimeout(sendCodeInterval)
+    isSendCode.value=false
+}
+
+//发送验证码
+
+let isSendCode=ref(false)//是否已发送验证码
+const handleSendCode=async (type)=>{
+    if(isSendCode.value) return
+    let res
+    if(type=='phone'){
+        if(!form.mobile){
+            ElMessage('请输入手机号')
+            return
+        }
+        res=await apiGetSMSCode({
+            mobile: form.mobile,
+            area_num: selectArea.value.value
+        })
+    }else{
+        if(!form.email){
+            ElMessage('请输入邮箱')
+            return
+        }
+        res=await apiGetEmailCode({email: form.email})
+    }
+    if(res.code===200){
+        isSendCode.value=true
+        ElMessage.success('验证码已发送')
+        sendCodeInterval=setTimeout(() => {
+            isSendCode.value=false
+        }, 60000);
+    }
+}
+
+onUnmounted(()=>{
+    clearTimeout(sendCodeInterval)
+})
+
+const handleLogin=async ()=>{
+    let params = {}
+    if (type === 'phone') {
+        params = {
+            area_num: selectArea.value.value,
+            mobile: form.mobile,
+            verify_code: form.code,
+            bind_type: 1
+        }
+    } else {
+        params = {
+            email: form.email,
+            verify_code: form.code,
+            bind_type: 2
+        }
+    }
+    if(!params.verify_code){
+        ElMessage('请填写验证码')
+        return
+    }
+    const res=await apiUserLogin(params)
+    if(res.code===200){
+        window.location.href(import.meta.env.VITE_APP_REDIRECT_URI)
+    }
+}
+
 
 onMounted(()=>{
     // https://hzreport.hzinsights.com/index     wx615472d6874eeb7f  
     // https://ybpctest.hzinsights.com           wxb059c872d79b9967
-    const redirect_uri=encodeURIComponent(import.meta.env.VITE_APP_REDIRECT_URI)
-    let obj=new WxLogin({
-        self_redirect:false,
-        id:"wx-qrcode-box", 
-        appid: import.meta.env.VITE_APP_APPID, 
-        scope: "snsapi_login", 
-        redirect_uri: redirect_uri,
-        state: "",
-        style: "",
-        href: "",//可传入base64 编码的 css样式
-    });
+    // const redirect_uri=encodeURIComponent(import.meta.env.VITE_APP_REDIRECT_URI)
+    // let obj=new WxLogin({
+    //     self_redirect:false,
+    //     id:"wx-qrcode-box", 
+    //     appid: import.meta.env.VITE_APP_APPID, 
+    //     scope: "snsapi_login", 
+    //     redirect_uri: redirect_uri,
+    //     state: "",
+    //     style: "",
+    //     href: "",//可传入base64 编码的 css样式
+    // });
 })
 
-
 </script>
 
 <template>
     <div class="login-wrap">
         <div class="content">
             <!-- <img class="close-icon" src="@/assets/icon-close.png" alt=""> -->
-            <div id="wx-qrcode-box" class="wx-qrcode-box"></div>
+            <div id="wx-qrcode-box" class="wx-qrcode-box" v-if="$store.state.showLogin"></div>
+            <div class="bind-accout-box" v-if="$store.state.showBind">
+                <!-- <img class="logo" :src="$store.state.globalImgUrls.loginTop" alt=""> -->
+                <!-- 手机号登录 -->
+                <template v-if="form.type=='phone'">
+                <h2 class="title">手机号登录</h2>
+                <div class="phone-area-box" style="margin-bottom: 30px;">
+                    <div class="area-box">
+                        <span style="margin-right: 10px;">国家/区号</span>
+                        <el-dropdown trigger="click" popper-class="self-dropdown">
+                            <span class="el-dropdown-link" style="color: #F3A52F;font-size: 16px;">
+                                {{selectArea.name}}
+                                <el-icon class="el-icon--right">
+                                    <arrow-down />
+                                </el-icon>
+                            </span>
+                            <template #dropdown>
+                                <el-dropdown-menu>
+                                    <el-dropdown-item 
+                                        v-for="item in phoneAreaList" 
+                                        :key="item.value"
+                                        @click="selectArea=item"
+                                    >{{item.name}}</el-dropdown-item>
+                                </el-dropdown-menu>
+                            </template>
+                        </el-dropdown>
+                    </div>
+                </div>
+                <div class="input-item">
+                    <input type="text" v-model="form.mobile" placeholder="请输入手机号">
+                </div>
+                <div class="input-item">
+                    <input v-model="form.code" placeholder="请输入验证码" />
+                    <span :class="['send-code-btn',isSendCode&&'send-code-disabled']" @click="handleSendCode('phone')">{{isSendCode?'已发送':'发送验证码'}}</span>
+                </div>
+                <div class="global-main-btn login-btn" @click="handleLogin">登录</div>
+                <div class="bot-img">
+                    <img src="@/assets/icon-login-email.png" alt="" @click="typeChange('email')">
+                    <div>邮箱登录</div>
+                </div>
+
+                </template>
+                <!-- 邮箱登录 -->
+                <template v-if="form.type=='email'">
+                <h2 class="title">邮箱登录</h2>
+                <div class="input-item">
+                    <input type="text" v-model="form.email" placeholder="请输入手机号">
+                </div>
+                <div class="input-item">
+                    <input v-model="form.code" placeholder="请输入验证码" />
+                    <span :class="['send-code-btn',isSendCode&&'send-code-disabled']" @click="handleSendCode('email')">{{isSendCode?'已发送':'发送验证码'}}</span>
+                </div>
+                <div class="global-main-btn login-btn" @click="handleLogin">登录</div>
+                <div class="bot-img">
+                    <img src="@/assets/icon-login-phone.png" alt="" @click="typeChange('phone')">
+                    <div>手机号登录</div>
+                </div>
+                </template>
+            </div>
         </div>
     </div>
 </template>
@@ -37,7 +201,7 @@ onMounted(()=>{
     bottom: 00;
     top: 0;
     background-color: rgba(0,0,0,0.4);
-    z-index: 9999;
+    z-index: 999;
     .content{
         position: absolute;
         top: 50%;
@@ -59,5 +223,55 @@ onMounted(()=>{
         width: 20px;
         height: 20px;
     }
+    .bind-accout-box{
+        background-color: #fff;
+        width: 400px;
+        // min-height: 400px;
+        padding: 20px;
+        border-radius: 16px;
+        .logo{
+            width: 50px;
+            display: block;
+            margin: 0 auto 50px auto;
+        }
+        .title{
+            font-size: 20px;
+            margin-bottom: 50px;
+        }
+        input{
+            border: none;
+            outline: none;
+        }
+        .input-item{
+            margin-bottom: 20px;
+            padding: 8px 0;
+            border-bottom: 1px solid #F2F2F2;
+            position: relative;
+            .send-code-btn{
+                position: absolute;
+                right: 0;
+                bottom: 8px;
+                color: #F3A52F;
+                cursor: pointer;
+            }
+            .send-code-disabled{
+                color: #999;
+            }
+        }
+        .login-btn{
+            width: 300px;
+            margin: 50px auto;
+        }
+        .bot-img{
+            text-align: center;
+            font-size: 14px;
+            img{
+                width: 50px;
+                height: 50px;
+            }
+        }
+    }
+
+
 }
 </style>

+ 1 - 1
src/layout/Index.vue

@@ -40,7 +40,7 @@ import LoginPop from '@/components/LoginPop.vue'
     <AudioBox></AudioBox>
 
     <!-- 登录弹窗 -->
-    <LoginPop v-if="$store.state.showLogin"></LoginPop>
+    <LoginPop v-if="$store.state.showLogin||$store.state.showBind"></LoginPop>
   </div>
 </template>
 

+ 6 - 1
src/store/index.js

@@ -17,6 +17,7 @@ export default createStore({
       specialColumnBanner:'https://hzstatic.hzinsights.com/static/icon/hzyb/special_column_banner_pc.png'
     },
     showLogin:false,//是否显示微信扫码弹窗
+    showBind:false,//是否显示绑定手机号\邮箱弹窗
     token:token,
     userInfo:null,
     audioData:{
@@ -39,7 +40,11 @@ export default createStore({
   mutations: {
     //显示微信扫码登陆
     showLogin(state,e){
-      state.showLogin=true
+      state.showLogin=e
+    },
+    //显示绑定手机号
+    showBindAccount(state,e){
+      state.showBind=e
     },
       
     // 获取token

+ 49 - 0
src/utils/webLogin.js

@@ -0,0 +1,49 @@
+// web端扫码登录
+
+import {apiWxLoginInWeb} from '@/api/common'
+import store from '@/store'
+
+let HASLOGIN=false 
+
+// 获取路由中参数
+const getRouteParams=()=>{
+    const url = location.search; //获取url携带的参数
+    let urlParams = new Object();
+    if (url.indexOf("?") != -1) {
+        let str = url.substr(1);
+        let strs = str.split("&");
+        for (let i = 0; i < strs.length; i++) {
+        urlParams[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
+        }
+    }
+    return urlParams
+}
+
+export const webLogin=async ()=>{
+    if(HASLOGIN) return
+    HASLOGIN=true
+    const urlParams=getRouteParams()
+    if(urlParams.code){//路由中有code
+        // const res=await apiWxLoginInWeb({code:urlParams.code})
+        setTimeout(() => {
+        let res={
+            code:200,
+            data:{
+                authorization:'d201487b4e9b55d26e13557b766b7ea587adad4f1d7dcb951c49c31913c6ce3a',
+                is_bind:false
+            }
+        }
+        if(res.code===200){
+            store.commit('showLogin',false)
+            store.commit('getToken',res.data.authorization)
+            if(!res.data.is_bind){
+                store.commit('showBindAccount',true)
+            }else{
+                window.location.href(import.meta.env.VITE_APP_REDIRECT_URI)
+            }
+        }
+        }, 300);
+    }else{
+        store.commit('showLogin',true)
+    }
+}

+ 30 - 2
src/views/report/ChapterDetail.vue

@@ -169,6 +169,17 @@ const getAsideBanner=async (data)=>{
     }
 }
 
+//点击侧边栏报告合集
+const handleAsideBanner=(data)=>{
+    router.push({
+        path:'/report/list',
+        query:{
+            classifyId:data.classify_id_first,
+            classifyName:data.classify_name_first
+        }
+    })
+}
+
 //点击底部切换章节
 const handleChangeChapter=(item)=>{
     chapterId.value=item.report_chapter_id
@@ -335,8 +346,10 @@ let showDisclaimers=ref(false)//显示免责声明
                     </el-popover>
                 </div>
                 <div class="hot-box" style="margin-top: 60px;">
-                    <div class="label">报告合集</div>
-                    <div class="img-con"></div>
+                    <div class="label">{{banner.Type}}</div>
+                    <div class="img-con" :style="'background-image:url('+banner.ImgUrl+')'" @click="handleAsideBanner(banner)">
+                        <span class="stage">第{{banner.Stage}}期</span>
+                    </div>
                 </div>
                 <div class="recmd-box" style="margin-top: 60px;">
                     <div class="label">更多推荐</div>
@@ -501,5 +514,20 @@ let showDisclaimers=ref(false)//显示免责声明
         }
     }
 
+    .right-aside-box{
+        .hot-box{
+            .img-con{
+                position: relative;
+                .stage{
+                    position: absolute;
+                    bottom: 27px;
+                    left: 16px;
+                    color: #F3A52F;
+                    font-size: 12px;
+                }
+            }
+        }
+    }
+
 }
 </style>

+ 82 - 3
src/views/report/Detail.vue

@@ -4,7 +4,7 @@ import { ElMessageBox } from 'element-plus'
 import moment from 'moment';
 import 'moment/dist/locale/zh-cn'
 import AudioBox from './components/AudioBox.vue'
-import {apiReportDetail,apiReportMoreRecmd} from '@/api/report'
+import {apiReportDetail,apiReportMoreRecmd,apiReportDetailBanner} from '@/api/report'
 import {apiGetWechatQRCode} from '@/api/common'
 import { useRoute, useRouter } from 'vue-router';
 import { useStore } from 'vuex';
@@ -72,6 +72,7 @@ const getReportDetail=async ()=>{
         // 获取侧边更多推荐
         if(res.data.auth_ok){
             getAsideMoreRecmd(res.data.report_info)
+            getAsideBanner(res.data.report_info)
         }
 
         //向小程序发送分享数据
@@ -101,6 +102,38 @@ const getAsideMoreRecmd=async (data)=>{
     }
 }
 
+//侧边栏报告合集
+let banner=ref(null)
+const getAsideBanner=async (data)=>{
+    const res=await apiReportDetailBanner({
+        reportId:Number(data.report_id),
+        classify_name_first:data.classify_name_first
+    })
+    if(res.code===200){
+        banner.value=res.data
+    }
+}
+
+//点击侧边栏报告合集
+const handleAsideBanner=(data)=>{
+    if(data.Type=='报告合集'){
+        router.push({
+            path:'/report/list',
+            query:{
+                classifyId:data.ClassifyIdFirst,
+                classifyName:data.ClassifyNameFirst
+            }
+        })
+    }else{
+        router.push({
+            path:'/report/specialcolumndetail',
+            query:{
+                columnId:data.ClassifyIdSecond
+            }
+        })
+    }
+}
+
 let preViewImgs=ref([])
 let preViewImgIndex=ref(0)
 let showPreViewImg=ref(false)
@@ -295,8 +328,19 @@ let showDisclaimers=ref(false)//显示免责声明
                     </el-popover>
                 </div>
                 <div class="hot-box" style="margin-top: 60px;">
-                    <div class="label">热门栏目</div>
-                    <div class="img-con"></div>
+                    <div class="label">{{banner.Type}}</div>
+                    <div class="img-con" :style="'background-image:url('+banner.ImgUrl+')'" @click="handleAsideBanner(banner)">
+                        <!-- 专栏 -->
+                        <div v-if="banner.Type=='专栏详情'" class="cloumn-box">
+                            <div class="title">{{banner.ClassifyNameSecond}}</div>
+                            <div class="user">{{banner.Author}} {{banner.VipTitle}}</div>
+                            <div class="stage">更新至{{banner.Stage}}期</div>
+                        </div>
+                        <!-- 报告列表 -->
+                        <div v-else class="rep-box">
+                            <span class="stage">第{{banner.Stage}}期</span>
+                        </div>
+                    </div>
                 </div>
                 <div class="recmd-box" style="margin-top: 60px;">
                     <div class="label">更多推荐</div>
@@ -491,5 +535,40 @@ let showDisclaimers=ref(false)//显示免责声明
             }
         }
     }
+
+    .right-aside-box{
+        .hot-box{
+            .img-con{
+                position: relative;
+                .cloumn-box{
+                    padding: 17px 15px;
+                    .title{
+                        font-size: 18px;
+                        font-weight: bold;
+                        color: #fff;
+                    }
+                    .user{
+                        font-size: 12px;
+                        color: #fff;
+                        margin-top: 4px;
+                    }
+                    .stage{
+                        margin-top: 10px;
+                        color: #F3A52F;
+                        font-size: 12px;
+                    }
+                }
+                .rep-box{
+                    .stage{
+                        position: absolute;
+                        bottom: 27px;
+                        left: 16px;
+                        color: #F3A52F;
+                        font-size: 12px;
+                    }
+                }
+            }
+        }
+    }
 }
 </style>