Files
shoot-miniprograms/src/components/Signin.vue
2025-09-04 15:42:59 +08:00

244 lines
5.3 KiB
Vue

<script setup>
import { ref } from "vue";
import { onShow } from "@dcloudio/uni-app";
import Avatar from "@/components/Avatar.vue";
import SButton from "@/components/SButton.vue";
import { getMyDevicesAPI, loginAPI, getHomeData } from "@/apis";
import useStore from "@/store";
const store = useStore();
const { updateUser, updateDevice } = store;
const props = defineProps({
onClose: {
type: Function,
default: () => {},
},
});
const agree = ref(false);
const avatarUrl = ref("");
const nickName = ref("");
const loading = ref(false);
const handleAgree = () => {
agree.value = !agree.value;
};
function onChooseAvatar(e) {
avatarUrl.value = e.detail.avatarUrl;
}
function onNicknameChange(e) {
nickName.value = e.detail.value;
}
const handleLogin = () => {
if (loading.value) return;
if (!avatarUrl.value) {
return uni.showToast({
title: "请选择头像",
icon: "none",
});
}
if (!nickName.value) {
return uni.showToast({
title: "请输入昵称",
icon: "none",
});
}
if (!agree.value) {
return uni.showToast({
title: "请先同意协议",
icon: "none",
});
}
loading.value = true;
uni.login({
provider: "weixin",
success: async (loginRes) => {
const { code } = loginRes;
const fileManager = uni.getFileSystemManager();
const avatarBase64 = fileManager.readFileSync(avatarUrl.value, "base64");
const base64Url = `data:image/png;base64,${avatarBase64}`;
const result = await loginAPI(nickName.value, base64Url, code);
const data = await getHomeData();
if (data.user) updateUser(data.user);
const devices = await getMyDevicesAPI();
if (devices.bindings && devices.bindings.length) {
updateDevice(
devices.bindings[0].deviceId,
devices.bindings[0].deviceName
);
}
props.onClose();
},
fail: (err) => {
loading.value = false;
uni.showToast({
title: "登录失败",
icon: "none",
});
console.error("登录失败:", err);
},
});
};
const openServiceLink = () => {
uni.navigateTo({
url:
"/pages/webview?url=" +
encodeURIComponent(
"https://static.shelingxingqiu.com/shootServiceAgreement.html"
),
});
};
const openPrivacyLink = () => {
uni.navigateTo({
url:
"/pages/webview?url=" +
encodeURIComponent(
"https://static.shelingxingqiu.com/shootPrivacyPolicy.html"
),
});
};
onShow(() => {
loading.value = false;
});
</script>
<template>
<view class="container">
<view class="avatar">
<text>头像:</text>
<button
open-type="chooseAvatar"
@chooseavatar="onChooseAvatar"
class="login-btn"
hover-class="none"
>
<Avatar v-if="avatarUrl" :src="avatarUrl" :size="30" />
<text v-else>点击获取</text>
</button>
</view>
<view class="nickname">
<text>昵称:</text>
<input
type="nickname"
placeholder="请输入昵称"
placeholder-style="color: #fff9"
@change="onNicknameChange"
@blur="onNicknameBlur"
/>
</view>
<SButton :rounded="20" width="80vw" :onClick="handleLogin">
<block v-if="!loading">
<image
src="../static/wechat-icon.png"
mode="widthFix"
class="wechat-icon"
/>
<text :style="{ color: '#000' }">登录/注册</text>
</block>
<block v-else>
<image
src="../static/btn-loading.png"
mode="widthFix"
class="loading"
/>
</block>
</SButton>
<view class="protocol" @click="handleAgree">
<view v-if="!agree" />
<image v-if="agree" src="../static/checked.png" mode="widthFix" />
<view>
<text>已同意并阅读</text>
<view @click.stop="openServiceLink">用户协议</view>
<text></text>
<view @click.stop="openPrivacyLink">隐私协议</view>
<text>内容</text>
</view>
</view>
</view>
</template>
<style scoped>
.container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.avatar,
.nickname {
width: 80%;
display: flex;
align-items: center;
margin-bottom: 20px;
border-bottom: 1px solid #fff3;
}
.avatar {
margin: 0;
}
.avatar > text,
.nickname > text {
width: 20%;
color: #fff9;
font-size: 14px;
line-height: 55px;
}
.avatar > button > text {
color: #fff9;
font-size: 14px;
}
.nickname > input {
flex: 1;
font-size: 14px;
color: #fff;
line-height: 55px;
}
.wechat-icon {
width: 24px;
height: 24px;
margin-right: 20px;
}
.protocol {
display: flex;
justify-content: center;
align-items: center;
font-size: 13px;
margin-top: 15px;
color: #8a8a8a;
}
.protocol > image {
width: 16px;
height: 16px;
margin-right: 10px;
}
.protocol > view:first-child {
width: 14px;
height: 14px;
border-radius: 50%;
margin-right: 10px;
border: 1px solid #fff;
}
.protocol > view:last-child {
display: flex;
align-items: center;
}
.protocol > view:last-child > view {
color: #fff;
}
.login-btn {
line-height: 55px;
width: 80%;
display: flex;
}
.loading {
width: 25px;
height: 25px;
background-blend-mode: darken;
animation: rotate 1s linear infinite;
}
</style>