Skip to content
On this page

其他扩展

第三方登录-QQ 登录流程

image-20220902131335118

首先:

  • 需要在 QQ 互联 平台注册。
  • 需要实名身份认证,审核通过。
  • 然后创建我的 web 应用,需要有网站域名,需要域名备案号,设置登录成功回跳地址,审核通过。
  • 得到 appid 和 回跳地址。
bash
# 测试用 appid
# 100556005
# 测试用 redirect_uri
# http://consult-patients.itheima.net/login/callback

步骤:

  • 准备 QQ 登录按钮
  • 准备回跳页面
    • 使用 openID 进行登录
    • 登录成功,跳转到来源页面
    • 登录失败,显示绑定手机界面
      • 绑定成功,跳转到来源页面

第三方登录-跳转 QQ 登录

步骤:

  • 引入 QQ 登录 SDK
  • 生成 QQ 登录跳转链接
  • 登录后回跳成功
  • 本地 host 配置

代码:

1) 引入 QQ 登录 SDK

SDK本质:官方提供的JS脚本

作用:在你的网页中,通过JS创建一个QQ登录

index.html

vue
<script
  src="https://connect.qq.com/qc_jssdk.js"
  data-appid="102015968"
  data-redirecturi="http://consult-patients.itheima.net/login/callback"
></script>

2)网页版-生成 QQ 登录跳转链接,改成直接跳转

html
<div class="icon" id="qq"></div>
ts
onMounted(() => {
  // 组件渲染完毕,使用QC生成QQ登录按钮,目的得到跳转链接
  QC.Login({
    btnId: "qq",
  });
});

3 ) 移动版

  • 删除上方第二步代码
  • 以上可以审查元素看到登录链接,复制后改成 A 标签改成 href 跳转即可
html
<a
   class='icon'
  href="https://graph.qq.com/oauth2.0/authorize?client_id=102015968&response_type=token&scope=all&redirect_uri=http%3A%2F%2Fconsult-patients.itheima.net%2Flogin%2Fcallback"
>
  <img src="@/assets/qq.svg" alt="" />
</a>

image-20220901163913209

注意

在手机访问会走 QQ 登录手机页面,点击按钮唤起 QQ 应用进行登录,课堂无法演示(限制域名)

开发中可以把谷歌手机模拟器关闭,关闭后可以可以使用手机 QQ 扫码进行登录,可走通流程。

image-20220901163349489

3)登录后回跳成功

链接如下,路由为 /login/callback

http://consult-patients.itheima.net/login/callback#access_token=B417C0C3EBF93A380A22A188A9C491A4&expires_in=7776000

4) 本地 host 配置 windows

1. 找到 C:\Windows\System32\drivers\etc 下hosts文件
2. 在文件中加入  127.0.0.1       consult-patients.itheima.net
3. 保存即可。
# 如果提示没有权限
1. 将hosts文件移到桌面,然后进行修改,确认保存。
2. 将桌面hosts文件替换c盘文件

mac OS

1. 打开命令行窗口
2. 输入:sudo vim /etc/hosts
3. 按下:i 键
4. 输入:127.0.0.1       consult-patients.itheima.net
5. 按下:esc
6. 按下:shift + :
7. 输入:wq 回车即可

5) vite 配置

ts
server: {
    port: 80,
    host: true,
    open: true
  },

6)回跳地址白名单

diff
router.beforeEach((to) => {
  NProgress.start()
  const store = useUserStore()
+  const whiteList = ['/login', '/login/callback']
  if (!store.user?.token && !whiteList.includes(to.path)) return '/login'
})

第三方登录-进行登录

步骤:

  • 路由规则,加入登录白名单,基础结构
  • 编写 QQ 登录 API 函数
  • 提供 QC 相关的类型,使用 QQ 的 SDK 提供 QC 相关 API 获取 openId
  • 提交三方登录亲请求
    • 1 如果之前没绑定,失败:需要绑定手机
    • 2 如果之前绑定过,成功:和之前登录成功一样的逻辑
    • 刚注册用户,属于情况 1

代码:

1)路由规则,加入登录白名单,基础结构

ts
{
      path: '/login/callback',
      component: () => import('@/views/Login/LoginCallback.vue'),
      meta: { title: 'QQ登录-绑定手机' }
    }
ts
// 不需要登录的页面,白名单
const wihteList = ["/login", "/login/callback"];

新建页面:views/login/LoginCallback.vue

vue
<script setup lang="ts">
import { sendCodeAPI } from '@/services/user'
import { codeRules, mobileRules } from '@/utils/rules'
import { onUnmounted, ref } from 'vue'

const mobile = ref('13230000001')
const code = ref('')
const second = ref(0)

let timerId: number
const onSendCode = async () => {
  const res = await sendCodeAPI(mobile.value, 'bindMobile')
  code.value = res.data.code
  second.value = 60
  timerId = setInterval(() => {
    second.value--
    if (second.value === 0) clearInterval(timerId)
  }, 100)
}

onUnmounted(() => {
  clearInterval(timerId)
})

const onSubmit = async () => {}
</script>

<template>
  <div class="login-page" >
    <cp-nav-bar title="绑定手机号"></cp-nav-bar>

    <van-form autocomplete="off" @submit="onSubmit">
      <van-field v-model="mobile" placeholder="请输入手机号" type="tel" :rules="mobileRules" />

      <van-field :rules="codeRules" v-model="code" placeholder="请输入验证码">
        <template #button>
          <span class="send-code" v-show="second === 0" @click="onSendCode">发送验证码</span>
          <span v-show="second > 0">{{ second }}秒后获取</span>
        </template>
      </van-field>
			<div class="cp-cell">
        <van-button block round type="primary" native-type="submit"> 立即绑定 </van-button>
      </div>
    </van-form>
  </div>
</template>

<style lang="scss" scoped>
.van-form {
  padding: 0 14px;
  .cp-cell {
    height: 52px;
    line-height: 24px;
    padding: 14px 16px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    .van-checkbox {
      a {
        color: var(--cp-primary);
        padding: 0 5px;
      }
    }
  }
  .send-code {
    --cp-primary: #16c2a3;
    color: var(--cp-primary);
  }
}
</style>

2)编写 QQ 登录 API 函数 services/user.ts

ts
export const loginByQQAPI = (openId: string) => {
  return request({
    url: '/login/thirdparty',
    method: 'post',
    data: { openId, source: 'qq' }
  })
}

3)提供 QC 相关的类型,使用 QQ 的 SDK 提供 QC 相关 API 获取 openIdenv.d.ts

diff
interface Window {
  _AMapSecurityConfig: {
    securityJsCode: string
  }
+  QC: {
+    Login: {
+      check(): boolean
+      getMe(cb: (openId: string) => void): void
+    }
+  }
}

/Login/LoginCallback.vue 记录 openId 和 isBind

vue
<script setup lang="ts">
import { onMounted, ref } from "vue";

const openId = ref("");
const isBind = ref(true);
const router = useRouter()
onMounted(() => {
  if (window.QC.Login.check()) {
    window.QC.Login.getMe((id) => {
      openId.value = id;
      // QQ,登录
      loginByQQAPI(id)
        .then((res) => {
          // 登录成功
        	router.push('/')
        })
        .catch(() => {
          // 登录失败
          isBind.value = false;
        });
    });
  }
});
</script>

<template>
  <div class="login-page">login-callback</div>
</template>

<style lang="scss" scoped></style>

小结:

  • isBindfalse 需要显示绑定手机界面

第三方登录-绑定手机

步骤:

  • 准备基础页面
  • 表单校验
  • 发送验证码(拷贝)
  • 进行绑定
  • 绑定成功即是登录成功,根据是否有回跳地址进行跳转

代码:

1)准备基础页面

3)声明API

ts
export const bindMobileAPI = (data: { mobile: string; code: string; openId: string }) => {
  return request({ url: '/login/binding', method: 'post', data })
}
ts
import type { User } from "@/types/user";

// 登录成功
const store = useUserStore();
const router = useRouter();
const loginSuccess = (res: { data: User }) => {
  store.saveUser(res.data);
  router.replace("/");
  showSuccessToast("登录成功");
};

const onSubmit = async () => {
  const res = await bindMobileAPI({
    mobile: mobile.value,
    code: code.value,
    openId: openId.value,
  });
  loginSuccess(res);
};
diff
loginByQQ(id)
        .then((res) => {
+          loginSuccess(res)
        })
        .catch(() => {
          isBind.value = false
        })

第三方登录-验证码 hook 封装

步骤:

  • 分析 hook 需要传入参数,返回哪些数据
  • 封装 hook 函数
  • 使用 hook 函数

代码:

1)分析 hook 需要传入参数,返回哪些数据

参数:
1. 手机号
2. 发短信类型

返回:
1. form 表单响应式数据
2. time 倒计时数据
3. send 发送函数

2)提取函数

ts
import { type FormInstance } from "vant";
import { sendMobileCode } from "@/services/user";
import type { CodeType } from "@/types/user";
ts
// 发送短信验证码吗逻辑
export const useSendMobileCode = (
  mobile: Ref<string>,
  type: CodeType = "login"
) => {
  
  const time = ref(0);
  let timeId: number;
  const send = async () => {
    if (time.value > 0) return;
    await sendMobileCode(mobile.value, type);
    showSuccessToast("发送成功");
    time.value = 60;
    // 倒计时
   
    timeId = window.setInterval(() => {
      time.value--;
      if (time.value <= 0) window.clearInterval(timeId);
    }, 1000);
  };
  onUnmounted(() => {
    window.clearInterval(timeId);
  });
  return {  time, send };
};

3)使用函数

ts
const {  time, send } = useSendMobileCode(mobile, "bindMobile");

第三方登录-开发生产环境

步骤:

代码:

.env.development

VITE_APP_CALLBACK=http://consult-patients.itheima.net
VITE_APP_TITLE=本地-优医问诊

.env.production

VITE_APP_CALLBACK=https://cp.itheima.net
VITE_APP_TITLE=优医问诊

JS 文件中

  1. 跳转 QQ 登录 Login/index.vue
ts
const qqUrl = `https://graph.qq.com/oauth2.0/authorize?client_id=102015968&response_type=token&scope=all&redirect_uri=${encodeURIComponent(
  import.meta.env.VITE_APP_CALLBACK + "/login/callback"
)}`;
html
<a @click="store.setReturnUrl($route.query.returnUrl as string)" :href="qqUrl">
  <img src="@/assets/qq.svg" alt="" />
</a>

2)CpPaySheet 加入动态域名

diff
const res = await getConsultOrderPayUrl({
    orderId: orderId,
    paymentMethod: paymentMethod.value,
+    payCallback: import.meta.env.VITE_APP_CALLBACK + payCallback
  })

3)配置问诊支付回调 Consult/ConsultPay.vue User/ConsultDetail.vue

diff
<cp-pay-sheet
        v-model:show="show"
        :order-id="orderId"
        :actualPayment="payInfo.actualPayment"
        :onClose="onClose"
+        pay-callback="/room"
      />

4)配置药品订单支付回调 Order/OrderPay.vue

diff
<cp-pay-sheet
      :orderId="orderId"
      :actualPayment="orderPre.actualPayment"
      v-model:show="show"
+      payCallback="/order/pay/result"
    />

index.html 中

安装 html 模板插件

bash
pnpm i vite-plugin-html

vite.config.ts

ts
import { createHtmlPlugin } from "vite-plugin-html";
ts
plugins: [createHtmlPlugin()];

index.html <%=环境变量名%> 取出值

html
<head>
  <meta charset="UTF-8" />
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title><%=VITE_APP_TITLE%></title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.ts"></script>
  <script
    src="https://connect.qq.com/qc_jssdk.js"
    data-appid="102015968"
    data-redirecturi="<%=VITE_APP_CALLBACK%>/login/callback"
  ></script>
</body>

扩展-真机调试

  • 在 Chrome 浏览器中使用什么进行调试?
    • 控制面板,开发者工具,F12
  • 在 手机端 浏览器使用什么进行调试呢?
  • Eruda 手机调试面板工具

使用方式:https://github.com/liriliri/eruda

index.html

html
<script src="https://cdnjs.cloudflare.com/ajax/libs/eruda/2.4.1/eruda.min.js"></script>
<script>
  eruda.init();
</script>

只在开发环境使用:

html
<% if(DEV){ %>
<script src="https://cdnjs.cloudflare.com/ajax/libs/eruda/2.4.1/eruda.min.js"></script>
<script>
  eruda.init();
</script>
<% } %>

小结:

  • 使用 vite-plugin-html 解析模板,默认支持 ejs 模板引擎语法

扩展-mock 接口数据

https://www.npmjs.com/package/vite-plugin-mock

1)安装 vite-plugin-mock mockjs

bash
pnpm i vite-plugin-mock mockjs -D

2)使用插件扫描 src/mock 下文件

ts
import { viteMockServe } from "vite-plugin-mock";
ts
plugins: [
  viteMockServe({
    mockPath: "./src/mock",
    localEnabled: true,
  }),
];

3)mock 文件 src/mock/index.ts

ts
import type { MockMethod } from "vite-plugin-mock";
import Mock from "mockjs";

const rules: MockMethod[] = [
  {
    url: "/patient/message/list",
    method: "get",
    timeout: 1000,
    response: () => {
      const data = [];
      for (let i = 0; i < 10; i++) {
        data.push(
          Mock.mock({
            id: "@id",
            avatar: '@image("100x100")',
            title: "@ctitle(3,10)",
            lastContent: "@ctitle(10,40)",
            sendTime: "@datetime()",
          })
        );
      }
      return {
        code: 10000,
        message: "获取数据成功",
        data,
      };
    },
  },
];

export default rules;

使用注意:

  • 这些 mock 接口是 vite 本地服务器提供的,请求的时候不能带上其他服务器的域名。

项目部署-pm2 部署

1)本地打包

sh
pnpm build

得到 dist 资源包

2)上传服务器

服务器一般是 linux 系统,使用 XFTP 进行文件的上传和下载(这个就不赘述了)

3)服务器使用 pm2 进行部署(本地演示) 托管静态资源

全局安装:

sh
npm i pm2 -g

进入 dist:

sh
# pm2 serve 目录 端口  --name 服务名称
pm2 serve ./ 8080 --name my-cp-server

4)history 路由模式问题,如果有子路径,刷新页面 404

原因:hostory 改变路由是前端切换,不会请求服务器,一旦刷新浏览器 /consult/dep按照这个地址请求服务器,是没有对应的资源的。

解决:遇见子路径且没有后缀名,服务器定位到 ‘index.html’ 页面返回给前端即可。

命令:

sh
pm2 serve --spa ./ 8080 --name my-cp-server

pm2 其他命令:

sh
# 查看服务列表
pm2 list
# 删除服务
pm2 delete my-cp-server

自动部署-腾讯云部署

腾讯云-Web 应用托管

  • 注册,实名认证,才可以使用 webify 服务

使用步骤演示:

1)创建应用

image-20220905110845886

2)关联码云,需要码云授权

image-20220905111106986

3)选择需要使用的仓库

image-20220905111526726

4)构建配置

image-20220905111847289

image-20220905111952307

5)进行构建

image-20220905112105818

image-20220905112731150

6)尝试访问

image-20220905112907762

自动部署流程:

image-20220905113553768

自动部署-gitlab 部署(演示)

image-20220905124702905

  1. 本地 Vscode 编写代码
  2. gitlab 是企业版,内部部署
  3. Linux 服务器
    1. 安装 gitlab-runner 用于拉取仓库代码
    2. 安装 Nodejs 用于打包项目
    3. 安装 pm2 用于启动静态资源托管,守护进程
  4. 运维使用 Nginx 进行域名代理
  5. 用户通过浏览器访问服务

依赖于 gitlab-ci.yml 配置文件

text
stages:
  - build
cache:
  key: ${CI_BUILD_REF_NAME}
  paths:
    - node_modules/

build-140:
  stage: build
  only:
    - master
  script:
    - rm -rf node_modules/
    - pnpm i
    - pnpm build
    - rm -rf /home/patient-h5-preview
    - mkdir /home/patient-h5-preview
    - cp -r dist/* /home/patient-h5-preview
    - cd /home/patient-h5-preview
    - pm2 delete patient-h5-preview || echo no
    - pm2 serve --spa ./ 8083 --name patient-h5-preview
  tags:
    - patient-h5-preview

Released under the MIT License.