罗广辉
2026-05-08 626e8ccc9d05f4834f351779125145c49c99b2bb
feat: 微信小程序支持上传头像
1 files modified
577 ■■■■ changed files
src/subPackages/userDetail/infos/index.vue 577 ●●●● patch | view | raw | blame | history
src/subPackages/userDetail/infos/index.vue
@@ -1,307 +1,328 @@
<template>
    <view class="container">
        <div class="avatarBox">
            <u-avatar @click="uploadAvatar" :src="userInfo.avatar" size="114" mode="aspectFill" />
        </div>
        <view class="detailBox">
            <div class="detailCon">
                <div class="orderRow">
                    <div class="rowTitle">姓名</div>
                    <div>{{userInfo.realName}}</div>
                </div>
                <div class="orderRow">
                    <div class="rowTitle">所属单位</div>
                    <div>{{userInfo.deptName}}</div>
                </div>
                <div class="orderRow">
                    <div class="rowTitle">手机号</div>
                    <u-input input-align="right" v-model="userInfo.phone" type="number" placeholder="请输入手机号"
                        class="input-item" />
  <view class="container">
    <div class="avatarBox">
                </div>
                <div class="orderRow">
                    <div class="rowTitle">邮箱</div>
                    <u-input input-align="right" v-model="userInfo.email" type="email" placeholder="请输入邮箱"
                        class="input-item" />
                </div>
            </div>
        </view>
        <view class="btngroup">
            <u-button color="#AEAEAE" class="custom-style" shape="circle" @click="reset">重置</u-button>
            <u-button color="#1D6FE9" class="custom-style" shape="circle" @click="submit">提交</u-button>
        </view>
    </view>
      <u-avatar @click="uploadAvatar" :src="userInfo.avatar" size="114" mode="aspectFill"/>
    </div>
    <view class="detailBox">
      <div class="detailCon">
        <div class="orderRow">
          <div class="rowTitle">姓名</div>
          <div>{{ userInfo.realName }}</div>
        </div>
        <div class="orderRow">
          <div class="rowTitle">所属单位</div>
          <div>{{ userInfo.deptName }}</div>
        </div>
        <div class="orderRow">
          <div class="rowTitle">手机号</div>
          <u-input input-align="right" v-model="userInfo.phone" type="number" placeholder="请输入手机号"
                   class="input-item"/>
        </div>
        <div class="orderRow">
          <div class="rowTitle">邮箱</div>
          <u-input input-align="right" v-model="userInfo.email" type="email" placeholder="请输入邮箱"
                   class="input-item"/>
        </div>
      </div>
    </view>
    <view class="btngroup">
      <u-button color="#AEAEAE" class="custom-style" shape="circle" @click="reset">重置</u-button>
      <u-button color="#1D6FE9" class="custom-style" shape="circle" @click="submit">提交</u-button>
    </view>
  </view>
</template>
<script setup>
    import {
        getEnvObj,
        getWebViewUrl
    } from "@/utils/index.js";
    import {
        getUserInfo,
        updateInfo,
        updatePassword
    } from '@/api/user/index.js';
    import {
        useUserStore
    } from "@/store/index.js";
    const userInfo = ref({
        id: '',
        avatar: '',
        realName: '',
        name: '',
        phone: '',
        email: '',
        deptName: '',
    });
    // 校验手机号
    const validatePhone = () => {
        const phone = userInfo.value.phone
        if (!phone) return true
        const phoneRegex = /^1[3-9]\d{9}$/
        if (!phoneRegex.test(phone)) {
import {getEnvObj} from "@/utils/index.js";
import {getUserInfo, updateInfo} from '@/api/user/index.js';
import {useUserStore} from "@/store/index.js";
            uni.showToast({
                title: '请输入正确的手机号码',
                icon: 'none',
                duration: 2000
            });
            return false
        }
        return true
    }
    // 校验邮箱
    const validateEmail = () => {
        if (!userInfo.value.email) return true
        const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
        if (!emailRegex.test(userInfo.value.email)) {
const userInfo = ref({
  id: '',
  avatar: '',
  realName: '',
  name: '',
  phone: '',
  email: '',
  deptName: '',
});
            uni.showToast({
                title: '请输入正确的邮箱地址',
                icon: 'none',
                duration: 2000
            });
            return false
        }
        return true
    }
    const getUserInfoData = () => {
        getUserInfo().then(res => {
            const user = res.data.data;
            userInfo.value = {
                id: user.id,
                avatar: user.avatar,
                name: user.name,
                realName: user.realName,
                phone: user.phone,
                email: user.email,
                deptName: user.deptName,
            };
// 校验手机号
const validatePhone = () => {
  const phone = userInfo.value.phone
  if (!phone) return true
  const phoneRegex = /^1[3-9]\d{9}$/
  if (!phoneRegex.test(phone)) {
        });
    };
    const reset = () => {
        getUserInfoData();
    };
    uni.showToast({
      title: '请输入正确的手机号码',
      icon: 'none',
      duration: 2000
    });
    return false
  }
  return true
}
    const {
        VITE_API_BASE_URL
    } = getEnvObj()
// 校验邮箱
const validateEmail = () => {
  if (!userInfo.value.email) return true
  const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
  if (!emailRegex.test(userInfo.value.email)) {
    function uploadUtil(options) {
        const {
            formData,
            filePath,
            url
        } = options;
    uni.showToast({
      title: '请输入正确的邮箱地址',
      icon: 'none',
      duration: 2000
    });
    return false
  }
  return true
}
        return new Promise((resolve, reject) => {
            let accessToken = useUserStore()?.$state?.userInfo?.access_token;
const getUserInfoData = () => {
  getUserInfo().then(res => {
    const user = res.data.data;
    userInfo.value = {
      id: user.id,
      avatar: user.avatar,
      name: user.name,
      realName: user.realName,
      phone: user.phone,
      email: user.email,
      deptName: user.deptName,
    };
            uni.uploadFile({
                url: `${VITE_API_BASE_URL}${url}`,
                name: 'file',
                header: {
                    'Blade-Auth': 'bearer ' + accessToken
                },
                filePath: filePath,
                formData,
                success: (res) => {
                    const resData = JSON.parse(res.data)
                    if (resData.code === 200 || resData.code === 0) {
                        resolve(res)
                    } else {
                        showToast(resData.message)
  });
};
const reset = () => {
  getUserInfoData();
};
                        reject(res)
                    }
                },
                fail: (err) => {
                    reject(err)
                }
            });
        })
    }
    const uploadAvatar = () => {
        uni.chooseImage({
            count: 1,
            success: (res) => {
                const tempFile = res.tempFiles[0]; // 获取文件对象
                const filePath = tempFile.path || tempFile.tempFilePath;
                if (!filePath) {
                    uni.showToast({
                        title: '获取文件路径失败',
                        icon: 'none'
                    });
                    return;
                }
                let fileName = tempFile.name;
                if (!fileName) {
                    const pathWithoutProtocol = filePath.replace(/^file:\/\//, '');
                    fileName = pathWithoutProtocol.split('/').pop() || 'unknown.png';
                }
                // 显示加载中
                uni.showLoading({
                    title: '上传中...'
                });
const {
  VITE_API_BASE_URL
} = getEnvObj()
                // 上传文件
                uploadUtil({
                    filePath: filePath,
                    formData: {
                        fileName: fileName,
                        sn: 'avatar_upload'
                    },
                    url: '/blade-resource/oss/endpoint/put-file'
                }).then(res => {
                    const resData = JSON.parse(res.data);
                    if (resData.code === 200 || resData.code === 0) {
                        // 更新头像显示
                        userInfo.value.avatar = resData.data.link || resData.data.url;
                        uni.hideLoading();
                        uni.showToast({
                            title: '头像上传成功',
                            icon: 'success'
                        });
                    } else {
                        throw new Error(resData.message || '上传失败');
// 头像上传到服务器
function uploadUtil(options) {
  const {formData, filePath, url} = options;
                    }
                }).catch(err => {
                    uni.hideLoading();
                    uni.showToast({
                        title: err.message || '上传失败',
                        icon: 'none'
                    });
                });
            }
        });
    }
    const submit = () => {
        if (!validatePhone() || !validateEmail()) return
        userInfo.value.name = userInfo.value.realName;
        updateInfo(userInfo.value).then(res => {
            if (res.data.code === 200) {
                uni.showToast({
                    title: '修改信息成功',
                    icon: 'none',
                    duration: 2000
                });
                    getUserInfoData()
  return new Promise((resolve, reject) => {
    let accessToken = useUserStore()?.$state?.userInfo?.access_token;
    uni.uploadFile({
      url: `${VITE_API_BASE_URL}${url}`,
      name: 'file',
      header: {
        'Blade-Auth': 'bearer ' + accessToken
      },
      filePath: filePath,
      formData,
      success: (res) => {
        const resData = JSON.parse(res.data)
        if (resData.code === 200 || resData.code === 0) {
          resolve(res)
        } else {
          uni.showToast(resData.message)
          reject(res)
        }
      },
      fail: (err) => {
        reject(err)
      }
    });
  })
}
            } else {
                uni.showToast({
                    title: res.msg,
                    icon: 'none',
                    duration: 2000
                });
                    getUserInfoData()
            }
// 上传头像功能
const uploadAvatar = () => {
  // #ifdef MP-WEIXIN
  uni.chooseMedia({
    count: 1,
    mediaType: ['image'],
    sourceType: ['album', 'camera'],
    maxDuration: 30,
    camera: 'back',
    success(res) {
      uploadUtil({
        filePath: res.tempFiles[0].tempFilePath,
        formData: {
          fileName: res.tempFiles[0].tempFilePath,
          sn: 'avatar_upload'
        },
        url: '/blade-resource/oss/endpoint/put-file'
      }).then(res => {
        const resData = JSON.parse(res.data);
        if (resData.code === 200 || resData.code === 0) {
          // 更新头像显示
          userInfo.value.avatar = resData.data.link || resData.data.url;
          uni.hideLoading();
          uni.showToast({title: '头像上传成功', icon: 'success'});
        } else {
          throw new Error(resData.message || '上传失败');
        }
      })
    }
  })
  // #endif
        });
    };
    onShow(async () => {
        getUserInfoData()
    });
  // #ifndef MP-WEIXIN
  uni.chooseImage({
    count: 1,
    success: (res) => {
      const tempFile = res.tempFiles[0]; // 获取文件对象
      const filePath = tempFile.path || tempFile.tempFilePath;
      if (!filePath) {
        uni.showToast({
          title: '获取文件路径失败',
          icon: 'none'
        });
        return;
      }
      let fileName = tempFile.name;
      if (!fileName) {
        const pathWithoutProtocol = filePath.replace(/^file:\/\//, '');
        fileName = pathWithoutProtocol.split('/').pop() || 'unknown.png';
      }
      // 显示加载中
      uni.showLoading({
        title: '上传中...'
      });
      // 上传文件
      uploadUtil({
        filePath: filePath,
        formData: {
          fileName: fileName,
          sn: 'avatar_upload'
        },
        url: '/blade-resource/oss/endpoint/put-file'
      }).then(res => {
        const resData = JSON.parse(res.data);
        if (resData.code === 200 || resData.code === 0) {
          // 更新头像显示
          userInfo.value.avatar = resData.data.link || resData.data.url;
          uni.hideLoading();
          uni.showToast({title: '头像上传成功', icon: 'success'});
        } else {
          throw new Error(resData.message || '上传失败');
        }
      }).catch(err => {
        uni.hideLoading();
        uni.showToast({
          title: err.message || '上传失败',
          icon: 'none'
        });
      });
    }
  });
  // #endif
}
const submit = () => {
  if (!validatePhone() || !validateEmail()) return
  userInfo.value.name = userInfo.value.realName;
  updateInfo(userInfo.value).then(res => {
    if (res.data.code === 200) {
      uni.showToast({
        title: '修改信息成功',
        icon: 'none',
        duration: 2000
      });
      getUserInfoData()
    } else {
      uni.showToast({
        title: res.msg,
        icon: 'none',
        duration: 2000
      });
      getUserInfoData()
    }
  });
};
onShow(async () => {
  getUserInfoData()
});
</script>
<style lang="scss" scoped>
    .container {
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
    }
    .avatarBox {
        width: 228rpx;
        height: 228rpx;
        margin: 76rpx 0;
    }
.container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}
    .detailBox {
        width: 702rpx;
        min-height: 430rpx;
        background: #FFFFFF;
        border-radius: 12rpx;
        box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.avatarBox {
  width: 228rpx;
  height: 228rpx;
  margin: 76rpx 0;
}
        .detailCon {
            padding: 0 24rpx;
        }
.detailBox {
  width: 702rpx;
  min-height: 430rpx;
  background: #FFFFFF;
  border-radius: 12rpx;
  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
        .orderRow {
            display: flex;
            justify-content: space-between;
            align-items: center;
            height: 96rpx;
            border-bottom: 2rpx solid #f5f5f5;
            color: #7b7b7b;
  .detailCon {
    padding: 0 24rpx;
  }
            .rowTitle {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 400;
                font-size: 30rpx;
                color: #222324;
                white-space: nowrap;
            }
  .orderRow {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 96rpx;
    border-bottom: 2rpx solid #f5f5f5;
    color: #7b7b7b;
            ::v-deep .u-input {
                border: none !important;
                background: transparent !important;
                padding: 0 !important;
            }
    .rowTitle {
      font-family: Source Han Sans CN, Source Han Sans CN;
      font-weight: 400;
      font-size: 30rpx;
      color: #222324;
      white-space: nowrap;
    }
            ::v-deep .u-input__input {
                border: none !important;
                box-shadow: none !important;
                background: transparent !important;
                padding: 0 !important;
                margin: 0 !important;
                height: auto !important;
            }
        }
    }
    ::v-deep .u-input {
      border: none !important;
      background: transparent !important;
      padding: 0 !important;
    }
    .btngroup {
        display: flex;
        position: absolute;
        bottom: 150rpx;
    }
    ::v-deep .u-input__input {
      border: none !important;
      box-shadow: none !important;
      background: transparent !important;
      padding: 0 !important;
      margin: 0 !important;
      height: auto !important;
    }
  }
}
    :deep(.u-button:first-child) {
        margin-right: 30rpx !important;
    }
.btngroup {
  display: flex;
  position: absolute;
  bottom: 150rpx;
}
    .custom-style {
        width: 276rpx;
        height: 76rpx;
    }
    :deep(.u-button){
        width: 276rpx !important;
        height: 76rpx !important;
    }
</style>
:deep(.u-button:first-child) {
  margin-right: 30rpx !important;
}
.custom-style {
  width: 276rpx;
  height: 76rpx;
}
:deep(.u-button) {
  width: 276rpx !important;
  height: 76rpx !important;
}
</style>