其他扩展
第三方登录-QQ 登录流程
首先:
- 需要在 QQ 互联 平台注册。
- 需要实名身份认证,审核通过。
- 然后创建我的 web 应用,需要有网站域名,需要域名备案号,设置登录成功回跳地址,审核通过。
- 得到 appid 和 回跳地址。
# 测试用 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
<script
src="https://connect.qq.com/qc_jssdk.js"
data-appid="102015968"
data-redirecturi="http://consult-patients.itheima.net/login/callback"
></script>
2)网页版-生成 QQ 登录跳转链接,改成直接跳转
<div class="icon" id="qq"></div>
onMounted(() => {
// 组件渲染完毕,使用QC生成QQ登录按钮,目的得到跳转链接
QC.Login({
btnId: "qq",
});
});
3 ) 移动版
- 删除上方第二步代码
- 以上可以审查元素看到登录链接,复制后改成 A 标签改成 href 跳转即可
<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>
注意
在手机访问会走 QQ 登录手机页面,点击按钮唤起 QQ 应用进行登录,课堂无法演示(限制域名)
开发中可以把谷歌手机模拟器关闭,关闭后可以可以使用手机 QQ 扫码进行登录,可走通流程。
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 配置
server: {
port: 80,
host: true,
open: true
},
6)回跳地址白名单
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)路由规则,加入登录白名单,基础结构
{
path: '/login/callback',
component: () => import('@/views/Login/LoginCallback.vue'),
meta: { title: 'QQ登录-绑定手机' }
}
// 不需要登录的页面,白名单
const wihteList = ["/login", "/login/callback"];
新建页面:views/login/LoginCallback.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
export const loginByQQAPI = (openId: string) => {
return request({
url: '/login/thirdparty',
method: 'post',
data: { openId, source: 'qq' }
})
}
3)提供 QC
相关的类型,使用 QQ 的 SDK 提供 QC
相关 API 获取 openId
env.d.ts
interface Window {
_AMapSecurityConfig: {
securityJsCode: string
}
+ QC: {
+ Login: {
+ check(): boolean
+ getMe(cb: (openId: string) => void): void
+ }
+ }
}
/Login/LoginCallback.vue
记录 openId 和 isBind
<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>
小结:
isBind
是false
需要显示绑定手机界面
第三方登录-绑定手机
步骤:
- 准备基础页面
- 表单校验
- 发送验证码(拷贝)
- 进行绑定
- 绑定成功即是登录成功,根据是否有回跳地址进行跳转
代码:
1)准备基础页面
3)声明API
export const bindMobileAPI = (data: { mobile: string; code: string; openId: string }) => {
return request({ url: '/login/binding', method: 'post', data })
}
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);
};
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)提取函数
import { type FormInstance } from "vant";
import { sendMobileCode } from "@/services/user";
import type { CodeType } from "@/types/user";
// 发送短信验证码吗逻辑
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)使用函数
const { time, send } = useSendMobileCode(mobile, "bindMobile");
第三方登录-开发生产环境
步骤:
- 知道使用 什么是开发环境和生成环境?
- run dev 是本地开发环境,run build 是线上生产环境
- QQ 回调地址:
- 支付回调
- 标题:生产环境(优医问诊),本地环境(本地-优医问诊)
代码:
.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 文件中
- 跳转 QQ 登录
Login/index.vue
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"
)}`;
<a @click="store.setReturnUrl($route.query.returnUrl as string)" :href="qqUrl">
<img src="@/assets/qq.svg" alt="" />
</a>
2)CpPaySheet 加入动态域名
const res = await getConsultOrderPayUrl({
orderId: orderId,
paymentMethod: paymentMethod.value,
+ payCallback: import.meta.env.VITE_APP_CALLBACK + payCallback
})
3)配置问诊支付回调 Consult/ConsultPay.vue
User/ConsultDetail.vue
<cp-pay-sheet
v-model:show="show"
:order-id="orderId"
:actualPayment="payInfo.actualPayment"
:onClose="onClose"
+ pay-callback="/room"
/>
4)配置药品订单支付回调 Order/OrderPay.vue
<cp-pay-sheet
:orderId="orderId"
:actualPayment="orderPre.actualPayment"
v-model:show="show"
+ payCallback="/order/pay/result"
/>
index.html 中
安装 html 模板插件
pnpm i vite-plugin-html
vite.config.ts
import { createHtmlPlugin } from "vite-plugin-html";
plugins: [createHtmlPlugin()];
index.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
<script src="https://cdnjs.cloudflare.com/ajax/libs/eruda/2.4.1/eruda.min.js"></script>
<script>
eruda.init();
</script>
只在开发环境使用:
<% 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
pnpm i vite-plugin-mock mockjs -D
2)使用插件扫描 src/mock
下文件
import { viteMockServe } from "vite-plugin-mock";
plugins: [
viteMockServe({
mockPath: "./src/mock",
localEnabled: true,
}),
];
3)mock 文件 src/mock/index.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)本地打包
pnpm build
得到 dist 资源包
2)上传服务器
服务器一般是 linux 系统,使用 XFTP 进行文件的上传和下载(这个就不赘述了)
3)服务器使用 pm2 进行部署(本地演示) 托管静态资源
全局安装:
npm i pm2 -g
进入 dist:
# pm2 serve 目录 端口 --name 服务名称
pm2 serve ./ 8080 --name my-cp-server
4)history 路由模式问题,如果有子路径,刷新页面 404
原因:hostory 改变路由是前端切换,不会请求服务器,一旦刷新浏览器 /consult/dep
按照这个地址请求服务器,是没有对应的资源的。
解决:遇见子路径且没有后缀名,服务器定位到 ‘index.html’ 页面返回给前端即可。
命令:
pm2 serve --spa ./ 8080 --name my-cp-server
pm2 其他命令:
# 查看服务列表
pm2 list
# 删除服务
pm2 delete my-cp-server
自动部署-腾讯云部署
- 注册,实名认证,才可以使用 webify 服务
使用步骤演示:
1)创建应用
2)关联码云,需要码云授权
3)选择需要使用的仓库
4)构建配置
5)进行构建
6)尝试访问
自动部署流程:
自动部署-gitlab 部署(演示)
- 本地 Vscode 编写代码
- gitlab 是企业版,内部部署
- Linux 服务器
- 安装 gitlab-runner 用于拉取仓库代码
- 安装 Nodejs 用于打包项目
- 安装 pm2 用于启动静态资源托管,守护进程
- 运维使用 Nginx 进行域名代理
- 用户通过浏览器访问服务
依赖于 gitlab-ci.yml 配置文件
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