服务号授权
This commit is contained in:
parent
2cf940e0b9
commit
b21609d1d8
@ -4,3 +4,4 @@ MP_CACHE_PREFIX=development
|
|||||||
MP_WX_APP_ID=wx93af55767423938e
|
MP_WX_APP_ID=wx93af55767423938e
|
||||||
MP_CORP_ID=wwe3fb2faa52cf9dfb
|
MP_CORP_ID=wwe3fb2faa52cf9dfb
|
||||||
MP_TIM_SDK_APP_ID=1600123876
|
MP_TIM_SDK_APP_ID=1600123876
|
||||||
|
MP_WX_MP_APP_ID=wxce46d19ff09c1832
|
||||||
@ -4,3 +4,5 @@ MP_CACHE_PREFIX=development
|
|||||||
MP_WX_APP_ID=wx93af55767423938e
|
MP_WX_APP_ID=wx93af55767423938e
|
||||||
MP_CORP_ID=wwe3fb2faa52cf9dfb
|
MP_CORP_ID=wwe3fb2faa52cf9dfb
|
||||||
MP_TIM_SDK_APP_ID=1600123876
|
MP_TIM_SDK_APP_ID=1600123876
|
||||||
|
MP_WX_MP_APP_ID=wxce46d19ff09c1832
|
||||||
|
MP_WX_MP_OAUTH_URL=www.youcan365.com
|
||||||
12
pages.json
12
pages.json
@ -1,5 +1,11 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "pages/login/login",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "授权登录"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/login/redirect-page",
|
"path": "pages/login/redirect-page",
|
||||||
"style": {
|
"style": {
|
||||||
@ -206,12 +212,6 @@
|
|||||||
"navigationBarTitleText": "上传证照"
|
"navigationBarTitleText": "上传证照"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "pages/login/login",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "授权登录"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "pages/work/team/invite/invite-patient",
|
"path": "pages/work/team/invite/invite-patient",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<view class="login-page">
|
||||||
<view class="pt-lg px-15 flex flex-col items-center text-center">
|
<view class="pt-lg px-15 flex flex-col items-center text-center">
|
||||||
<image src="/static/logo-plain.png" class="logo"></image>
|
<image src="/static/logo-plain.png" class="logo"></image>
|
||||||
<view class="mt-15 text-xl font-semibold text-dark">柚健康</view>
|
<view class="mt-15 text-xl font-semibold text-dark">柚健康</view>
|
||||||
@ -8,19 +9,24 @@
|
|||||||
<!-- <button v-if="checked" class="login-btn" type="primary" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
|
<!-- <button v-if="checked" class="login-btn" type="primary" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
|
||||||
手机号快捷登录
|
手机号快捷登录
|
||||||
</button> -->
|
</button> -->
|
||||||
<button v-if="checked" class="login-btn" type="primary" @click="getPhoneNumber()">
|
<button v-if="checked" class="login-btn" type="primary" open-type="getPhoneNumber"
|
||||||
|
@getphonenumber="getPhoneNumber">
|
||||||
手机号快捷登录
|
手机号快捷登录
|
||||||
</button>
|
</button>
|
||||||
<button v-else class="login-btn" type="primary" @click="remind()">
|
<button v-else class="login-btn" type="primary" @click="remind()">
|
||||||
手机号快捷登录
|
手机号快捷登录
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="mt-10 text-center">
|
||||||
|
<text class="mp-oauth-link" @click="toMpOauth">关联公众号(用于用户映射)</text>
|
||||||
|
</view>
|
||||||
<view class="flex items-center justify-center mt-12 px-15" @click="checked = !checked">
|
<view class="flex items-center justify-center mt-12 px-15" @click="checked = !checked">
|
||||||
<checkbox :checked="checked" style="transform: scale(0.7)" />
|
<checkbox :checked="checked" style="transform: scale(0.7)" />
|
||||||
<view class="text-sm text-gray">我已阅读并同意</view>
|
<view class="text-sm text-gray">我已阅读并同意</view>
|
||||||
<view class="text-sm text-primary">《用户协议》、</view>
|
<view class="text-sm text-primary">《用户协议》、</view>
|
||||||
<view class="text-sm text-primary">《隐私政策》</view>
|
<view class="text-sm text-primary">《隐私政策》</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -28,7 +34,7 @@ import { ref } from "vue";
|
|||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
import useAccountStore from "@/store/account";
|
import useAccountStore from "@/store/account";
|
||||||
import { get } from "@/utils/cache";
|
import { get, set } from "@/utils/cache";
|
||||||
import { toast } from "@/utils/widget";
|
import { toast } from "@/utils/widget";
|
||||||
|
|
||||||
const team = ref(true);
|
const team = ref(true);
|
||||||
@ -67,8 +73,30 @@ function toHome() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toMpOauth() {
|
||||||
|
// 写死:公众号网页授权(snsapi_base)
|
||||||
|
// TODO: 把 YOUR_MP_APPID 替换为真实「公众号 appid」
|
||||||
|
// 注意:redirect_uri 的域名必须在公众号后台配置为“网页授权域名”(你们是 www.youcan365.com)
|
||||||
|
const MP_APPID = __VITE_ENV__.MP_WX_MP_APP_ID;
|
||||||
|
const REDIRECT_URI = "https://www.youcan365.com/wx-callback";
|
||||||
|
const state = `ykt_wxapp_${Date.now()}`;
|
||||||
|
const oauthUrl =
|
||||||
|
`https://open.weixin.qq.com/connect/oauth2/authorize` +
|
||||||
|
`?appid=${MP_APPID}` +
|
||||||
|
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
|
||||||
|
`&response_type=code` +
|
||||||
|
`&scope=snsapi_base` +
|
||||||
|
`&state=${encodeURIComponent(state)}` +
|
||||||
|
`#wechat_redirect`;
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/webview/webview?purpose=mp_oauth&url=${encodeURIComponent(oauthUrl)}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function getPhoneNumber(e) {
|
async function getPhoneNumber(e) {
|
||||||
|
console.log('e', e);
|
||||||
const phoneCode = e && e.detail && e.detail.code;
|
const phoneCode = e && e.detail && e.detail.code;
|
||||||
|
console.log('phoneCode', phoneCode);
|
||||||
// if (e && !phoneCode) return;
|
// if (e && !phoneCode) return;
|
||||||
const res = await login(phoneCode);
|
const res = await login(phoneCode);
|
||||||
|
|
||||||
@ -92,6 +120,11 @@ async function attempToPage(url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onLoad((opts) => {
|
onLoad((opts) => {
|
||||||
|
// 如果你从公众号H5 OAuth回跳到小程序,可以把公众号 oauth code 通过参数带过来(比如 ?mpCode=xxx)
|
||||||
|
// 这里先缓存下来,供 store/account.js 登录时透传给后端做用户映射
|
||||||
|
if (opts && opts.mpCode) {
|
||||||
|
set("mp-oauth-code", opts.mpCode, 300); // 5分钟过期
|
||||||
|
}
|
||||||
if (opts.source === "teamInvite") {
|
if (opts.source === "teamInvite") {
|
||||||
team.value = get("invite-team-info");
|
team.value = get("invite-team-info");
|
||||||
redirectUrl.value = `/pages/archive/edit-archive?teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
|
redirectUrl.value = `/pages/archive/edit-archive?teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
|
||||||
@ -142,4 +175,9 @@ onLoad((opts) => {
|
|||||||
.login-btn:active {
|
.login-btn:active {
|
||||||
background: linear-gradient(270deg, #1b5cc8 2.26%, #0877f1 94.33%);
|
background: linear-gradient(270deg, #1b5cc8 2.26%, #0877f1 94.33%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mp-oauth-link {
|
||||||
|
color: #0877f1;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,19 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="webview-container">
|
<view class="webview-container">
|
||||||
<web-view :src="url"></web-view>
|
<web-view :src="url" @message="onMessage"></web-view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { set } from "@/utils/cache";
|
||||||
|
import { toast } from "@/utils/widget";
|
||||||
const url = ref("");
|
const url = ref("");
|
||||||
|
const purpose = ref("");
|
||||||
|
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
if (options.url) {
|
if (options.url) {
|
||||||
url.value = decodeURIComponent(options.url);
|
url.value = decodeURIComponent(options.url);
|
||||||
|
console.log('url2', url.value);
|
||||||
}
|
}
|
||||||
|
purpose.value = options.purpose || "";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function onMessage(e) {
|
||||||
|
// H5(公众号OAuth回调页) 可通过 wx.miniProgram.postMessage 传回 mpCode
|
||||||
|
// 参考:wx.miniProgram.postMessage({ data: { mpCode: '<code>' } })
|
||||||
|
const msg = e && e.detail && e.detail.data;
|
||||||
|
const payload = Array.isArray(msg) ? msg[msg.length - 1] : msg;
|
||||||
|
const mpCode = payload && payload.mpCode;
|
||||||
|
if (!mpCode) return;
|
||||||
|
console.log('mpCode', mpCode);
|
||||||
|
set("mp-oauth-code", mpCode, 300); // 5分钟过期,供后续登录透传给后端映射
|
||||||
|
toast("公众号授权成功");
|
||||||
|
|
||||||
|
// 返回上一页(通常是登录页)
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({ url: '/pages/login/login' });
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -3,12 +3,14 @@ import { defineStore } from "pinia";
|
|||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import { toast } from '@/utils/widget';
|
import { toast } from '@/utils/widget';
|
||||||
import { initGlobalTIM, globalTimChatManager } from "@/utils/tim-chat.js";
|
import { initGlobalTIM, globalTimChatManager } from "@/utils/tim-chat.js";
|
||||||
|
import { get, remove } from "@/utils/cache";
|
||||||
const env = __VITE_ENV__;
|
const env = __VITE_ENV__;
|
||||||
|
|
||||||
|
|
||||||
export default defineStore("accountStore", () => {
|
export default defineStore("accountStore", () => {
|
||||||
const appid = env.MP_WX_APP_ID;
|
const appid = env.MP_WX_APP_ID;
|
||||||
const corpId = env.MP_CORP_ID;
|
const corpId = env.MP_CORP_ID;
|
||||||
|
const mp_appid = env.MP_WX_MP_APP_ID;
|
||||||
const account = ref(null);
|
const account = ref(null);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const loginPromise = ref(null);
|
const loginPromise = ref(null);
|
||||||
@ -32,17 +34,30 @@ export default defineStore("accountStore", () => {
|
|||||||
|
|
||||||
async function loginByCode(phoneCode = '') {
|
async function loginByCode(phoneCode = '') {
|
||||||
try {
|
try {
|
||||||
|
console.log('appid', appid);
|
||||||
|
console.log('mp_appid', mp_appid);
|
||||||
|
|
||||||
|
// 获取小程序 code(静默,无需授权页)
|
||||||
const { code } = await uni.login({
|
const { code } = await uni.login({
|
||||||
appid,
|
appid,
|
||||||
provider: "weixin",
|
provider: "weixin",
|
||||||
scope: "snsapi_base",
|
scope: "snsapi_base",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 公众号(服务号)OAuth code 不能在小程序内通过 uni.login 获取。
|
||||||
|
// 正确方式:在“微信内置浏览器/H5”走公众号 OAuth 重定向后,把 code 回传到小程序(例如通过 query 参数/缓存),这里读取并透传给后端做映射。
|
||||||
|
const mpCode = get("mp-oauth-code", "");
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
|
// 将小程序 code + 公众号 code 一起传给后端,进行用户映射(后端用各自 appid/secret 换 openid/unionid)
|
||||||
const res = await api('wxAppLogin', {
|
const res = await api('wxAppLogin', {
|
||||||
phoneCode,
|
phoneCode,
|
||||||
code,
|
code, // 小程序code
|
||||||
|
mpCode, // 公众号 code(如果有)
|
||||||
corpId,
|
corpId,
|
||||||
});
|
});
|
||||||
|
if (mpCode) remove("mp-oauth-code");
|
||||||
|
|
||||||
if (res.success && res.data) {
|
if (res.success && res.data) {
|
||||||
if (!res.data.mobile) {
|
if (!res.data.mobile) {
|
||||||
const target = '/pages/login/login';
|
const target = '/pages/login/login';
|
||||||
@ -52,7 +67,6 @@ export default defineStore("accountStore", () => {
|
|||||||
account.value = res.data;
|
account.value = res.data;
|
||||||
openid.value = res.data.openid;
|
openid.value = res.data.openid;
|
||||||
// 登录成功后初始化腾讯IM
|
// 登录成功后初始化腾讯IM
|
||||||
|
|
||||||
await getDoctorInfo(openid.value);
|
await getDoctorInfo(openid.value);
|
||||||
await initIMAfterLogin();
|
await initIMAfterLogin();
|
||||||
return res.data
|
return res.data
|
||||||
@ -61,6 +75,7 @@ export default defineStore("accountStore", () => {
|
|||||||
toast('登录失败,请重新登录');
|
toast('登录失败,请重新登录');
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error('登录失败:', e);
|
||||||
toast('登录失败,请重新登录');
|
toast('登录失败,请重新登录');
|
||||||
}
|
}
|
||||||
return Promise.reject()
|
return Promise.reject()
|
||||||
|
|||||||
83
wx-callback.html
Normal file
83
wx-callback.html
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>微信授权回调</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||||
|
padding: 20px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-display {
|
||||||
|
word-break: break-all;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h3>微信公众号授权回调页</h3>
|
||||||
|
|
||||||
|
<p>当前页面地址:</p>
|
||||||
|
<div id="url-display" class="url-display"></div>
|
||||||
|
|
||||||
|
<p>状态:</p>
|
||||||
|
<div id="status" class="status">正在处理...</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const urlDisplay = document.getElementById('url-display');
|
||||||
|
const statusDiv = document.getElementById('status');
|
||||||
|
|
||||||
|
// 显示当前地址
|
||||||
|
urlDisplay.textContent = location.href;
|
||||||
|
|
||||||
|
// 解析 code
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const code = params.get('code');
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
statusDiv.textContent = '错误:URL中未找到 code 参数。';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
statusDiv.textContent = '成功获取到 code,等待小程序环境注入...';
|
||||||
|
|
||||||
|
function sendCodeToMiniProgram() {
|
||||||
|
if (!wx || !wx.miniProgram) {
|
||||||
|
statusDiv.textContent = '错误:当前不在小程序 web-view 环境中,无法回传 code。';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
statusDiv.textContent = '检测到小程序环境,正在回传 code...';
|
||||||
|
wx.miniProgram.postMessage({ data: { mpCode: code } });
|
||||||
|
setTimeout(function () {
|
||||||
|
wx.miniProgram.navigateBack();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等 JS-SDK/WeixinJSBridge 准备好再发
|
||||||
|
if (typeof WeixinJSBridge !== 'undefined') {
|
||||||
|
sendCodeToMiniProgram();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('WeixinJSBridgeReady', sendCodeToMiniProgram, false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user