这篇笔记主要记录uni-app小程序,获取微信用户的头像和用户名,并且使用云存储把获取的头像和用户名保存起来
- 获取微信用户的头像和用户名
- 使用云存储把获取的头像和用户名保存起来(我用的是是阿里云)
- 自定义云存储路径
获取微信头像
看下小程序文档,参考小程序头像呢称
需要将 button 组件
open-type
的值设置为chooseAvatar
,当用户选择需要使用的头像之后,可以通过bindchooseavatar
事件回调获取到头像信息的临时路径
<button
class="avatar-wrapper"
open-type="chooseAvatar"
@chooseavatar="onChooseAvatar">
<image class="avatar" :src="avatarUrl" mode="aspectFill" />
</button>
<script setup>
// 获取微信头像
const onChooseAvatar = (e: any) => {
console.log('选择的头像', e)
}
</script>
返回的头像在detail里, avatarUrl
detail: {
avatarUrl: "http://tmp/nmy0ZYYM897Q73de4ee7436541d541c504878398ef78.jpeg"
}
使用云存储把获取的头像保存起来
上面获取的路径http://tmp/....jpeg
, 是临时路径,并且它仅在当前用户会话中有效。为了确保头像路径长期有效,我们需要把头像上传到自己的服务器或云存储
我们看下云存储文档,
我们要用的是uniCloud.uploadFile, 不是uni.uploadFile, 我刚开始用的是uni.uploadFile,导致一直上传不成功
uniCloud.uploadFile接受cloudPath, 当cloudPathAsRealPath: true
的时候,可以自定义路径,这里我是avatar/${fileName}
, 我把用户头像都放到avatar文件夹;
如果不加路径,会默认都存到cloudstorage文件夹, 我把微信返回的临时路径截取文件名
// 提取文件名(包含扩展名)
const fileName = tempFilePath.substring(tempFilePath.lastIndexOf("/") + 1);
下面是完整代码,使用云存储成功后,我把头像放到用户信息的avatar字段,并且存到storage里,这样可以不用调用查询用户接口就可以回显
// 获取微信头像
const onChooseAvatar = (e: any) => {
const tempFilePath = e.detail.avatarUrl; // 获取临时路径
userInfo.avatar = e.detail.avatarUrl;
console.log("临时路径:", tempFilePath);
// 提取文件名(包含扩展名)
const fileName = tempFilePath.substring(tempFilePath.lastIndexOf("/") + 1);
// 上传头像到服务器
uniCloud.uploadFile({
filePath: tempFilePath, // 本地临时文件路径
cloudPath: `avatar/${fileName}`, // 文件存储路径
cloudPathAsRealPath: true, // 使用 cloudPath 作为绝对路径
success: (uploadRes) => {
console.log("uploadRes.data", uploadRes);
// 确保上传成功后处理
if (uploadRes.success) {
const avatar = uploadRes.fileID; // 服务器返回的文件路径
const existingUserInfo = uni.getStorageSync("userInfo") || {};
uni.setStorageSync("userInfo", { ...existingUserInfo, avatar });
// 查询是否已经有该用户的 avatar 数据
collection
.doc(userInfo.userId)
.update({ avatar })
.then((updateRes) => {
console.log("头像更新成功:", updateRes);
})
.catch((err) => {
console.error("头像更新失败:", err);
});
}
},
fail: (err) => {
console.error("上传失败:", err);
},
});
};
我把上传到头像放到avatar文件夹
获取微信昵称
利用type=nickname
的input, 就可以触发获取昵称
<script setup>
const onNameChange = (e) => {
userInfo.nickname = e.detail.value;
console.log("临时路径:", userInfo.nickname);
};
</script>
<template>
<input
v-model="userInfo.nickname"
class="weui-input mb-5"
@blur="onNameChange"
type="nickname"
placeholder="请输入昵称" />
</template>
返回的用户名在detail.value里
使用云存储把微信用户名存起来
逻辑和云存储微信头像一样,拿到呢称,查询到当前用户,更新昵称
const db = uniCloud.database();
const collection = db.collection("users");
const onNameChange = (e) => {
userInfo.nickname = e.detail.value;
console.log("微信返回的用户名:", e);
const existingUserInfo = uni.getStorageSync("userInfo") || {};
uni.setStorageSync("userInfo", {
...existingUserInfo,
nickname: e.detail.value,
});
// 查询是否已经有该用户的 avatar 数据
collection
.doc(userInfo.userId)
.update({ nickname: e.detail.value })
.then((updateRes) => {
console.log("名称更新成功:", updateRes);
})
.catch((err) => {
console.error("名称更新失败:", err);
});
};
从基础库2.24.4版本起,在
onBlur
事件触发时,微信将异步对用户输入的内容进行安全监测,若未通过安全监测,微信将清空用户输入的内容,建议开发者通过 form 中form-type
为submit
的button 组件收集用户输入的内容。
这里为了简便,我直接在onBlur事件收集用户的用户名, 不是微信官方推荐的通过 form 中form-type
为submit
的button 组件
上面代码更新用户名,头像,昵称的时候,.doc(userInfo.userId)
, 这个userId
,是怎么获取的呢, 我这里通过用户手机号去查询,如果用户没登录,没有手机号就点击获取头像,我会跳到登录页,拿到手机号
<script setup>
const getUser = () => {
// 查询是否已经有该用户的 avatar 数据
collection
.where({ mobile: userInfo.mobile })
.field("mobile")
.get()
.then((queryRes) => {
console.log("queryRes----", queryRes);
if (queryRes.result.errCode === 0 && queryRes.result.data.length) {
// userInfo.avatar = queryRes.result.data[0].avatar;
userInfo.userId = queryRes.result.data[0]._id;
}
});
};
onMounted(() => {
const storedUserInfo = uni.getStorageSync("userInfo");
if (storedUserInfo && storedUserInfo.mobile) {
userInfo.mobile = storedUserInfo.mobile.replace(
/(\d{3})\d{4}(\d{4})/,
"$1****$2"
); // 直接更新 mobile 字段
isLoggedIn.value = true;
getUser();
}
if (storedUserInfo && storedUserInfo.avatar) {
userInfo.avatar = storedUserInfo.avatar;
}
if (storedUserInfo && storedUserInfo.nickname) {
userInfo.nickname = storedUserInfo.nickname;
} else {
userInfo.nickname = "请输入昵称";
}
});
</script>
数据库里的用户昵称、avatar已经更新
开篇截图截图的完整代码
<template>
<view class="me-container">
<!-- 用户信息部分 -->
<view class="user-info">
<view class="left-section">
<button
class="avatar-wrapper"
open-type="chooseAvatar"
v-if="isLoggedIn"
@chooseavatar="onChooseAvatar">
<image class="avatar" :src="userInfo.avatar" mode="aspectFill" />
</button>
<view class="flex flex-col">
<button v-if="!isLoggedIn" class="login-btn" @click="handleLogin">
登录/注册
</button>
<input
v-model="userInfo.nickname"
class="weui-input mb-5"
v-else
@blur="onNameChange"
type="nickname"
placeholder="请输入昵称" />
<view v-if="isLoggedIn">{{ encryptMobile }}</view>
</view>
</view>
</view>
<!-- 功能卡片 -->
<view class="card">
<view
v-for="(item, index) in menuItems"
:key="index"
class="card-item"
@click="navigateTo(item.path)">
<uni-icons :type="item.icon" size="30"></uni-icons>
<text>{{ item.label }}</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from "vue";
const db = uniCloud.database();
const collection = db.collection("users");
// 登录状态
const isLoggedIn = ref(false);
const userInfo = reactive({
mobile: "",
avatar: "/static/images/defaultAvatar.jpeg", // 默认头像
userId: "",
nickname: "未登录",
});
// 获取用户电话获取用户id
// 获取用户信息的方法
const getUser = async () => {
try {
const queryRes = await collection
.where({ mobile: userInfo.mobile })
.field("mobile")
.get();
console.log("queryRes----", queryRes);
if (queryRes.result.errCode === 0 && queryRes.result.data.length) {
userInfo.userId = queryRes.result.data[0]._id;
console.log("获取到的 userId:", userInfo.userId);
return true;
} else {
console.warn("未查询到用户信息");
return false;
}
} catch (error) {
console.error("查询用户信息失败:", error);
return false;
}
};
const onChooseAvatar = async (e: any) => {
const tempFilePath = e.detail.avatarUrl; // 获取临时路径
userInfo.avatar = e.detail.avatarUrl;
console.log("临时路径:", tempFilePath);
// 提取文件名(包含扩展名)
const fileName = tempFilePath.substring(tempFilePath.lastIndexOf("/") + 1);
// 判断 userId 是否存在
if (!userInfo.userId) {
console.error("无法获取用户信息,无法上传头像");
return;
}
// 上传头像到服务器
uniCloud.uploadFile({
filePath: tempFilePath, // 本地临时文件路径
cloudPath: `avatar/${fileName}`, // 文件存储路径
cloudPathAsRealPath: true, // 使用 cloudPath 作为绝对路径
success: (uploadRes) => {
console.log("uploadRes.data", uploadRes);
// 确保上传成功后处理
if (uploadRes.success) {
const avatar = uploadRes.fileID; // 服务器返回的文件路径
const existingUserInfo = uni.getStorageSync("userInfo") || {};
uni.setStorageSync("userInfo", { ...existingUserInfo, avatar });
// 查询是否已经有该用户的 avatar 数据
collection
.doc(userInfo.userId)
.update({ avatar })
.then((updateRes) => {
console.log("头像更新成功:", updateRes);
})
.catch((err) => {
console.error("头像更新失败:", err);
});
}
},
fail: (err) => {
console.error("上传失败:", err);
},
});
};
const menuItems = ref([
{ label: "已约", path: "my-reservations", icon: "calendar" },
{ label: "上课记录", path: "course-records", icon: "info" },
{ label: "会员卡", path: "membership-cards", icon: "wallet" },
{
label: "课程表",
path: "/pages-courses/courseList/courseList",
icon: "wallet",
},
{
label: "联系客服",
path: "",
icon: "chat",
action: "contactCustomerService",
},
{ label: "设置", path: "settings", icon: "gear" },
{ label: "意见反馈", path: "feedback", icon: "mail-open" },
{ label: "退出登录", path: "", icon: "mail-open" },
]);
const encryptMobile = computed(() => {
return userInfo.mobile.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
});
const navigateTo = (path: string) => {
if (path) {
uni.navigateTo({
url: path,
});
} else {
logout();
}
};
// 跳转到登录页
const handleLogin = () => {
uni.navigateTo({
url: `../login/login`,
});
console.log("用户昵称:", userInfo.nickname);
};
const goLoginPage = () => {
if (!isLoggedIn.value) {
uni.showToast({
title: "未登录",
icon: "none",
});
return true;
}
return false;
};
const logout = () => {
console.log("logout");
if (goLoginPage()) return;
uni.showModal({
title: "是否确认退出",
success: (res) => {
console.log(res);
if (res.confirm) {
uni.clearStorageSync();
}
},
});
};
const onNameChange = async (e) => {
userInfo.nickname = e.detail.value;
console.log("微信返回的用户名:", e, userInfo.userId);
// 判断 userId 是否存在
if (!userInfo.userId) {
console.error("无法获取用户信息,无法更新昵称");
return;
}
const existingUserInfo = uni.getStorageSync("userInfo") || {};
uni.setStorageSync("userInfo", {
...existingUserInfo,
nickname: e.detail.value,
});
// 更新用户昵称
collection
.doc(userInfo.userId)
.update({ nickname: e.detail.value })
.then((updateRes) => {
console.log("名称更新成功:", updateRes);
})
.catch((err) => {
console.error("名称更新失败:", err);
});
};
onMounted(async () => {
const storedUserInfo = uni.getStorageSync("userInfo");
// 回显用户电话 头像 昵称
console.log("storedUserInfo", storedUserInfo);
if (storedUserInfo && storedUserInfo.avatar) {
userInfo.avatar = storedUserInfo.avatar;
}
if (storedUserInfo && storedUserInfo.nickname) {
userInfo.nickname = storedUserInfo.nickname;
} else {
userInfo.nickname = "请输入昵称";
}
if (storedUserInfo && storedUserInfo.mobile) {
userInfo.mobile = storedUserInfo.mobile; // 直接更新 mobile 字段
isLoggedIn.value = true;
const userFetched = await getUser();
if (!userFetched) {
console.error("用户信息获取失败");
}
}
});
</script>
<style>
button::after {
border: none !important;
box-shadow: none !important;
}
</style>
<style scoped lang="scss">
.me-container {
padding: 70rpx 20rpx 30rpx;
.user-info {
display: flex;
align-items: center;
justify-content: space-between; /* Left section (avatar, nickname) on the left and login button on the right */
margin-bottom: 40rpx;
}
.left-section {
display: flex;
align-items: center;
}
.avatar-wrapper {
display: flex;
justify-content: center;
align-items: center;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
overflow: hidden;
margin-right: 20rpx;
padding-left: 0rpx;
padding-right: 0rpx;
}
.avatar {
width: 100%;
height: 100%;
}
.avatar::after {
border: 1px solid transparent !important;
}
uni-button:after {
border: 1px solid transparent !important;
}
.weui-input {
width: 280rpx;
padding: 10rpx 0rpx;
font-size: 32rpx;
border-radius: 8rpx;
}
.login-btn {
background-color: rgb(116, 219, 239);
color: #fff;
border-radius: 8rpx;
margin-right: 5rpx;
}
.card {
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
padding: 20rpx;
display: flex;
// justify-content: space-between;
flex-wrap: wrap;
margin-top: 60rpx;
.card-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20rpx 0;
width: 33%;
}
.card-item:last-child {
border-bottom: none;
}
.arrow-icon {
width: 20rpx;
height: 20rpx;
}
/* CSS Arrow Right */
.arrow-right {
width: 0;
height: 0;
border-top: 10rpx solid transparent;
border-bottom: 10rpx solid transparent;
border-left: 10rpx solid #ccc;
}
}
}
</style>