UserAvatar.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <script setup lang="ts">
  2. import { ref, reactive, watch } from 'vue'
  3. import 'vue-cropper/dist/index.css'
  4. import { VueCropper } from 'vue-cropper'
  5. import { ElRow, ElCol, ElUpload, ElMessage, ElDialog } from 'element-plus'
  6. import { propTypes } from '@/utils/propTypes'
  7. import { uploadAvatarApi } from '@/api/system/user/profile'
  8. const cropper = ref()
  9. const dialogVisible = ref(false)
  10. const cropperVisible = ref(false)
  11. const props = defineProps({
  12. img: propTypes.string.def('')
  13. })
  14. const options = reactive({
  15. dialogTitle: '编辑头像',
  16. options: {
  17. img: props.img, //裁剪图片的地址
  18. autoCrop: true, // 是否默认生成截图框
  19. autoCropWidth: 200, // 默认生成截图框宽度
  20. autoCropHeight: 200, // 默认生成截图框高度
  21. fixedBox: true // 固定截图框大小 不允许改变
  22. },
  23. previews: {
  24. img: '',
  25. url: ''
  26. }
  27. })
  28. /** 编辑头像 */
  29. const editCropper = () => {
  30. dialogVisible.value = true
  31. }
  32. // 打开弹出层结束时的回调
  33. const modalOpened = () => {
  34. cropperVisible.value = true
  35. }
  36. /** 向左旋转 */
  37. const rotateLeft = () => {
  38. cropper.value.rotateLeft()
  39. }
  40. /** 向右旋转 */
  41. const rotateRight = () => {
  42. cropper.value.rotateRight()
  43. }
  44. /** 图片缩放 */
  45. const changeScale = (num: number) => {
  46. num = num || 1
  47. cropper.value.changeScale(num)
  48. }
  49. // 覆盖默认的上传行为
  50. const requestUpload: any = () => {}
  51. /** 上传预处理 */
  52. const beforeUpload = (file: Blob) => {
  53. if (file.type.indexOf('image/') == -1) {
  54. ElMessage('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。')
  55. } else {
  56. const reader = new FileReader()
  57. reader.readAsDataURL(file)
  58. reader.onload = () => {
  59. if (reader.result) {
  60. options.options.img = reader.result as string
  61. }
  62. }
  63. }
  64. }
  65. /** 上传图片 */
  66. const uploadImg = () => {
  67. cropper.value.getCropBlob((data: any) => {
  68. let formData = new FormData()
  69. formData.append('avatarfile', data)
  70. uploadAvatarApi(formData)
  71. })
  72. }
  73. /** 实时预览 */
  74. const realTime = (data: any) => {
  75. options.previews = data
  76. }
  77. watch(
  78. () => props.img,
  79. () => {
  80. if (props.img) {
  81. options.options.img = props.img
  82. options.previews.img = props.img
  83. options.previews.url = props.img
  84. }
  85. }
  86. )
  87. </script>
  88. <template>
  89. <div class="user-info-head" @click="editCropper()">
  90. <img :src="options.options.img" title="点击上传头像" class="img-circle img-lg" alt="" />
  91. </div>
  92. <el-dialog
  93. v-model="dialogVisible"
  94. :title="options.dialogTitle"
  95. width="800px"
  96. append-to-body
  97. style="padding: 30px 20px"
  98. @opened="modalOpened"
  99. >
  100. <el-row>
  101. <el-col :xs="24" :md="12" style="height: 350px">
  102. <VueCropper
  103. ref="cropper"
  104. :img="options.options.img"
  105. :info="true"
  106. :autoCrop="options.options.autoCrop"
  107. :autoCropWidth="options.options.autoCropWidth"
  108. :autoCropHeight="options.options.autoCropHeight"
  109. :fixedBox="options.options.fixedBox"
  110. @real-time="realTime"
  111. v-if="cropperVisible"
  112. />
  113. </el-col>
  114. <el-col :xs="24" :md="12" style="height: 350px">
  115. <div class="avatar-upload-preview">
  116. <img
  117. :src="options.previews.url"
  118. :style="options.previews.img"
  119. style="!max-width: 100%"
  120. alt=""
  121. />
  122. </div>
  123. </el-col>
  124. </el-row>
  125. <template #footer>
  126. <el-row>
  127. <el-col :lg="2" :md="2">
  128. <el-upload
  129. action="#"
  130. :http-request="requestUpload"
  131. :show-file-list="false"
  132. :before-upload="beforeUpload"
  133. >
  134. <el-button size="small">
  135. <Icon icon="ep:upload-filled" class="mr-5px" />
  136. 选择
  137. </el-button>
  138. </el-upload>
  139. </el-col>
  140. <el-col :lg="{ span: 1, offset: 2 }" :md="2">
  141. <el-button size="small" @click="changeScale(1)">
  142. <Icon icon="ep:zoom-in" class="mr-5px" />
  143. </el-button>
  144. </el-col>
  145. <el-col :lg="{ span: 1, offset: 1 }" :md="2">
  146. <el-button size="small" @click="changeScale(-1)">
  147. <Icon icon="ep:zoom-out" class="mr-5px" />
  148. </el-button>
  149. </el-col>
  150. <el-col :lg="{ span: 1, offset: 1 }" :md="2">
  151. <el-button size="small" @click="rotateLeft()">
  152. <Icon icon="ep:arrow-left-bold" class="mr-5px" />
  153. </el-button>
  154. </el-col>
  155. <el-col :lg="{ span: 1, offset: 1 }" :md="2">
  156. <el-button size="small" @click="rotateRight()">
  157. <Icon icon="ep:arrow-right-bold" class="mr-5px" />
  158. </el-button>
  159. </el-col>
  160. <el-col :lg="{ span: 2, offset: 6 }" :md="2">
  161. <el-button size="small" type="primary" @click="uploadImg()">提 交</el-button>
  162. </el-col>
  163. </el-row>
  164. </template>
  165. </el-dialog>
  166. </template>
  167. <style scoped>
  168. .user-info-head {
  169. position: relative;
  170. display: inline-block;
  171. }
  172. .img-circle {
  173. border-radius: 50%;
  174. }
  175. .img-lg {
  176. width: 120px;
  177. height: 120px;
  178. }
  179. .avatar-upload-preview {
  180. position: absolute;
  181. top: 50%;
  182. -webkit-transform: translate(50%, -50%);
  183. transform: translate(50%, -50%);
  184. width: 200px;
  185. height: 200px;
  186. border-radius: 50%;
  187. -webkit-box-shadow: 0 0 4px #ccc;
  188. box-shadow: 0 0 4px #ccc;
  189. overflow: hidden;
  190. }
  191. .user-info-head:hover:after {
  192. content: '+';
  193. position: absolute;
  194. left: 0;
  195. right: 0;
  196. top: 0;
  197. bottom: 0;
  198. color: #eee;
  199. background: rgba(0, 0, 0, 0.5);
  200. font-size: 24px;
  201. font-style: normal;
  202. -webkit-font-smoothing: antialiased;
  203. -moz-osx-font-smoothing: grayscale;
  204. cursor: pointer;
  205. line-height: 110px;
  206. border-radius: 50%;
  207. }
  208. </style>