|
|
@@ -22,13 +22,15 @@
|
|
|
<van-image width="30px" height="30px" :src="userInfos.avatar" />
|
|
|
</template>
|
|
|
</van-cell>
|
|
|
- <van-field v-model="state.form.nickName" label="用户昵称" placeholder="用户昵称" :rules="[{ required: true, message: '请输入用户昵称' }]" />
|
|
|
+ <van-field v-model="state.form.nickName" label="用户昵称" placeholder="用户昵称"
|
|
|
+ :rules="[{ required: true, message: '请输入用户昵称' }]" />
|
|
|
<van-field v-model="state.form.phone" label="手机号" placeholder="手机号"> </van-field>
|
|
|
<van-field v-model="state.form.email" label="电子邮箱" placeholder="电子邮箱" />
|
|
|
<van-field v-model="state.form.sex" label="性别" placeholder="性别">
|
|
|
<template #input>
|
|
|
<van-radio-group v-model="state.form.sex" direction="horizontal">
|
|
|
- <van-radio v-for="item in userSexList" :key="item.id" :name="item.dictValue">{{ item.dictLabel }}</van-radio>
|
|
|
+ <van-radio v-for="item in userSexList" :key="item.id" :name="item.dictValue">{{ item.dictLabel
|
|
|
+ }}</van-radio>
|
|
|
</van-radio-group>
|
|
|
</template>
|
|
|
</van-field>
|
|
|
@@ -39,10 +41,12 @@
|
|
|
</div>
|
|
|
</van-form>
|
|
|
<!-- 剪裁图片组件 -->
|
|
|
- <van-popup class="bg-tran" v-model:show="cropperState.isShowDialog" closeable @close="closeDialog" position="bottom" :style="{ padding: '10px' }">
|
|
|
+ <van-popup class="bg-tran" v-model:show="cropperState.isShowDialog" closeable @close="closeDialog" position="bottom"
|
|
|
+ :style="{ padding: '10px' }">
|
|
|
<div class="cropper-warp">
|
|
|
<div class="cropper-warp-left">
|
|
|
- <img :src="typeof cropperState.cropperImg === 'string' ? cropperState.cropperImg : ''" class="cropper-warp-left-img" />
|
|
|
+ <img :src="typeof cropperState.cropperImg === 'string' ? cropperState.cropperImg : ''"
|
|
|
+ class="cropper-warp-left-img" />
|
|
|
</div>
|
|
|
<!-- <div class="cropper-warp-right">
|
|
|
<div class="cropper-warp-right-title">预览</div>
|
|
|
@@ -65,262 +69,269 @@
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
- import { storeToRefs } from 'pinia'
|
|
|
- import { useUserInfo } from '/@/stores/userInfo'
|
|
|
- import { Local } from '/@/utils/storage'
|
|
|
- import { showConfirmDialog, showDialog, showNotify } from 'vant'
|
|
|
- import { useRouter } from 'vue-router'
|
|
|
- import { nextTick, onMounted, reactive, ref } from 'vue'
|
|
|
- import { useUserApi } from '/@/api/system/user'
|
|
|
- import to from 'await-to-js'
|
|
|
- import { useDictApi } from '/@/api/system/dict'
|
|
|
- import Cropper from 'cropperjs'
|
|
|
- import 'cropperjs/dist/cropper.css'
|
|
|
- import axios from 'axios'
|
|
|
+import { storeToRefs } from 'pinia'
|
|
|
+import { useUserInfo } from '/@/stores/userInfo'
|
|
|
+import { showNotify } from 'vant'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import { nextTick, onMounted, reactive, ref } from 'vue'
|
|
|
+import { useUserApi } from '/@/api/system/user'
|
|
|
+import to from 'await-to-js'
|
|
|
+import { useDictApi } from '/@/api/system/dict'
|
|
|
+import Cropper from 'cropperjs'
|
|
|
+import 'cropperjs/dist/cropper.css'
|
|
|
+import axios from 'axios'
|
|
|
+import { userImgSize } from '/@/constants/pageConstants'
|
|
|
|
|
|
- const router = useRouter()
|
|
|
- const storesUseUserInfo = useUserInfo()
|
|
|
- const { userInfos } = storeToRefs(storesUseUserInfo)
|
|
|
- const userApi = useUserApi()
|
|
|
- const formRef = ref()
|
|
|
- const dictApi = useDictApi()
|
|
|
- const userSexList = ref(<RowDicDataType[]>[])
|
|
|
- const state = reactive({
|
|
|
- form: {
|
|
|
- id: 0,
|
|
|
- userId: 0, // 用户账号
|
|
|
- nickName: '', // 用户昵称
|
|
|
- phone: '', // 手机号
|
|
|
- email: '', // 电子邮件
|
|
|
- sex: '', // 性别
|
|
|
- avatar: '', // 头像图片地址
|
|
|
- pgName: ''
|
|
|
- },
|
|
|
- dialog: {
|
|
|
- isShowDialog: false,
|
|
|
- type: '',
|
|
|
- title: '',
|
|
|
- submitTxt: ''
|
|
|
- }
|
|
|
- })
|
|
|
- // 定义变量内容
|
|
|
- const cropperState = reactive({
|
|
|
+const router = useRouter()
|
|
|
+const storesUseUserInfo = useUserInfo()
|
|
|
+const { userInfos } = storeToRefs(storesUseUserInfo)
|
|
|
+const userApi = useUserApi()
|
|
|
+const formRef = ref()
|
|
|
+const dictApi = useDictApi()
|
|
|
+const userSexList = ref(<RowDicDataType[]>[])
|
|
|
+const state = reactive({
|
|
|
+ form: {
|
|
|
+ id: 0,
|
|
|
+ userId: 0, // 用户账号
|
|
|
+ nickName: '', // 用户昵称
|
|
|
+ phone: '', // 手机号
|
|
|
+ email: '', // 电子邮件
|
|
|
+ sex: '', // 性别
|
|
|
+ avatar: '', // 头像图片地址
|
|
|
+ pgName: ''
|
|
|
+ },
|
|
|
+ dialog: {
|
|
|
isShowDialog: false,
|
|
|
- cropperImg: '' as ArrayBuffer | string,
|
|
|
- cropperImgBase64: '',
|
|
|
- cropper: '' as RefType
|
|
|
+ type: '',
|
|
|
+ title: '',
|
|
|
+ submitTxt: ''
|
|
|
+ }
|
|
|
+})
|
|
|
+// 定义变量内容
|
|
|
+const cropperState = reactive({
|
|
|
+ isShowDialog: false,
|
|
|
+ cropperImg: '' as ArrayBuffer | string,
|
|
|
+ cropperImgBase64: '',
|
|
|
+ cropper: '' as RefType
|
|
|
+})
|
|
|
+const cropper = ref()
|
|
|
+const getDicts = () => {
|
|
|
+ Promise.all([dictApi.getDictDataByType('sys_com_sex')]).then(([sex]) => {
|
|
|
+ userSexList.value = sex.data.values || []
|
|
|
})
|
|
|
- const cropper = ref()
|
|
|
- const getDicts = () => {
|
|
|
- Promise.all([dictApi.getDictDataByType('sys_com_sex')]).then(([sex]) => {
|
|
|
- userSexList.value = sex.data.values || []
|
|
|
+}
|
|
|
+const afterRead = (res: any) => {
|
|
|
+ const rawFile = res.file
|
|
|
+ if (
|
|
|
+ rawFile.type !== 'image/jpeg' &&
|
|
|
+ rawFile.type !== 'image/jpg' &&
|
|
|
+ rawFile.type !== 'image/png' &&
|
|
|
+ rawFile.type !== 'image/bmp' &&
|
|
|
+ rawFile.type !== 'image/gif'
|
|
|
+ ) {
|
|
|
+ showNotify({
|
|
|
+ message: '上传图片必须是JPG/PNG/BMP/GIF类型!',
|
|
|
+ type: 'warning'
|
|
|
})
|
|
|
- }
|
|
|
- const afterRead = (res: any) => {
|
|
|
- const rawFile = res.file
|
|
|
- if (
|
|
|
- rawFile.type !== 'image/jpeg' &&
|
|
|
- rawFile.type !== 'image/jpg' &&
|
|
|
- rawFile.type !== 'image/png' &&
|
|
|
- rawFile.type !== 'image/bmp' &&
|
|
|
- rawFile.type !== 'image/gif'
|
|
|
- ) {
|
|
|
- showNotify({
|
|
|
- message: '上传图片必须是JPG/PNG/BMP/GIF类型!',
|
|
|
- type: 'warning'
|
|
|
- })
|
|
|
- return false
|
|
|
- } else if (rawFile.size / 1024 / 1024 > 2) {
|
|
|
- showNotify({
|
|
|
- message: '图片大小不能超过2MB!',
|
|
|
- type: 'warning'
|
|
|
- })
|
|
|
- return false
|
|
|
- }
|
|
|
- const reader = new FileReader()
|
|
|
- reader.readAsDataURL(rawFile)
|
|
|
- reader.onload = () => {
|
|
|
- cropperState.cropperImg = reader.result
|
|
|
- cropperState.cropper.destroy()
|
|
|
- setTimeout(() => {
|
|
|
- initCropper()
|
|
|
- })
|
|
|
- }
|
|
|
return false
|
|
|
- }
|
|
|
- // 打开弹窗
|
|
|
- const openDialog = (imgs: string) => {
|
|
|
- cropperState.cropperImg = imgs
|
|
|
- cropperState.isShowDialog = true
|
|
|
- nextTick(() => {
|
|
|
- initCropper()
|
|
|
+ } else if (rawFile.size / 1024 / 1024 > userImgSize) {
|
|
|
+ showNotify({
|
|
|
+ message: `图片大小不能超过${userImgSize}MB!`,
|
|
|
+ type: 'warning'
|
|
|
})
|
|
|
+ return false
|
|
|
}
|
|
|
- // 初始化cropperjs图片裁剪
|
|
|
- const initCropper = () => {
|
|
|
- const letImg = <HTMLImageElement>document.querySelector('.cropper-warp-left-img')
|
|
|
- cropperState.cropper = new Cropper(letImg, {
|
|
|
- viewMode: 1,
|
|
|
- dragMode: 'move',
|
|
|
- initialAspectRatio: 1,
|
|
|
- aspectRatio: 1,
|
|
|
- preview: '.before',
|
|
|
- background: false,
|
|
|
- autoCropArea: 1,
|
|
|
- cropBoxMovable: true, // 是否允许剪裁框拖动
|
|
|
- cropBoxResizable: false, // 是否允许剪裁框缩放
|
|
|
- zoomable: true,
|
|
|
- ready() {
|
|
|
- cropperState.cropper.setCropBoxData({
|
|
|
- width: 500, // 宽度
|
|
|
- height: 500 // 高度
|
|
|
- })
|
|
|
- },
|
|
|
- crop: () => {
|
|
|
- cropperState.cropperImgBase64 = cropperState.cropper
|
|
|
- .getCroppedCanvas({ width: 500, height: 500 })
|
|
|
- .toDataURL('image/jpeg')
|
|
|
- }
|
|
|
+ const reader = new FileReader()
|
|
|
+ reader.readAsDataURL(rawFile)
|
|
|
+ reader.onload = () => {
|
|
|
+ cropperState.cropperImg = reader.result
|
|
|
+ cropperState.cropper.destroy()
|
|
|
+ setTimeout(() => {
|
|
|
+ initCropper()
|
|
|
})
|
|
|
}
|
|
|
- const editAvatar = () => {
|
|
|
- openDialog(state.form.avatar)
|
|
|
- }
|
|
|
- const onAvatarSubmit = () => {
|
|
|
- const base64Url = cropperState.cropper.getCroppedCanvas({ width: 500, height: 500 }).toDataURL('image/jpeg')
|
|
|
- let file = dataURLtoFile(base64Url, 'avatar.png')
|
|
|
- uploadBaseFunc(file)
|
|
|
- }
|
|
|
- // base64转file
|
|
|
- const dataURLtoFile = (dataurl, filename) => {
|
|
|
- // 获取到base64编码
|
|
|
- const arr = dataurl.split(',')
|
|
|
- // 将base64编码转为字符串
|
|
|
- const bstr = window.atob(arr[1])
|
|
|
- let n = bstr.length
|
|
|
- const u8arr = new Uint8Array(n) // 创建初始化为0的,包含length个元素的无符号整型数组
|
|
|
- while (n--) {
|
|
|
- u8arr[n] = bstr.charCodeAt(n)
|
|
|
+ return false
|
|
|
+}
|
|
|
+// 打开弹窗
|
|
|
+const openDialog = (imgs: string) => {
|
|
|
+ cropperState.cropperImg = imgs
|
|
|
+ cropperState.isShowDialog = true
|
|
|
+ nextTick(() => {
|
|
|
+ initCropper()
|
|
|
+ })
|
|
|
+}
|
|
|
+// 初始化cropperjs图片裁剪
|
|
|
+const initCropper = () => {
|
|
|
+ const letImg = <HTMLImageElement>document.querySelector('.cropper-warp-left-img')
|
|
|
+ cropperState.cropper = new Cropper(letImg, {
|
|
|
+ viewMode: 1,
|
|
|
+ dragMode: 'move',
|
|
|
+ initialAspectRatio: 1,
|
|
|
+ aspectRatio: 1,
|
|
|
+ preview: '.before',
|
|
|
+ background: false,
|
|
|
+ autoCropArea: 1,
|
|
|
+ cropBoxMovable: true, // 是否允许剪裁框拖动
|
|
|
+ cropBoxResizable: false, // 是否允许剪裁框缩放
|
|
|
+ zoomable: true,
|
|
|
+ ready() {
|
|
|
+ cropperState.cropper.setCropBoxData({
|
|
|
+ width: 500, // 宽度
|
|
|
+ height: 500 // 高度
|
|
|
+ })
|
|
|
+ },
|
|
|
+ crop: () => {
|
|
|
+ cropperState.cropperImgBase64 = cropperState.cropper
|
|
|
+ .getCroppedCanvas({ width: 500, height: 500 })
|
|
|
+ .toDataURL('image/jpeg')
|
|
|
}
|
|
|
- return new File([u8arr], filename, {
|
|
|
- type: 'image/png'
|
|
|
- })
|
|
|
+ })
|
|
|
+}
|
|
|
+const editAvatar = () => {
|
|
|
+ openDialog(state.form.avatar)
|
|
|
+}
|
|
|
+const onAvatarSubmit = () => {
|
|
|
+ const base64Url = cropperState.cropper.getCroppedCanvas({ width: 500, height: 500 }).toDataURL('image/jpeg')
|
|
|
+ let file = dataURLtoFile(base64Url, 'avatar.png')
|
|
|
+ uploadBaseFunc(file)
|
|
|
+}
|
|
|
+// base64转file
|
|
|
+const dataURLtoFile = (dataurl, filename) => {
|
|
|
+ // 获取到base64编码
|
|
|
+ const arr = dataurl.split(',')
|
|
|
+ // 将base64编码转为字符串
|
|
|
+ const bstr = window.atob(arr[1])
|
|
|
+ let n = bstr.length
|
|
|
+ const u8arr = new Uint8Array(n) // 创建初始化为0的,包含length个元素的无符号整型数组
|
|
|
+ while (n--) {
|
|
|
+ u8arr[n] = bstr.charCodeAt(n)
|
|
|
}
|
|
|
- // 上传图片获取地址
|
|
|
- const uploadBaseFunc = (file) => {
|
|
|
- const formData = new FormData()
|
|
|
- formData.append('file', file)
|
|
|
- const uploadUrl = import.meta.env.VITE_UPLOAD // 上传的图片服务器地址
|
|
|
- axios
|
|
|
- .post(uploadUrl, formData, {
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'multipart/form-data'
|
|
|
- }
|
|
|
- })
|
|
|
- .then(async (res) => {
|
|
|
- if (res) {
|
|
|
- if (res.data.Code == 200) {
|
|
|
- // 图片上传成功,直接修改
|
|
|
- const [err]: ToResponse = await to(userApi.setAvatar({ fileUrl: res?.data.Data || '' }))
|
|
|
- if (err) return
|
|
|
- showNotify({
|
|
|
- type: 'success',
|
|
|
- message: '修改成功'
|
|
|
- })
|
|
|
- storesUseUserInfo.setUserInfos()
|
|
|
- closeDialog()
|
|
|
- }
|
|
|
+ return new File([u8arr], filename, {
|
|
|
+ type: 'image/png'
|
|
|
+ })
|
|
|
+}
|
|
|
+// 上传图片获取地址
|
|
|
+const uploadBaseFunc = (file) => {
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('file', file)
|
|
|
+ const uploadUrl = import.meta.env.VITE_UPLOAD // 上传的图片服务器地址
|
|
|
+ axios
|
|
|
+ .post(uploadUrl, formData, {
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'multipart/form-data'
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then(async (res) => {
|
|
|
+ if (res) {
|
|
|
+ if (res.data.Code == 200) {
|
|
|
+ // 图片上传成功,直接修改
|
|
|
+ const [err]: ToResponse = await to(userApi.setAvatar({ fileUrl: res?.data.Data || '' }))
|
|
|
+ if (err) return
|
|
|
+ showNotify({
|
|
|
+ type: 'success',
|
|
|
+ message: '修改成功'
|
|
|
+ })
|
|
|
+ storesUseUserInfo.setUserInfos()
|
|
|
+ closeDialog()
|
|
|
}
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ showNotify({
|
|
|
+ type: 'warning',
|
|
|
+ message: '上传失败'
|
|
|
})
|
|
|
- .catch(() => {
|
|
|
- showNotify({
|
|
|
- type: 'warning',
|
|
|
- message: '上传失败'
|
|
|
- })
|
|
|
- })
|
|
|
- }
|
|
|
- // 关闭弹窗
|
|
|
- const closeDialog = () => {
|
|
|
- if (cropperState.cropper) {
|
|
|
- cropperState.cropper.destroy()
|
|
|
- cropperState.isShowDialog = false
|
|
|
- cropperState.cropperImg = ''
|
|
|
- cropperState.cropperImgBase64 = ''
|
|
|
- cropperState.cropper = ''
|
|
|
- cropperState.isShowDialog = false
|
|
|
- }
|
|
|
- }
|
|
|
- const initForm = async () => {
|
|
|
- const [err, res]: ToResponse = await to(userApi.getProfile())
|
|
|
- if (err) return
|
|
|
- state.form = res?.data
|
|
|
- }
|
|
|
- const onSubmit = async () => {
|
|
|
- const [errValid] = await to(formRef.value.validate())
|
|
|
- if (errValid) return
|
|
|
- state.form.userId = state.form.id
|
|
|
- const params = JSON.parse(JSON.stringify(state.form))
|
|
|
- const [err]: ToResponse = await to(userApi.updateProfile(params))
|
|
|
- if (err) return
|
|
|
- showNotify({
|
|
|
- type: 'success',
|
|
|
- message: '修改成功'
|
|
|
})
|
|
|
- storesUseUserInfo.setUserInfos()
|
|
|
- router.push('/user')
|
|
|
+}
|
|
|
+// 关闭弹窗
|
|
|
+const closeDialog = () => {
|
|
|
+ if (cropperState.cropper) {
|
|
|
+ cropperState.cropper.destroy()
|
|
|
+ cropperState.isShowDialog = false
|
|
|
+ cropperState.cropperImg = ''
|
|
|
+ cropperState.cropperImgBase64 = ''
|
|
|
+ cropperState.cropper = ''
|
|
|
+ cropperState.isShowDialog = false
|
|
|
}
|
|
|
- onMounted(() => {
|
|
|
- getDicts()
|
|
|
- initForm()
|
|
|
+}
|
|
|
+const initForm = async () => {
|
|
|
+ const [err, res]: ToResponse = await to(userApi.getProfile())
|
|
|
+ if (err) return
|
|
|
+ state.form = res?.data
|
|
|
+}
|
|
|
+const onSubmit = async () => {
|
|
|
+ const [errValid] = await to(formRef.value.validate())
|
|
|
+ if (errValid) return
|
|
|
+ state.form.userId = state.form.id
|
|
|
+ const params = JSON.parse(JSON.stringify(state.form))
|
|
|
+ const [err]: ToResponse = await to(userApi.updateProfile(params))
|
|
|
+ if (err) return
|
|
|
+ showNotify({
|
|
|
+ type: 'success',
|
|
|
+ message: '修改成功'
|
|
|
})
|
|
|
+ storesUseUserInfo.setUserInfos()
|
|
|
+ router.push('/user')
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ getDicts()
|
|
|
+ initForm()
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
- .app-container {
|
|
|
- header {
|
|
|
- background-color: #1c9bfd;
|
|
|
- color: #fff;
|
|
|
- padding: 10px;
|
|
|
- border-radius: 8px;
|
|
|
- margin-top: 10px;
|
|
|
+.app-container {
|
|
|
+ header {
|
|
|
+ background-color: #1c9bfd;
|
|
|
+ color: #fff;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 8px;
|
|
|
+ margin-top: 10px;
|
|
|
+ display: flex;
|
|
|
+
|
|
|
+ img {
|
|
|
+ width: 100px;
|
|
|
+ height: 100px;
|
|
|
+ border-radius: 50%;
|
|
|
+ margin-right: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .content {
|
|
|
+ flex: 1;
|
|
|
display: flex;
|
|
|
- img {
|
|
|
- width: 100px;
|
|
|
- height: 100px;
|
|
|
- border-radius: 50%;
|
|
|
- margin-right: 10px;
|
|
|
- }
|
|
|
- .content {
|
|
|
- flex: 1;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-around;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .bold {
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
- justify-content: space-around;
|
|
|
- overflow: hidden;
|
|
|
- .bold {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- font-weight: bold;
|
|
|
- // flex-wrap: nowrap;
|
|
|
- white-space: nowrap;
|
|
|
- .van-text-ellipsis {
|
|
|
- flex: 1;
|
|
|
- }
|
|
|
+ align-items: center;
|
|
|
+ font-weight: bold;
|
|
|
+ // flex-wrap: nowrap;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ .van-text-ellipsis {
|
|
|
+ flex: 1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- .avatar {
|
|
|
- vertical-align: middle;
|
|
|
- }
|
|
|
}
|
|
|
- .cropper-warp {
|
|
|
- width: 100%;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- background-color: #eee;
|
|
|
- .cropper-warp-left {
|
|
|
- height: 500px;
|
|
|
- width: 500px;
|
|
|
- }
|
|
|
+
|
|
|
+ .avatar {
|
|
|
+ vertical-align: middle;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.cropper-warp {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background-color: #eee;
|
|
|
+
|
|
|
+ .cropper-warp-left {
|
|
|
+ height: 500px;
|
|
|
+ width: 500px;
|
|
|
}
|
|
|
+}
|
|
|
</style>
|