automated_uniapp/pages/user/login.vue
2025-01-09 16:16:11 +08:00

474 lines
8.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<cl-page background-color="#fff">
<cl-topbar :border="false" background-color="transparent" />
<view class="page-login">
<!-- Logo -->
<view class="logo">
<image src="/static/logo.png" mode="aspectFill" />
<text>{{ app.info.name }}</text>
</view>
<div class="container">
<!-- 登录方式 -->
<view class="mode" :class="[`is-${mode}`]">
<!-- 手机号 -->
<template v-if="mode == 'phone'">
<text class="label">手机号登录</text>
<view class="phone">
<text>+86</text>
<cl-input
v-model="phone"
type="number"
placeholder="请填写手机号码"
:border="false"
:maxlength="11"
:font-size="30"
background-color="transparent"
/>
</view>
<sms-btn
:ref="setRefs('smsBtn')"
:phone="phone"
@success="phoneLogin(false)"
>
<template #default="{ disabled, btnText }">
<cl-button
fill
type="primary"
:height="90"
:font-size="30"
:disabled="disabled"
@tap="phoneLogin"
>
{{ btnText }}
</cl-button>
</template>
</sms-btn>
</template>
<!-- 微信登录 -->
<template v-else-if="mode == 'wx'">
<cl-button
type="primary"
fill
:height="90"
:font-size="30"
:loading="loading"
@tap="wxLogin"
>
微信一键登录
</cl-button>
</template>
<!-- 协议 -->
<view class="agree">
<agree-btn :ref="setRefs('agreeBtn')" />
</view>
</view>
</div>
<!-- 其他登录方式 -->
<view class="other" v-if="!isEmpty(platformsEnv)">
<cl-divider width="400rpx" background-color="#ffffff">
<cl-text color="#ccc" value="其他登录方式" />
</cl-divider>
<view class="platform">
<view
class="platform__item"
v-for="(item, index) in platformsEnv"
:key="index"
@tap="changeMode(item)"
>
<image :src="item.icon" mode="aspectFit" v-if="item.icon" />
<text>{{ item.label }}</text>
</view>
</view>
</view>
</view>
</cl-page>
</template>
<script lang="ts" setup>
import { computed, reactive, ref } from "vue";
import { onReady } from "@dcloudio/uni-app";
import { useApp, useCool, useStore, useWx } from "/@/cool";
import { useUi } from "/$/cool-ui";
import SmsBtn from "/@/components/sms-btn.vue";
import AgreeBtn from "/@/components/agree-btn.vue";
import { config } from "/@/config";
import { ctx } from "virtual:ctx";
import { cloneDeep, isEmpty } from "lodash-es";
interface Platform {
label: string;
value: any;
icon?: string;
hidden?: boolean;
onClick?: () => void;
}
const { service, router, refs, setRefs, storage } = useCool();
const { user } = useStore();
const app = useApp();
const ui = useUi();
const wx = useWx();
// 登录中
const loading = ref(false);
// 手机号
const phone = ref(storage.get("phone") || "");
// 登录方式
const mode = ref();
// 登录平台
const platforms = ref<Platform[]>([
{
label: "通过手机登录",
value: "phone",
icon: "/pages/user/static/icon/phone.png",
hidden: false,
},
{
label: "通过微信登录",
value: "wx",
icon: "/pages/user/static/icon/wx.png",
hidden: true,
},
]);
// 环境校验
const platformsEnv = computed(() => {
let arr = cloneDeep(platforms.value);
// #ifdef H5
if (wx.isWxBrowser()) {
arr[1].hidden = false;
}
// #endif
// #ifdef MP-WEIXIN
arr[1].hidden = false;
// #endif
// 过滤隐藏的
arr = arr.filter((e) => !e.hidden);
// 默认第一个登录方式
if (!mode.value) {
mode.value = arr[0]?.value;
}
// 过滤不是当前登录方式
return arr.filter((e) => e.value != mode.value);
});
// 切换模式
function changeMode(item: Platform) {
if (item.onClick) {
item.onClick();
} else {
mode.value = item.value;
}
}
// 登录请求
async function nextLogin(key: "mini" | "mp" | "uniPhone", data: any) {
return service.user.login[key](data)
.then(async (res) => {
// 设置token
user.setToken(res);
// 获取用户信息
await user.get();
// 登录跳转
router.nextLogin(key);
})
.catch((err) => {
ui.showTips(err.message);
});
}
// 短信登录
function phoneLogin(sms?: boolean) {
if (sms) {
check(() => {
refs.smsBtn.open();
});
} else {
storage.set("phone", phone.value);
router.push({
path: "/pages/user/captcha",
query: {
phone: phone.value,
},
});
}
}
// 微信登录
function wxLogin() {
check(async () => {
// #ifdef APP
if (wx.hasApp()) {
wx.appLogin().then((code) => {
//
});
} else {
ui.showConfirm({
title: "温馨提示",
message: "您还未安装微信~",
showCancelButton: false,
confirmButtonText: "去下载",
callback(action) {
if (action == "confirm") {
wx.downloadApp();
}
},
});
}
// #endif
// #ifdef MP-WEIXIN
loading.value = true;
await wx
.miniLogin()
.then(async (res) => {
await nextLogin("mini", res);
})
.catch((err) => {
ui.showToast(err.message);
});
loading.value = false;
// #endif
// #ifdef H5
wx.mpAuth();
// #endif
});
}
// 公众号登录
function mpLogin() {
// #ifdef H5
wx.mpLogin().then(async (code) => {
if (code) {
ui.showLoading();
await nextLogin("mp", { code });
ui.hideLoading();
}
});
// #endif
}
// 协议检测
function check(cb: () => void) {
if (refs.agreeBtn.check()) {
cb();
}
}
// 手机号一键登录
const univerify = reactive({
error: "",
check() {
// #ifdef APP
uni.preLogin({
provider: "univerify",
success() {
platforms.value.push({
label: "手机号一键登录",
value: "univerify",
onClick() {
univerify.login();
},
});
},
fail(err) {
univerify.error = err.metadata?.msg || "当前环境不支持一键登录,请切换至验证码登录";
},
});
// #endif
},
login() {
uni.login({
provider: "univerify",
univerifyStyle: {
authButton: {
normalColor: "#6b69f8",
highlightColor: "#6b69f8",
disabledColor: "#73aaf5",
textColor: "#ffffff",
title: "一键登录",
borderRadius: "12px",
},
privacyTerms: {
defaultCheckBoxState: true, // 条款勾选框初始状态
textColor: "#BBBBBB",
termsColor: "#5496E3",
prefix: "我已阅读并同意",
suffix: "并使用本机号码登录",
privacyItems: [
// 自定义协议条款最大支持2个需要同时设置url和title. 否则不生效
{
url: `${config.baseUrl}/app/base/comm/html?key=userAgreement`,
title: "用户协议",
},
{
url: `${config.baseUrl}/app/base/comm/html?key=privacyPolicy`,
title: "隐私政策",
},
],
},
},
async success(res: { authResult: any }) {
await nextLogin("uniPhone", {
appId: ctx.appid,
...res.authResult,
});
uni.closeAuthView();
},
fail() {
if (univerify.error) {
ui.showToast(univerify.error);
}
},
});
},
});
// 手机号一键登录环境检测
univerify.check();
onReady(() => {
// 公众号登录授权回调
mpLogin();
});
</script>
<style lang="scss" scoped>
.page-login {
.logo {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 20%;
image {
display: block;
height: 150rpx;
width: 150rpx;
border-radius: 24rpx;
margin-bottom: 30rpx;
box-shadow: 0 25rpx 30rpx -25rpx #666666;
}
text {
font-size: 36rpx;
font-weight: bold;
letter-spacing: 1rpx;
color: #333;
}
}
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 80rpx;
.mode {
width: 100%;
padding: 0 80rpx;
box-sizing: border-box;
.label {
display: block;
font-size: 36rpx;
font-weight: 500;
margin-bottom: 30rpx;
}
&.is-phone {
.phone {
display: flex;
align-items: center;
background-color: #eeeeee;
border-radius: 10rpx;
height: 90rpx;
margin-bottom: 30rpx;
font-size: 30rpx;
text {
display: inline-block;
padding: 0 40rpx;
border-right: $cl-border-width solid $cl-border-color;
font-weight: bold;
color: #404040;
}
input {
height: 100%;
flex: 1;
padding: 0 30rpx;
}
}
}
}
.agree {
text-align: center;
margin: 50rpx -60rpx 0 0;
}
}
.other {
margin-top: 100rpx;
.platform {
display: flex;
flex-direction: column;
align-items: center;
margin: 20rpx 0 60rpx 0;
&__item {
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #000000;
height: 30px;
width: 130px;
margin-bottom: 28rpx;
border-radius: 6px;
background-color: #ffffff;
image {
height: 32rpx;
width: 32rpx;
margin-right: 10rpx;
}
text {
font-size: 24rpx;
color: #000000;
}
}
}
}
}
</style>