|
|
@@ -7,7 +7,7 @@
|
|
|
<view class="user-item-label">头像</view>
|
|
|
<view class="user-item-value">
|
|
|
<!-- #ifdef MP-WEIXIN -->
|
|
|
- <button class="avatar-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
|
|
|
+ <button class="avatar-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar" :disabled="uploadingAvatar">
|
|
|
<view class="avatar-container">
|
|
|
<image
|
|
|
:src="formData.avatar || getFileUrl('img/personal/userDemo.png')"
|
|
|
@@ -94,6 +94,9 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
+ <view class="submit-wrap">
|
|
|
+ <view class="submit-btn hover-active" @click="handleConfirm">确定</view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
@@ -102,8 +105,27 @@ import { ref, onMounted } from 'vue';
|
|
|
import { useUserStore } from '@/store/user.js';
|
|
|
import { getFileUrl } from '@/utils/file.js';
|
|
|
import { go } from '@/utils/utils.js';
|
|
|
+import { PostUpdateProfile, uploadFileToServer } from '@/api/dining.js';
|
|
|
+import { UPLOAD } from '@/settings/siteSetting.js';
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
+const uploadingAvatar = ref(false);
|
|
|
+
|
|
|
+// 选择完头像后直接上传到 /file/upload,返回完整图片 URL
|
|
|
+async function doUploadAvatar(tempPath) {
|
|
|
+ const res = await uploadFileToServer(tempPath);
|
|
|
+ if (!res || typeof res !== 'string') return '';
|
|
|
+ const p = res.trim();
|
|
|
+ if (/^https?:\/\//i.test(p)) return p;
|
|
|
+ const base = (UPLOAD || '').replace(/\/$/, '');
|
|
|
+ return base ? base + p.replace(/^\//, '') : p;
|
|
|
+}
|
|
|
+
|
|
|
+function isTempAvatarPath(url) {
|
|
|
+ if (!url || typeof url !== 'string') return false;
|
|
|
+ const u = url.trim();
|
|
|
+ return !/^https?:\/\//i.test(u) || /127\.0\.0\.1|localhost|\/tmp\/|wxfile:\/\//i.test(u);
|
|
|
+}
|
|
|
|
|
|
// 性别选项
|
|
|
const genderOptions = [
|
|
|
@@ -111,27 +133,29 @@ const genderOptions = [
|
|
|
{ value: 'female', label: '女' }
|
|
|
];
|
|
|
|
|
|
-// 表单数据
|
|
|
+// 表单数据(userId 用于更新资料接口必填)
|
|
|
const formData = ref({
|
|
|
+ userId: null,
|
|
|
avatar: '',
|
|
|
nickname: '',
|
|
|
- gender: 'male', // 默认选中男
|
|
|
+ gender: 'male',
|
|
|
birthday: '',
|
|
|
phone: '1510987760'
|
|
|
});
|
|
|
|
|
|
-// 初始化用户信息
|
|
|
+// 初始化用户信息(头像用 avatarUrl/avatar,与缓存字段一致)
|
|
|
onMounted(() => {
|
|
|
- const userInfo = userStore.getUserInfo;
|
|
|
- if (userInfo) {
|
|
|
- formData.value = {
|
|
|
- avatar: userInfo.avatar || '',
|
|
|
- nickname: userInfo.nickname || '',
|
|
|
- gender: userInfo.gender || 'male',
|
|
|
- birthday: userInfo.birthday || '',
|
|
|
- phone: userInfo.phone || '1510987760'
|
|
|
- };
|
|
|
- }
|
|
|
+ const userInfo = userStore.getUserInfo || {};
|
|
|
+ const g = userInfo.gender;
|
|
|
+ const genderVal = g === '女' || g === 'female' ? 'female' : 'male';
|
|
|
+ formData.value = {
|
|
|
+ userId: userInfo.id ?? userInfo.userId ?? null,
|
|
|
+ avatar: userInfo.avatarUrl ?? userInfo.avatar ?? '',
|
|
|
+ nickname: userInfo.nickName ?? userInfo.nickname ?? '',
|
|
|
+ gender: genderVal,
|
|
|
+ birthday: userInfo.birthday ?? '',
|
|
|
+ phone: userInfo.phone ?? userInfo.mobile ?? '1510987760'
|
|
|
+ };
|
|
|
});
|
|
|
|
|
|
// 处理头像点击
|
|
|
@@ -156,19 +180,32 @@ const handleAvatarClick = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-// 微信小程序:通过「头像昵称填写」能力选择头像(button open-type="chooseAvatar" 触发)
|
|
|
-// 注意:e.detail.avatarUrl 为临时路径,需上传到服务器后保存永久链接
|
|
|
-const onChooseAvatar = (e) => {
|
|
|
- if (e.detail && e.detail.avatarUrl) {
|
|
|
- formData.value.avatar = e.detail.avatarUrl;
|
|
|
- uni.showToast({ title: '头像已选择', icon: 'success' });
|
|
|
+// 选择头像后直接上传到服务器接口 /file/upload,把返回的 url 写入表单
|
|
|
+async function uploadAvatarAndSet(tempPath) {
|
|
|
+ if (!tempPath) return;
|
|
|
+ try {
|
|
|
+ uploadingAvatar.value = true;
|
|
|
+ const url = await doUploadAvatar(tempPath);
|
|
|
+ formData.value.avatar = url || tempPath;
|
|
|
+ uni.showToast({ title: url ? '头像上传成功' : '头像上传未返回地址', icon: url ? 'success' : 'none' });
|
|
|
+ } catch (err) {
|
|
|
+ uni.showToast({ title: err?.message || '头像上传失败', icon: 'none' });
|
|
|
+ formData.value.avatar = tempPath;
|
|
|
+ } finally {
|
|
|
+ uploadingAvatar.value = false;
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+// 微信小程序:选择头像后立即上传,防止重复点击导致 another chooseAvatar is in progress
|
|
|
+const onChooseAvatar = async (e) => {
|
|
|
+ if (uploadingAvatar.value) return;
|
|
|
+ const tempPath = e.detail?.avatarUrl;
|
|
|
+ if (tempPath) await uploadAvatarAndSet(tempPath);
|
|
|
};
|
|
|
|
|
|
// 获取微信头像(仅非 MP-WEIXIN 时从 actionSheet 进入;MP-WEIXIN 已改用模板内 button chooseAvatar)
|
|
|
const handleWechatAvatar = () => {
|
|
|
// #ifdef MP-WEIXIN
|
|
|
- // 微信已废弃 getUserProfile,请直接点击页面头像区域,使用「选择头像」按钮
|
|
|
uni.showToast({
|
|
|
title: '请点击上方头像区域选择',
|
|
|
icon: 'none'
|
|
|
@@ -182,17 +219,14 @@ const handleWechatAvatar = () => {
|
|
|
// #endif
|
|
|
};
|
|
|
|
|
|
-// 选择相册
|
|
|
+// 选择相册:选择后立即上传
|
|
|
const handleChooseAlbum = () => {
|
|
|
uni.chooseImage({
|
|
|
count: 1,
|
|
|
sizeType: ['original', 'compressed'],
|
|
|
sourceType: ['album'],
|
|
|
- success: (res) => {
|
|
|
- const tempFilePath = res.tempFilePaths[0];
|
|
|
- // 这里可以调用上传接口
|
|
|
- // 暂时直接使用本地路径
|
|
|
- formData.value.avatar = tempFilePath;
|
|
|
+ success: async (res) => {
|
|
|
+ await uploadAvatarAndSet(res.tempFilePaths[0]);
|
|
|
},
|
|
|
fail: (err) => {
|
|
|
console.log('选择相册失败', err);
|
|
|
@@ -200,17 +234,14 @@ const handleChooseAlbum = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-// 拍照
|
|
|
+// 拍照:选择后立即上传
|
|
|
const handleTakePhoto = () => {
|
|
|
uni.chooseImage({
|
|
|
count: 1,
|
|
|
sizeType: ['original', 'compressed'],
|
|
|
sourceType: ['camera'],
|
|
|
- success: (res) => {
|
|
|
- const tempFilePath = res.tempFilePaths[0];
|
|
|
- // 这里可以调用上传接口
|
|
|
- // 暂时直接使用本地路径
|
|
|
- formData.value.avatar = tempFilePath;
|
|
|
+ success: async (res) => {
|
|
|
+ await uploadAvatarAndSet(res.tempFilePaths[0]);
|
|
|
},
|
|
|
fail: (err) => {
|
|
|
console.log('拍照失败', err);
|
|
|
@@ -218,6 +249,47 @@ const handleTakePhoto = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+// 确定:先默认调用一次上传头像接口,等待返回后,再把返回的图片 url 传给 dining/user/updateProfile
|
|
|
+const handleConfirm = async () => {
|
|
|
+ const uid = formData.value.userId;
|
|
|
+ if (uid == null || uid === '') {
|
|
|
+ uni.showToast({ title: '请先登录', icon: 'none' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let avatarUrl = formData.value.avatar || '';
|
|
|
+ // 有头像且为本地临时路径时,先调用自己的上传接口,等待返回后再走更新资料
|
|
|
+ if (avatarUrl && isTempAvatarPath(avatarUrl)) {
|
|
|
+ try {
|
|
|
+ avatarUrl = await doUploadAvatar(avatarUrl);
|
|
|
+ if (!avatarUrl) {
|
|
|
+ uni.showToast({ title: '头像上传未返回地址', icon: 'none' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ uni.showToast({ title: err?.message || '头像上传失败', icon: 'none' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const genderStr = formData.value.gender === 'female' ? '女' : '男';
|
|
|
+ const dto = { userId: Number(uid) };
|
|
|
+ if (avatarUrl) dto.avatarUrl = avatarUrl;
|
|
|
+ if (formData.value.nickname) dto.nickName = formData.value.nickname;
|
|
|
+ dto.gender = genderStr;
|
|
|
+ if (formData.value.birthday) dto.birthday = formData.value.birthday;
|
|
|
+ try {
|
|
|
+ await PostUpdateProfile(dto);
|
|
|
+ userStore.setUserInfo({
|
|
|
+ ...userStore.getUserInfo,
|
|
|
+ avatarUrl: avatarUrl || formData.value.avatar,
|
|
|
+ nickName: formData.value.nickname
|
|
|
+ });
|
|
|
+ uni.showToast({ title: '保存成功', icon: 'success' });
|
|
|
+ setTimeout(() => uni.navigateBack(), 1500);
|
|
|
+ } catch (e) {
|
|
|
+ uni.showToast({ title: e?.message || '保存失败', icon: 'none' });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 处理生日选择
|
|
|
const handleBirthdayChange = (e) => {
|
|
|
formData.value.birthday = e.detail.value;
|
|
|
@@ -238,6 +310,22 @@ const handleChangePhone = () => {
|
|
|
padding: 20rpx;
|
|
|
}
|
|
|
|
|
|
+.submit-wrap {
|
|
|
+ margin-top: 60rpx;
|
|
|
+ padding: 0 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.submit-btn {
|
|
|
+ width: 100%;
|
|
|
+ height: 88rpx;
|
|
|
+ line-height: 88rpx;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #fff;
|
|
|
+ background: linear-gradient(90deg, #FCB73F 0%, #FC743D 100%);
|
|
|
+ border-radius: 44rpx;
|
|
|
+}
|
|
|
+
|
|
|
.user-info {
|
|
|
width: 100%;
|
|
|
border-radius: 23rpx;
|
|
|
@@ -281,6 +369,9 @@ const handleChangePhone = () => {
|
|
|
&::after {
|
|
|
border: none;
|
|
|
}
|
|
|
+ &[disabled] {
|
|
|
+ opacity: 0.7;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.avatar-container {
|