Avatar.vue 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <script setup lang="ts">
  2. import type { UploadChangeParam } from 'ant-design-vue';
  3. import { ref, watchEffect } from 'vue';
  4. import { useRequest } from 'alova/client';
  5. import { Modal, Upload } from 'ant-design-vue';
  6. import { uploadFileMethod } from '#/api/method/common';
  7. interface UploadFile extends File {
  8. uid?: string;
  9. status: 'done' | 'error' | 'removed' | 'success' | 'uploading';
  10. thumbUrl: string;
  11. url: string;
  12. }
  13. defineOptions({
  14. inheritAttrs: false,
  15. });
  16. function getBase64(img: Blob, callback: (base64Url: string) => void) {
  17. const reader = new FileReader();
  18. reader.addEventListener('load', () => callback(reader.result as string), {
  19. once: true,
  20. });
  21. reader.readAsDataURL(img);
  22. }
  23. const { loading, send: upload } = useRequest(uploadFileMethod, {
  24. immediate: false,
  25. });
  26. const avatar = defineModel<string | void>('value', { default: void 0 });
  27. const fileList = ref<UploadFile[]>([]);
  28. watchEffect(() => {
  29. const url = avatar.value ?? '';
  30. if (url) {
  31. const name = url.split('/').pop() ?? 'avatar.png';
  32. fileList.value = [{ uid: '-1', status: 'done', name, url } as UploadFile];
  33. } else {
  34. fileList.value = [];
  35. }
  36. });
  37. const previewVisible = ref(false);
  38. const previewImage = ref('');
  39. const handleCancel = () => {
  40. previewVisible.value = false;
  41. };
  42. const onPreviewHandle = async (file: UploadFile) => {
  43. previewImage.value = file.url || file.thumbUrl;
  44. previewVisible.value = true;
  45. };
  46. const onChangeHandle = async (info: UploadChangeParam<UploadFile>) => {
  47. info.file.status ??= 'uploading';
  48. if (info.file.status === 'uploading') {
  49. getBase64(info.file, (thumbUrl) => {
  50. info.file.thumbUrl = thumbUrl;
  51. });
  52. try {
  53. const url = await upload(info.file);
  54. info.file.status = 'done';
  55. avatar.value = url;
  56. } catch {
  57. info.file.status = 'error';
  58. avatar.value = void 0;
  59. }
  60. }
  61. };
  62. const onRemoveHandle = () => {
  63. avatar.value = void 0;
  64. };
  65. </script>
  66. <template>
  67. <div>
  68. <Upload
  69. v-bind="$attrs"
  70. v-model:file-list="fileList"
  71. accept="image/*"
  72. :max-count="1"
  73. name="avatar"
  74. list-type="picture-card"
  75. class="avatar-uploader"
  76. :disabled="loading"
  77. :before-upload="() => false"
  78. @change="onChangeHandle"
  79. @remove="onRemoveHandle"
  80. @preview="onPreviewHandle"
  81. >
  82. <div v-if="!fileList?.length" class="ant-upload-text">上传</div>
  83. </Upload>
  84. <Modal
  85. :open="previewVisible"
  86. title="图片预览"
  87. :footer="null"
  88. @cancel="handleCancel"
  89. >
  90. <img alt="example" style="width: 100%" :src="previewImage" />
  91. </Modal>
  92. </div>
  93. </template>
  94. <style scoped></style>