429 lines
10 KiB
Vue
429 lines
10 KiB
Vue
<script lang="ts" setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { useWxStore } from '@/pinia/stores/wx'
|
|
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
|
|
import { storeToRefs } from 'pinia'
|
|
import { getWxUserByOpenid, updateUserByOpenid } from '@/api/users'
|
|
import { useToast } from 'wot-design-uni'
|
|
import { getEnvBaseUploadUrl, toHttpsUrl } from '@/utils'
|
|
|
|
definePage({
|
|
style: {
|
|
navigationBarTitleText: '设置头像昵称',
|
|
},
|
|
})
|
|
|
|
const wxStore = useWxStore()
|
|
const ab98UserStore = useAb98UserStore()
|
|
const toast = useToast()
|
|
|
|
const { wxUserDTO, openid } = storeToRefs(wxStore)
|
|
const { name: userName, face_img } = storeToRefs(ab98UserStore)
|
|
|
|
// 头像相关
|
|
const avatarUrl = ref('')
|
|
const tempAvatarPath = ref('') // 临时头像路径,用于预览
|
|
|
|
// 昵称相关
|
|
const nickName = ref('')
|
|
|
|
// 初始化数据
|
|
onMounted(() => {
|
|
// 优先使用微信用户信息,其次使用企业用户信息
|
|
if (wxUserDTO.value) {
|
|
avatarUrl.value = wxUserDTO.value.avatar || ''
|
|
nickName.value = wxUserDTO.value.nickName || ''
|
|
}
|
|
|
|
// 如果没有微信头像,使用企业用户头像
|
|
if (!avatarUrl.value && face_img.value) {
|
|
avatarUrl.value = toHttpsUrl(face_img.value)
|
|
}
|
|
|
|
// 如果没有微信昵称,使用企业用户名
|
|
if (!nickName.value && userName.value) {
|
|
nickName.value = userName.value
|
|
}
|
|
|
|
// 默认头像
|
|
if (!avatarUrl.value) {
|
|
avatarUrl.value = '/static/favicon.ico'
|
|
}
|
|
})
|
|
|
|
// 图片上传相关
|
|
const uploadUrl = `${getEnvBaseUploadUrl()}/file/upload`
|
|
const uploading = ref(false)
|
|
|
|
// 图片压缩处理 - 参考审批提交页面的实现
|
|
const compressImage = (src: string): Promise<string> => {
|
|
return new Promise((resolve, reject) => {
|
|
// 如果是网络图片,需要先下载到本地再压缩
|
|
if (src && (src.startsWith('http://') || src.startsWith('https://'))) {
|
|
// 先下载图片
|
|
uni.downloadFile({
|
|
url: src,
|
|
success: (downloadResult) => {
|
|
console.log('网络图片下载完成:', downloadResult.tempFilePath)
|
|
// 下载成功后压缩图片
|
|
compressLocalImage(downloadResult.tempFilePath)
|
|
},
|
|
fail: (err) => {
|
|
console.warn('网络图片下载失败:', err)
|
|
reject(new Error('网络图片下载失败'))
|
|
}
|
|
})
|
|
} else {
|
|
// 本地图片直接压缩
|
|
compressLocalImage(src)
|
|
}
|
|
|
|
// 压缩本地图片的函数
|
|
function compressLocalImage(localSrc: string) {
|
|
uni.compressImage({
|
|
src: localSrc,
|
|
quality: 0.8,
|
|
maxWidth: 980,
|
|
maxHeight: 980,
|
|
success: (res) => {
|
|
console.log('头像图片压缩成功:', res)
|
|
resolve(res.tempFilePath)
|
|
},
|
|
fail: (err) => {
|
|
console.warn('头像图片压缩失败,使用原图:', err)
|
|
// 压缩失败时使用原图
|
|
resolve(localSrc)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
// 上传头像图片
|
|
const uploadAvatar = async (filePath: string): Promise<string> => {
|
|
uploading.value = true
|
|
try {
|
|
// 先压缩图片
|
|
const compressedPath = await compressImage(filePath)
|
|
console.log('头像压缩完成:', compressedPath)
|
|
|
|
// 上传到服务器
|
|
const uploadResult = await new Promise<any>((resolve, reject) => {
|
|
uni.uploadFile({
|
|
url: uploadUrl,
|
|
filePath: compressedPath,
|
|
name: 'file',
|
|
success: (res) => {
|
|
if (res.statusCode === 200) {
|
|
try {
|
|
const response = JSON.parse(res.data)
|
|
resolve(response)
|
|
} catch (e) {
|
|
reject(new Error('上传响应解析失败'))
|
|
}
|
|
} else {
|
|
reject(new Error(`上传失败,状态码: ${res.statusCode}`))
|
|
}
|
|
},
|
|
fail: (err) => {
|
|
reject(err)
|
|
}
|
|
})
|
|
})
|
|
|
|
// 解析上传响应
|
|
if (uploadResult.code === 0 && uploadResult.data && uploadResult.data.url) {
|
|
const avatarUrl = toHttpsUrl(uploadResult.data.url)
|
|
console.log('头像上传成功:', avatarUrl)
|
|
return avatarUrl
|
|
} else {
|
|
throw new Error(uploadResult.msg || '上传失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('头像上传失败:', error)
|
|
throw error
|
|
} finally {
|
|
uploading.value = false
|
|
}
|
|
}
|
|
|
|
// 选择头像并自动压缩上传
|
|
const onChooseAvatar = async (e: any) => {
|
|
const { avatarUrl: tempUrl } = e.detail
|
|
tempAvatarPath.value = tempUrl
|
|
avatarUrl.value = tempUrl
|
|
|
|
// 显示上传提示
|
|
uni.showLoading({
|
|
title: '正在压缩上传头像...',
|
|
mask: true
|
|
})
|
|
|
|
try {
|
|
// 上传头像到服务器
|
|
const uploadedUrl = await uploadAvatar(tempUrl)
|
|
// 使用上传成功后的URL更新显示
|
|
avatarUrl.value = uploadedUrl
|
|
tempAvatarPath.value = uploadedUrl
|
|
|
|
uni.showToast({
|
|
title: '头像上传成功',
|
|
icon: 'success',
|
|
duration: 2000
|
|
})
|
|
} catch (error) {
|
|
console.error('头像上传失败:', error)
|
|
uni.showToast({
|
|
title: '头像上传失败,请重试',
|
|
icon: 'error',
|
|
duration: 2000
|
|
})
|
|
// 上传失败时,保持本地图片预览,但提交时会使用本地路径
|
|
} finally {
|
|
uni.hideLoading()
|
|
}
|
|
}
|
|
|
|
// 提交更新
|
|
const loading = ref(false)
|
|
const handleSubmit = async () => {
|
|
if (!openid.value) {
|
|
toast.show('用户信息获取失败,请重新登录')
|
|
return
|
|
}
|
|
|
|
if (!nickName.value.trim()) {
|
|
toast.show('请输入昵称')
|
|
return
|
|
}
|
|
|
|
// 获取要使用的头像路径
|
|
const finalAvatarPath = tempAvatarPath.value || avatarUrl.value
|
|
|
|
loading.value = true
|
|
try {
|
|
// 使用网络URL或静态资源URL更新用户信息
|
|
const result = await updateUserByOpenid(
|
|
openid.value,
|
|
nickName.value.trim(),
|
|
finalAvatarPath
|
|
)
|
|
|
|
if (result?.code === 0) {
|
|
// 更新store中的用户信息
|
|
const wxUser = await getWxUserByOpenid(openid.value);
|
|
console.log('wxUser:', wxUser);
|
|
await wxStore.processUserInfo(wxUser.data, wxStore.corpid);
|
|
|
|
toast.show('更新成功')
|
|
// 返回上一页
|
|
uni.navigateBack()
|
|
} else {
|
|
toast.show(result?.msg || '更新失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('更新用户信息失败:', error)
|
|
toast.show('更新失败,请稍后重试')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<view class="profile-setting-page">
|
|
<!-- 头像设置区域 -->
|
|
<view class="setting-card">
|
|
<view class="section-title">设置头像</view>
|
|
<view class="avatar-section">
|
|
<view class="current-avatar">
|
|
<image class="avatar" :src="avatarUrl" mode="aspectFill" />
|
|
</view>
|
|
<view class="avatar-hint">当前头像</view>
|
|
</view>
|
|
|
|
<!-- 微信小程序选择头像按钮 -->
|
|
<view class="choose-avatar-btn">
|
|
<button
|
|
class="avatar-button"
|
|
open-type="chooseAvatar"
|
|
@chooseavatar="onChooseAvatar"
|
|
>
|
|
选择头像
|
|
</button>
|
|
<view class="avatar-tip">点击按钮选择微信头像</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 昵称设置区域 -->
|
|
<view class="setting-card">
|
|
<view class="section-title">设置昵称</view>
|
|
<view class="nickname-section">
|
|
<input
|
|
class="nickname-input"
|
|
type="nickname"
|
|
v-model="nickName"
|
|
placeholder="请输入昵称"
|
|
:maxlength="20"
|
|
/>
|
|
<view class="nickname-tip">输入时键盘上方会展示微信昵称</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 提交按钮 -->
|
|
<view class="submit-section">
|
|
<wd-button
|
|
type="primary"
|
|
block
|
|
:loading="loading"
|
|
@click="handleSubmit"
|
|
>
|
|
{{ loading ? '更新中...' : '保存设置' }}
|
|
</wd-button>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.profile-setting-page {
|
|
background: #f7f8fa;
|
|
min-height: 100vh;
|
|
padding: 12px;
|
|
}
|
|
|
|
.setting-card {
|
|
background: white;
|
|
border-radius: 16px;
|
|
padding: 20px 16px;
|
|
margin-bottom: 16px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
|
|
.section-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #1a1a1a;
|
|
margin-bottom: 20px;
|
|
padding-left: 8px;
|
|
position: relative;
|
|
letter-spacing: 0.5px;
|
|
|
|
&::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 3px;
|
|
height: 16px;
|
|
background: linear-gradient(135deg, #409EFF 0%, #66B1FF 100%);
|
|
border-radius: 2px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.avatar-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
margin-bottom: 24px;
|
|
|
|
.current-avatar {
|
|
width: 120px;
|
|
height: 120px;
|
|
border-radius: 50%;
|
|
overflow: hidden;
|
|
border: 4px solid rgba(64, 158, 255, 0.2);
|
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
|
|
margin-bottom: 12px;
|
|
|
|
.avatar {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
|
|
.avatar-hint {
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
}
|
|
|
|
.choose-avatar-btn {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
|
|
.avatar-button {
|
|
width: 200px;
|
|
height: 44px;
|
|
line-height: 44px;
|
|
background: linear-gradient(135deg, #409EFF 0%, #66B1FF 100%);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 22px;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
|
margin-bottom: 12px;
|
|
|
|
&:active {
|
|
opacity: 0.9;
|
|
transform: translateY(1px);
|
|
}
|
|
}
|
|
|
|
.avatar-tip {
|
|
font-size: 12px;
|
|
color: #999;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
.nickname-section {
|
|
.nickname-input {
|
|
width: 100%;
|
|
height: 48px;
|
|
padding: 0 16px;
|
|
background: #f7f8fa;
|
|
border: 1px solid #e5e5e5;
|
|
border-radius: 24px;
|
|
font-size: 16px;
|
|
color: #1a1a1a;
|
|
margin-bottom: 12px;
|
|
|
|
&:focus {
|
|
border-color: #409EFF;
|
|
background: white;
|
|
}
|
|
|
|
&::placeholder {
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.nickname-tip {
|
|
font-size: 12px;
|
|
color: #999;
|
|
padding-left: 8px;
|
|
}
|
|
}
|
|
|
|
.submit-section {
|
|
margin-top: 24px;
|
|
|
|
::v-deep .wd-button {
|
|
height: 48px;
|
|
border-radius: 24px;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
background: linear-gradient(135deg, #409EFF 0%, #66B1FF 100%);
|
|
border: none;
|
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
|
|
|
&:active {
|
|
opacity: 0.9;
|
|
}
|
|
}
|
|
}
|
|
</style> |