添加登录功能
This commit is contained in:
32
src/apis.js
32
src/apis.js
@@ -57,4 +57,34 @@ export const getProvinceData = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取省份及下属城市列表
|
||||||
|
export const loginAPI = (nickName, avatarUrl, code) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: `${BASE_URL}/index/code`,
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
appName: "shoot",
|
||||||
|
appId: "wxa8f5989dcd45cc23",
|
||||||
|
nickName,
|
||||||
|
avatarUrl,
|
||||||
|
code,
|
||||||
|
},
|
||||||
|
success: (res) => {
|
||||||
|
const { code, data } = res.data;
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
reject(err);
|
||||||
|
uni.showToast({
|
||||||
|
title: "获取数据失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view
|
<view
|
||||||
class="container"
|
class="sbtn"
|
||||||
:style="{ width: width, borderRadius: rounded + 'px' }"
|
:style="{ width: width, borderRadius: rounded + 'px' }"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
@@ -26,7 +26,7 @@ const props = defineProps({
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.container {
|
.sbtn {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
@@ -34,5 +34,8 @@ const props = defineProps({
|
|||||||
background-color: #fed847;
|
background-color: #fed847;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from "vue";
|
import { computed } from "vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
showRank: {
|
showRank: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
});
|
user: {
|
||||||
const userInfo = ref({
|
type: Object,
|
||||||
name: "打酱油·路过",
|
default: () => ({
|
||||||
level: "L100",
|
nickName:'',
|
||||||
rank: 1928,
|
lvl:0,
|
||||||
rankTotal: 1320,
|
points:0,
|
||||||
|
rankLvl:0,
|
||||||
|
lvlPoints:0,
|
||||||
|
}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const containerWidth = computed(() => (props.showRank ? "72vw" : "100vw"));
|
const containerWidth = computed(() => (props.showRank ? "72vw" : "100vw"));
|
||||||
const toUserPage = () => {
|
const toUserPage = () => {
|
||||||
// 获取当前页面路径
|
// 获取当前页面路径
|
||||||
const pages = getCurrentPages();
|
const pages = getCurrentPages();
|
||||||
const currentPage = pages[pages.length - 1];
|
const currentPage = pages[pages.length - 1];
|
||||||
|
|
||||||
// 如果当前不是用户页面才进行跳转
|
// 如果当前不是用户页面才进行跳转
|
||||||
if (currentPage.route !== 'pages/user') {
|
if (currentPage.route !== "pages/user") {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: "/pages/user",
|
url: "/pages/user",
|
||||||
});
|
});
|
||||||
@@ -35,15 +39,15 @@ const toUserPage = () => {
|
|||||||
</view>
|
</view>
|
||||||
<view class="user-details">
|
<view class="user-details">
|
||||||
<view class="user-name">
|
<view class="user-name">
|
||||||
<text>{{ userInfo.name }}</text>
|
<text>{{ user.nickName }}</text>
|
||||||
<image src="../static/vip1.png" mode="widthFix" />
|
<image src="../static/vip1.png" mode="widthFix" />
|
||||||
</view>
|
</view>
|
||||||
<view class="user-stats">
|
<view class="user-stats">
|
||||||
<text class="level-tag">钻石1级</text>
|
<text class="level-tag">钻石1级</text>
|
||||||
<text class="level-tag">{{ userInfo.level }}</text>
|
<text class="level-tag">{{ user.lvl }}</text>
|
||||||
<view class="rank-tag">
|
<view class="rank-tag">
|
||||||
<view :style="{ width: '40%' }" />
|
<view :style="{ width: '40%' }" />
|
||||||
<text>158/1928</text>
|
<text>{{ user.lvl }}/{{ user.lvlPoints }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -52,7 +56,7 @@ const toUserPage = () => {
|
|||||||
<text>本赛季全国</text>
|
<text>本赛季全国</text>
|
||||||
<text class="rank-number"
|
<text class="rank-number"
|
||||||
>第<text :style="{ color: '#ffd700' }"
|
>第<text :style="{ color: '#ffd700' }"
|
||||||
>{{ userInfo.rank }}/{{ userInfo.rankTotal }}</text
|
>{{ user.points }}/{{ user.rankLvl }}</text
|
||||||
>名</text
|
>名</text
|
||||||
>
|
>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -6,6 +6,12 @@
|
|||||||
"navigationBarTitleText": "首页"
|
"navigationBarTitleText": "首页"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/login",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "登录"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/user",
|
"path": "pages/user",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@@ -1,85 +1,22 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import AppFooter from "@/components/AppFooter.vue";
|
import AppFooter from "@/components/AppFooter.vue";
|
||||||
import AppBackground from "@/components/AppBackground.vue";
|
import AppBackground from "@/components/AppBackground.vue";
|
||||||
import UserHeader from "@/components/UserHeader.vue";
|
import UserHeader from "@/components/UserHeader.vue";
|
||||||
import { getAppConfig } from "@/apis";
|
import { getAppConfig } from "@/apis";
|
||||||
|
import useStore from "@/store";
|
||||||
// import useStore from "@/store";
|
|
||||||
|
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
// const store = useStore();
|
const store = useStore();
|
||||||
// 使用actions方法
|
|
||||||
// const { updateUsername } = store;
|
|
||||||
// 使用storeToRefs,用于UI里显示,保持响应性
|
// 使用storeToRefs,用于UI里显示,保持响应性
|
||||||
// const { user } = storeToRefs(store);
|
const { user } = storeToRefs(store);
|
||||||
|
|
||||||
// 添加登录状态和用户信息
|
const toLoginPage = () => {
|
||||||
const isLogin = ref(true);
|
uni.navigateTo({
|
||||||
const userInfo = ref({
|
url: "/pages/login",
|
||||||
name: "",
|
|
||||||
avatarUrl: "",
|
|
||||||
level: "L1",
|
|
||||||
rank: 0,
|
|
||||||
rankTotal: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 检查登录状态
|
|
||||||
const checkLogin = () => {
|
|
||||||
const storedUserInfo = uni.getStorageSync("userInfo");
|
|
||||||
if (storedUserInfo) {
|
|
||||||
userInfo.value = JSON.parse(storedUserInfo);
|
|
||||||
isLogin.value = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理微信登录
|
|
||||||
const handleLogin = () => {
|
|
||||||
// console.log("getUsername", store.getUsername);
|
|
||||||
// updateUsername("we0f9we9f08");
|
|
||||||
uni.getUserProfile({
|
|
||||||
desc: "用于完善用户资料",
|
|
||||||
success: (res) => {
|
|
||||||
const { userInfo: wxUserInfo } = res;
|
|
||||||
console.log("wxUserInfo", wxUserInfo);
|
|
||||||
// 获取登录凭证
|
|
||||||
uni.login({
|
|
||||||
provider: "weixin",
|
|
||||||
success: async (loginRes) => {
|
|
||||||
const { code } = loginRes;
|
|
||||||
console.log("loginRes", loginRes);
|
|
||||||
// 这里可以把 code 和用户信息发送到后端
|
|
||||||
// const result = await loginAPI(code, wxUserInfo);
|
|
||||||
// 暂时直接使用微信返回的用户信息
|
|
||||||
userInfo.value = {
|
|
||||||
name: wxUserInfo.nickName,
|
|
||||||
avatarUrl: wxUserInfo.avatarUrl,
|
|
||||||
level: "L1",
|
|
||||||
rank: 0,
|
|
||||||
rankTotal: 0,
|
|
||||||
};
|
|
||||||
isLogin.value = true;
|
|
||||||
|
|
||||||
// 保存到本地存储
|
|
||||||
uni.setStorageSync("userInfo", JSON.stringify(userInfo.value));
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
uni.showToast({
|
|
||||||
title: "登录失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
console.error("登录失败:", err);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.log("获取用户信息失败:", err);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const toFristTryPage = () => {
|
const toFristTryPage = () => {
|
||||||
console.log("username", username);
|
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: "/pages/first-try",
|
url: "/pages/first-try",
|
||||||
});
|
});
|
||||||
@@ -128,11 +65,11 @@ onMounted(() => {
|
|||||||
<view class="root-container">
|
<view class="root-container">
|
||||||
<AppBackground />
|
<AppBackground />
|
||||||
<!-- 根据登录状态显示用户信息或登录按钮 -->
|
<!-- 根据登录状态显示用户信息或登录按钮 -->
|
||||||
<block v-if="isLogin">
|
<block v-if="user.id">
|
||||||
<UserHeader :userInfo="userInfo" showRank />
|
<UserHeader :user="user" showRank />
|
||||||
</block>
|
</block>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view class="login-btn" @click="handleLogin">
|
<view @click="toLoginPage">
|
||||||
<text>微信登录</text>
|
<text>微信登录</text>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
@@ -418,16 +355,4 @@ onMounted(() => {
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.login-btn {
|
|
||||||
margin: 20px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background-color: #07c160;
|
|
||||||
border-radius: 4px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-btn text {
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
123
src/pages/login.vue
Normal file
123
src/pages/login.vue
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import Header from "@/components/Header.vue";
|
||||||
|
import SButton from "@/components/SButton.vue";
|
||||||
|
import useStore from "@/store";
|
||||||
|
import { loginAPI } from "@/apis";
|
||||||
|
const store = useStore();
|
||||||
|
// 使用actions方法
|
||||||
|
const { updateUser, updateToken } = store;
|
||||||
|
const agree = ref(false);
|
||||||
|
|
||||||
|
const handleAgree = () => {
|
||||||
|
agree.value = !agree.value;
|
||||||
|
};
|
||||||
|
const handleLogin = () => {
|
||||||
|
if (!agree.value) {
|
||||||
|
return uni.showToast({
|
||||||
|
title: "请先同意协议",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// console.log("getUsername", store.getUsername);
|
||||||
|
uni.getUserProfile({
|
||||||
|
desc: "用于完善用户资料",
|
||||||
|
success: (res) => {
|
||||||
|
const { nickName, avatarUrl } = res.userInfo;
|
||||||
|
// 获取登录凭证
|
||||||
|
uni.login({
|
||||||
|
provider: "weixin",
|
||||||
|
success: async (loginRes) => {
|
||||||
|
const { code } = loginRes;
|
||||||
|
const result = await loginAPI(nickName, avatarUrl, code);
|
||||||
|
updateUser(result.user);
|
||||||
|
updateToken(result.token, result.expires);
|
||||||
|
uni.navigateBack();
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
uni.showToast({
|
||||||
|
title: "登录失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
console.error("登录失败:", err);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.log("获取用户信息失败:", err);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<image src="../static/login-bg.png" mode="widthFix" />
|
||||||
|
<Header />
|
||||||
|
<view>
|
||||||
|
<SButton :rounded="20" width="80vw" :onClick="handleLogin">
|
||||||
|
<image
|
||||||
|
src="../static/wechat-icon.png"
|
||||||
|
mode="widthFix"
|
||||||
|
class="wechat-icon"
|
||||||
|
/>
|
||||||
|
<text>登录/注册</text>
|
||||||
|
</SButton>
|
||||||
|
<view class="protocol" @click="handleAgree">
|
||||||
|
<view v-if="!agree" />
|
||||||
|
<image v-if="agree" src="../static/checked.png" mode="widthFix" />
|
||||||
|
<text
|
||||||
|
>已同意并阅读<text :style="{ color: '#fff' }">《用户协议》</text
|
||||||
|
>及<text :style="{ color: '#fff' }">《隐私协议》</text>内容</text
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.container > image:first-child {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
.container > view:last-child {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 96px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.protocol {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-top: 40px;
|
||||||
|
color: #8a8a8a;
|
||||||
|
}
|
||||||
|
.protocol > image {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.protocol > view:first-child {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 10px;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
}
|
||||||
|
.wechat-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
src/static/checked.png
Normal file
BIN
src/static/checked.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/static/login-bg.png
Normal file
BIN
src/static/login-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 570 KiB |
BIN
src/static/wechat-icon.png
Normal file
BIN
src/static/wechat-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
17
src/store.js
17
src/store.js
@@ -5,7 +5,12 @@ export default defineStore("store", {
|
|||||||
// 状态
|
// 状态
|
||||||
state: () => ({
|
state: () => ({
|
||||||
user: {
|
user: {
|
||||||
username: "游客",
|
id: "",
|
||||||
|
nickName: "游客",
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
token: "",
|
||||||
|
tokenExpire: 0,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -18,8 +23,12 @@ export default defineStore("store", {
|
|||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
actions: {
|
actions: {
|
||||||
updateUsername(newUsername) {
|
updateUser(user) {
|
||||||
this.user.username = newUsername;
|
this.user = user;
|
||||||
|
},
|
||||||
|
updateToken(token, expire) {
|
||||||
|
this.request.token = token;
|
||||||
|
this.request.tokenExpire = Date.now() + expire;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -29,7 +38,7 @@ export default defineStore("store", {
|
|||||||
strategies: [
|
strategies: [
|
||||||
{
|
{
|
||||||
storage: uni.getStorageSync,
|
storage: uni.getStorageSync,
|
||||||
paths: ["user"], // 只持久化用户信息
|
paths: ["user", "request"], // 只持久化用户信息
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user