userAvatar.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <template>
  2. <div class="user-info-head" @click="editCropper()">
  3. <img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
  4. <el-dialog v-model="open" :title="title" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
  5. <el-row>
  6. <el-col :xs="24" :md="12" :style="{ height: '350px' }">
  7. <vue-cropper
  8. v-if="visible"
  9. ref="cropper"
  10. :img="options.img"
  11. :info="true"
  12. :auto-crop="options.autoCrop"
  13. :auto-crop-width="options.autoCropWidth"
  14. :auto-crop-height="options.autoCropHeight"
  15. :fixed-box="options.fixedBox"
  16. :output-type="options.outputType"
  17. @real-time="realTime"
  18. />
  19. </el-col>
  20. <el-col :xs="24" :md="12" :style="{ height: '350px' }">
  21. <div class="avatar-upload-preview">
  22. <img :src="options.previews.url" :style="options.previews.img" />
  23. </div>
  24. </el-col>
  25. </el-row>
  26. <br />
  27. <el-row>
  28. <el-col :lg="2" :md="2">
  29. <el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
  30. <el-button>
  31. 选择
  32. <el-icon class="el-icon--right">
  33. <Upload />
  34. </el-icon>
  35. </el-button>
  36. </el-upload>
  37. </el-col>
  38. <el-col :lg="{ span: 1, offset: 2 }" :md="2">
  39. <el-button icon="Plus" @click="changeScale(1)"></el-button>
  40. </el-col>
  41. <el-col :lg="{ span: 1, offset: 1 }" :md="2">
  42. <el-button icon="Minus" @click="changeScale(-1)"></el-button>
  43. </el-col>
  44. <el-col :lg="{ span: 1, offset: 1 }" :md="2">
  45. <el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
  46. </el-col>
  47. <el-col :lg="{ span: 1, offset: 1 }" :md="2">
  48. <el-button icon="RefreshRight" @click="rotateRight()"></el-button>
  49. </el-col>
  50. <el-col :lg="{ span: 2, offset: 6 }" :md="2">
  51. <el-button type="primary" @click="uploadImg()">提 交</el-button>
  52. </el-col>
  53. </el-row>
  54. </el-dialog>
  55. </div>
  56. </template>
  57. <script setup lang="ts">
  58. import { ref, reactive } from 'vue'
  59. import 'vue-cropper/dist/index.css'
  60. import { VueCropper } from 'vue-cropper'
  61. import { uploadAvatarApi } from '@/api/modules/system/user'
  62. import { useUserStore } from '@/stores/modules/user'
  63. import { UploadRawFile } from 'element-plus'
  64. import { ElMessage } from 'element-plus'
  65. interface Options {
  66. img: string | any // 裁剪图片的地址
  67. autoCrop: boolean // 是否默认生成截图框
  68. autoCropWidth: number // 默认生成截图框宽度
  69. autoCropHeight: number // 默认生成截图框高度
  70. fixedBox: boolean // 固定截图框大小 不允许改变
  71. fileName: string
  72. previews: any // 预览数据
  73. outputType: string
  74. visible: boolean
  75. }
  76. const userStore = useUserStore()
  77. // 弹出层
  78. const open = ref(false)
  79. // 是否显示cropper
  80. const visible = ref(false)
  81. // 弹出层标题
  82. const title = ref('修改头像')
  83. const cropper = ref<any>({})
  84. debugger
  85. //图片裁剪数据
  86. const options = reactive<Options>({
  87. img: userStore.avatar,
  88. autoCrop: true,
  89. autoCropWidth: 200,
  90. autoCropHeight: 200,
  91. fixedBox: true,
  92. outputType: 'png',
  93. fileName: '',
  94. previews: {},
  95. visible: false
  96. })
  97. /** 编辑头像 */
  98. const editCropper = () => {
  99. open.value = true
  100. }
  101. /** 打开弹出层结束时的回调 */
  102. const modalOpened = () => {
  103. visible.value = true
  104. }
  105. /** 覆盖默认上传行为 */
  106. // eslint-disable-next-line @typescript-eslint/no-empty-function
  107. const requestUpload: any = () => {}
  108. /** 向左旋转 */
  109. const rotateLeft = () => {
  110. cropper.value.rotateLeft()
  111. }
  112. /** 向右旋转 */
  113. const rotateRight = () => {
  114. cropper.value.rotateRight()
  115. }
  116. /** 图片缩放 */
  117. const changeScale = (num: number) => {
  118. num = num || 1
  119. cropper.value.changeScale(num)
  120. }
  121. /** 上传预处理 */
  122. const beforeUpload = (file: UploadRawFile): any => {
  123. if (file.type.indexOf('image/') == -1) {
  124. ElMessage.error('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。')
  125. } else {
  126. const reader = new FileReader()
  127. reader.readAsDataURL(file)
  128. reader.onload = () => {
  129. options.img = reader.result
  130. options.fileName = file.name
  131. }
  132. }
  133. }
  134. const baseUrl = import.meta.env.VITE_API_URL
  135. /** 上传图片 */
  136. const uploadImg = async () => {
  137. cropper.value.getCropBlob(async (data: any) => {
  138. let formData = new FormData()
  139. formData.append('avatarFile', data, options.fileName)
  140. const res = await uploadAvatarApi(formData)
  141. open.value = false
  142. options.img = res.data.imgUrl
  143. userStore.avatar = baseUrl + options.img
  144. ElMessage.success('修改成功')
  145. visible.value = false
  146. })
  147. }
  148. /** 实时预览 */
  149. const realTime = (data: any) => {
  150. options.previews = data
  151. }
  152. /** 关闭窗口 */
  153. const closeDialog = () => {
  154. options.img = userStore.avatar
  155. options.visible = false
  156. }
  157. </script>
  158. <style lang="scss" scoped>
  159. .user-info-head {
  160. position: relative;
  161. display: inline-block;
  162. // width: 200px;
  163. // height: 200px;
  164. height: 120px;
  165. }
  166. .user-info-head:hover::after {
  167. position: absolute;
  168. inset: 0;
  169. font-size: 24px;
  170. font-style: normal;
  171. line-height: 110px;
  172. color: #eeeeee;
  173. cursor: pointer;
  174. content: '+';
  175. background: rgb(0 0 0 / 50%);
  176. border-radius: 50%;
  177. -webkit-font-smoothing: antialiased;
  178. -moz-osx-font-smoothing: grayscale;
  179. }
  180. </style>