500 lines
13 KiB
Vue
500 lines
13 KiB
Vue
<script setup>
|
|
import {
|
|
ref,
|
|
onMounted,
|
|
getCurrentInstance,
|
|
nextTick
|
|
} from 'vue';
|
|
import {
|
|
onLoad,
|
|
onShow,
|
|
onReady,
|
|
onHide,
|
|
onPullDownRefresh
|
|
} from '@dcloudio/uni-app';
|
|
import CustomTabBar from '@/components/CustomTabBar.vue'
|
|
|
|
|
|
import {
|
|
getUserInfo,updateNickname,uploadAvatar
|
|
} from './api.js';
|
|
import {
|
|
useUserStore
|
|
} from '@/store/user.js';
|
|
|
|
import {
|
|
useI18n
|
|
} from 'vue-i18n';
|
|
|
|
const {
|
|
t,
|
|
locale
|
|
} = useI18n();
|
|
|
|
const props = defineProps(['status']);
|
|
const {
|
|
proxy
|
|
} = getCurrentInstance();
|
|
|
|
const userStore = useUserStore();
|
|
|
|
// 控制登录弹窗显示
|
|
const toggleLoginPopup = () => {
|
|
showLogin.value = !showLogin.value;
|
|
};
|
|
|
|
//用户信息
|
|
const userName = ref();
|
|
|
|
// 设备数据数组
|
|
const deviceList = ref([
|
|
{
|
|
id: 1,
|
|
name: 'Sweekar Alpha',
|
|
level: 'Lv.5 Childhood',
|
|
battery: '92%',
|
|
bluetooth: 'ON',
|
|
isCurrent: true
|
|
},
|
|
{
|
|
id: 2,
|
|
name: 'Little Beta',
|
|
level: 'Lv.1 Infancy',
|
|
battery: '92%',
|
|
bluetooth: 'ON',
|
|
isCurrent: false
|
|
}
|
|
]);
|
|
|
|
/**
|
|
* @desc 处理设备点击事件
|
|
*/
|
|
const handleDeviceClick = (clickedDeviceId) => {
|
|
deviceList.value.forEach(device => {
|
|
device.isCurrent = device.id === clickedDeviceId;
|
|
});
|
|
};
|
|
|
|
const userData = ref();
|
|
//获取首页数据
|
|
const getUserInformation = async () => {
|
|
const res = await getUserInfo();
|
|
|
|
userData.value = res.data;
|
|
userStore.updateUserInfo('nickname', userData.value.nickname);
|
|
console.log('用户名', userStore.userName);
|
|
userName.value = userData.value.nickname;
|
|
|
|
}
|
|
//更改用户名
|
|
const changeName = async(userName) =>{
|
|
const res = await updateNickname(userName);
|
|
if (res.data){
|
|
uni.showToast({
|
|
title:'success',
|
|
duration:2000
|
|
})
|
|
}else {
|
|
uni.showToast({
|
|
title:'fail',
|
|
duration:2000
|
|
})
|
|
}
|
|
}
|
|
|
|
// 选择并上传头像
|
|
const chooseAndUploadAvatar = async () => {
|
|
uni.chooseImage({
|
|
count: 1, // 最多选择一张图片
|
|
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图
|
|
sourceType: ['album', 'camera'], // 从相册选择或拍照
|
|
success: async (res) => {
|
|
const tempFilePaths = res.tempFilePaths;
|
|
if (tempFilePaths.length > 0) {
|
|
const filePath = tempFilePaths[0];
|
|
|
|
// 显示上传中提示
|
|
uni.showLoading({
|
|
title: 'Uploading...'
|
|
});
|
|
|
|
try {
|
|
const uploadRes = await new Promise((resolve, reject) => {
|
|
uni.uploadFile({
|
|
url: 'https://takway-ai.top/test/app-api/member/user/upload-avatar',
|
|
filePath: filePath,
|
|
name: 'avatarFile',
|
|
header: {
|
|
'Authorization': `Bearer ${userStore.token}`
|
|
},
|
|
success: (uploadResult) => {
|
|
try {
|
|
const data = JSON.parse(uploadResult.data);
|
|
resolve(data);
|
|
} catch (e) {
|
|
console.error('Failed to parse upload response:', uploadResult.data);
|
|
reject(new Error(`Failed to parse response: ${uploadResult.data}`));
|
|
}
|
|
},
|
|
fail: (error) => {
|
|
console.error('Upload failed:', error);
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
|
|
if (uploadRes && uploadRes.code === 0) { // 假设成功状态码为0
|
|
// 更新用户数据中的头像URL
|
|
userData.value.avatar = uploadRes.data;
|
|
userStore.updateUserInfo('avatar', uploadRes.data);
|
|
|
|
uni.showToast({
|
|
title: 'Upload success',
|
|
icon: 'success'
|
|
});
|
|
} else {
|
|
uni.showToast({
|
|
title: uploadRes?.message || 'Upload failed',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Upload error:', error);
|
|
uni.showToast({
|
|
title: error?.message || 'Upload failed',
|
|
icon: 'none'
|
|
});
|
|
} finally {
|
|
uni.hideLoading();
|
|
}
|
|
}
|
|
},
|
|
fail: (error) => {
|
|
console.error('Choose image error:', error);
|
|
uni.showToast({
|
|
title: 'Failed to select image',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @desc 跳转至登录页面
|
|
*/
|
|
const toLoginPage = (productId) => {
|
|
userStore.logout()
|
|
uni.navigateTo({
|
|
url: '/pages/login/index',
|
|
});
|
|
};
|
|
|
|
onMounted(() => {
|
|
getUserInformation();
|
|
});
|
|
onLoad(() => {
|
|
getUserInformation();
|
|
});
|
|
|
|
// 测试缓存中的数据
|
|
|
|
const test = () => {
|
|
console.log('登录数据', userStore.token);
|
|
}
|
|
|
|
//弹窗控制
|
|
const show = ref(false)
|
|
const newNickname = ref('')
|
|
const showPopup = () => {
|
|
newNickname.value = userName.value || ''
|
|
show.value = true
|
|
}
|
|
|
|
// 关闭弹窗方法
|
|
const hidePopup = () => {
|
|
show.value = false
|
|
}
|
|
|
|
// 确认修改用户名
|
|
const confirmUpdateNickname = async () => {
|
|
if (!newNickname.value.trim()) {
|
|
uni.showToast({
|
|
title: 'Username cannot be empty',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
try {
|
|
const res = await updateNickname({ nickname: newNickname.value })
|
|
if (res.data) {
|
|
userName.value = newNickname.value
|
|
userStore.updateUserInfo('nickname', newNickname.value)
|
|
hidePopup()
|
|
uni.showToast({
|
|
title: 'Update success',
|
|
icon: 'success'
|
|
})
|
|
} else {
|
|
uni.showToast({
|
|
title: 'Update failed',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
} catch (error) {
|
|
uni.showToast({
|
|
title: 'Update failed',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<view class="bg-[#fff]">
|
|
<!-- 页面内容区域 -->
|
|
<view class="flex flex-col">
|
|
<!-- 个人页头部区域 -->
|
|
<view class="profile-header px-4 pt-4 bg-gradient-to-r from-blue-500 to-purple-600">
|
|
<view class="flex items-center text-thme">
|
|
<view
|
|
class="w-16 h-16 rounded-full border-2 border-theme border-solid mr-4 p-3 bg-[#ddd] overflow-hidden">
|
|
<!-- 直接使用原生 image 标签 -->
|
|
<image :src="userData?.avatar || '/static/image/head.png'" class="w-full h-full object-cover" @click="chooseAndUploadAvatar" />
|
|
</view>
|
|
|
|
<view class="flex flex-col justify-center items-start gap-1">
|
|
<view class="font-bold text-lg text-black" @click="showPopup">{{userName}}</view>
|
|
<view class="text-sm opacity-80 text-black">Sweekar Guardian since 2023</view>
|
|
<!-- 在线状态 -->
|
|
<view class="border-round-theme rounded-lg flex justify-center p-1 items-center">
|
|
<text class="text-[10px]">Online</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<!-- 功能区域分割线 -->
|
|
<uv-divider line-color="#333333" hairline="false"></uv-divider>
|
|
|
|
<!-- 功能列表 -->
|
|
<view class="p-4 flex flex-col">
|
|
<!-- MY DEVICES -->
|
|
<view class="flex flex-col p-1 gap-3">
|
|
<!-- title -->
|
|
<view class="flex gap-1 justify-between">
|
|
<view class="flex gap-2 items-center">
|
|
<text class="font-bold">|</text>
|
|
<text class="text-sm font-bold">MY DEVICES</text>
|
|
</view>
|
|
<text class="text-sm text-[#808080]">{{deviceList.length}} Active</text>
|
|
</view>
|
|
<!-- 功能模块 -->
|
|
<view
|
|
v-for="(device, index) in deviceList"
|
|
:key="device.id"
|
|
class="border-round-theme rounded-lg flex flex-col justify-center items-start p-4 gap-2 cursor-pointer"
|
|
:class="device.isCurrent?'shadow-[4px_4px_1px_0_rgba(0,0,0,1)]':''"
|
|
@click="handleDeviceClick(device.id)"
|
|
>
|
|
<!-- 上半部分 -->
|
|
<view class="flex items-center gap-2 w-full">
|
|
<!-- 图标部分 -->
|
|
<view
|
|
class="border-round-theme bg-[#ddd] rounded-lg w-10 h-10 flex justify-center items-center">
|
|
<u-icon name="phone"></u-icon>
|
|
</view>
|
|
|
|
<!-- 设备信息 -->
|
|
<view class="flex flex-col flex-1">
|
|
<text class="font-bold text-[16px] break-words">{{device.name}}</text>
|
|
<text class="text-xs text-[#808080]">{{device.level}}</text>
|
|
</view>
|
|
<!-- 电量和蓝牙 -->
|
|
<view class="flex">
|
|
<!-- tag -->
|
|
<view
|
|
v-if="device.isCurrent"
|
|
class="flex justify-center items-center border-round-theme bg-theme h-4 w-10 rounded-full ">
|
|
<text class="text-[8px] text-white">CURRENT</text>
|
|
</view>
|
|
<view
|
|
class="border-round-theme rounded-lg w-10 h-10 flex flex-col justify-center items-center mr-1">
|
|
<u-icon name="phone"></u-icon>
|
|
<text class="text-[8px]">{{device.battery}}</text>
|
|
</view>
|
|
<view
|
|
class="border-round-theme rounded-lg w-10 h-10 flex flex-col justify-center items-center mr-1">
|
|
<u-icon name="phone"></u-icon>
|
|
<text class="text-[8px]">{{device.bluetooth}}</text>
|
|
</view>
|
|
|
|
</view>
|
|
</view>
|
|
<!-- 底部按钮 -->
|
|
<view class="w-full" v-if="device.isCurrent">
|
|
<view class="border-round-theme rounded-lg bg-theme flex justify-center items-center p-2 gap-1">
|
|
<u-icon name="list" color="#fff"></u-icon>
|
|
<text class="text-white text-sm">CONTROL CONSOLE</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 添加模块 -->
|
|
<view class="border border-dashed border-[#808080] rounded-lg flex justify-center items-center p-2 gap-2 cursor-pointer">
|
|
<uv-icon name="plus" color="#808080"></uv-icon>
|
|
<text class="text-[#808080]">Link New Sweekar</text>
|
|
</view>
|
|
|
|
<!-- GENERAL -->
|
|
<view class="flex flex-col p-1 gap-3">
|
|
<!-- title -->
|
|
<view class="flex gap-1 justify-between">
|
|
<view class="flex gap-2 items-center">
|
|
<text class="font-bold">|</text>
|
|
<text class="text-sm font-bold">GENERAL</text>
|
|
</view>
|
|
</view>
|
|
<!-- 功能模块 -->
|
|
<view class="flex flex-col border border-round-theme rounded-lg p-4">
|
|
<view class="flex justify-between">
|
|
<!-- icon + name -->
|
|
<view class="flex gap-2">
|
|
<uv-icon name="account" size="20" bold="true"></uv-icon>
|
|
<text class="font-bold text-sm"> Account Info</text>
|
|
</view>
|
|
|
|
<uv-icon name="arrow-right" size="20" bold="true"></uv-icon>
|
|
</view>
|
|
<uv-divider line-color="#808080" hairline="false"></uv-divider>
|
|
<view class="flex justify-between">
|
|
<!-- icon + name -->
|
|
<view class="flex gap-2">
|
|
<uv-icon name="setting" size="20" bold="true"></uv-icon>
|
|
<text class="font-bold text-sm"> App Settings</text>
|
|
</view>
|
|
|
|
<uv-icon name="arrow-right" size="20" bold="true"></uv-icon>
|
|
</view>
|
|
<uv-divider line-color="#808080" hairline="false"></uv-divider>
|
|
<view class="flex justify-between">
|
|
<!-- icon + name -->
|
|
<view class="flex gap-2">
|
|
<uv-icon name="empty-permission" size="20" bold="true"></uv-icon>
|
|
<text class="font-bold text-sm"> Privacy & Data</text>
|
|
</view>
|
|
|
|
<uv-icon name="arrow-right" size="20" bold="true"></uv-icon>
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
<!-- SUPPORT -->
|
|
<view class="flex flex-col p-1 gap-3">
|
|
<!-- title -->
|
|
<view class="flex gap-1 justify-between">
|
|
<view class="flex gap-2 items-center">
|
|
<text class="font-bold">|</text>
|
|
<text class="text-sm font-bold">SUPPORT</text>
|
|
</view>
|
|
</view>
|
|
<!-- 功能模块 -->
|
|
<view class="flex flex-col border border-round-theme rounded-lg p-4">
|
|
<view class="flex justify-between">
|
|
<!-- icon + name -->
|
|
<view class="flex gap-2">
|
|
<uv-icon name="question-circle" size="20" bold="true"></uv-icon>
|
|
<text class="font-bold text-sm"> Help Center</text>
|
|
</view>
|
|
|
|
<uv-icon name="arrow-right" size="20" bold="true"></uv-icon>
|
|
</view>
|
|
<uv-divider line-color="#808080" hairline="false" ></uv-divider>
|
|
<view class="flex justify-between" @click="toLoginPage">
|
|
<!-- icon + name -->
|
|
<view class="flex gap-2">
|
|
<uv-icon name="lock" size="20" bold="true"></uv-icon>
|
|
<text class="font-bold text-sm"> {{ userStore.isLogin ? 'Log Out' : 'Log In' }}</text>
|
|
</view>
|
|
|
|
<uv-icon name="arrow-right" size="20" bold="true"></uv-icon>
|
|
</view>
|
|
|
|
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
<u-popup :show="show" @close="hidePopup" round="10" :closeable="true">
|
|
<view class="p-6 w-64">
|
|
<view class="text-lg font-bold mb-4 text-center">Edit Username</view>
|
|
<input
|
|
v-model="newNickname"
|
|
class="border border-gray-300 rounded-lg p-3 mb-4"
|
|
placeholder="Enter new username"
|
|
maxlength="20"
|
|
/>
|
|
<view class="flex gap-3">
|
|
<button
|
|
class="flex-1 py-2 rounded-lg bg-gray-200 text-gray-700"
|
|
@click="hidePopup"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
class="flex-1 py-2 rounded-lg bg-theme text-white"
|
|
@click="confirmUpdateNickname"
|
|
>
|
|
Confirm
|
|
</button>
|
|
</view>
|
|
</view>
|
|
</u-popup>
|
|
|
|
<CustomTabBar />
|
|
</view>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
:deep(.u-count-down__text) {
|
|
color: #ab002d !important;
|
|
font-size: 13px !important;
|
|
}
|
|
|
|
.navigation {
|
|
border-bottom: 1px solid #f2f2f2;
|
|
}
|
|
|
|
.navi-title-center {
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
.line {
|
|
transition: 0.3s all;
|
|
}
|
|
|
|
.search {
|
|
:deep(.u-search) {
|
|
flex: 1;
|
|
}
|
|
}
|
|
|
|
.profile-header {
|
|
min-height: 200rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
</style>
|