vue 结合 element 实现多图上传组件

简介

多图上传在后台管理系统中的表单和富文本中应用较多。这里实现的有多图的上传,预览,单个的删除等常用功能

主要依赖说明 (先安装,步骤略)

1
{
2
  "element-ui": "2.11.1",
3
  "vue": "^2.6.10",
4
  "vue-router": "^3.0.1"
5
}

正文

1.组件

src/components/MultipleUpload.vue

1
<template>
2
  <div class="upload-container">
3
    <el-tooltip
4
      class="item"
5
      effect="dark"
6
      content="上传图片"
7
      placement="bottom"
8
      :hide-after="800"
9
    >
10
      <el-button
11
        :style="buttonStyle"
12
        icon="el-icon-upload"
13
        size="mini"
14
        type="primary"
15
        @click="showDialog"
16
        >上传图片</el-button
17
      >
18
    </el-tooltip>
19
20
    <el-dialog
21
      title="上传图片"
22
      append-to-body
23
      width="700px"
24
      :visible.sync="dialogVisible"
25
      center
26
    >
27
      <div v-for="(item,index) in imgSrcList" :key="index" class="img-box">
28
        <span class="delete-image" title="点击删除">
29
          <i class="el-icon-delete-solid" @click="deleteImage(index)" />
30
        </span>
31
        <img v-if="item" class="img" :src="item" alt />
32
      </div>
33
      <div class="uploadImg-box">
34
        <input
35
          ref="fileElem"
36
          accept="image/*"
37
          class="img-input"
38
          type="file"
39
          multiple="multiple"
40
          @change="onchange"
41
        />
42
        <el-button
43
          class="btn"
44
          size="small"
45
          type="primary"
46
          @click="handleOpenFile"
47
          >点击上传</el-button
48
        >
49
      </div>
50
51
      <div class="btn-box">
52
        <el-button @click="dialogVisible = false">取 消</el-button>
53
        <el-button :loading="loading" type="primary" @click="handleSubmit"
54
          >确 定</el-button
55
        >
56
      </div>
57
    </el-dialog>
58
  </div>
59
</template>
60
61
<script>
62
  import { Loading } from "element-ui";
63
  import { readFile } from "@/utils/upload"; // 见下文
64
  import { Base64ToBlob } from "@/utils/cos"; // 见下文
65
66
  // 定义的接口根据自己项目更换
67
  import { uploadImage } from "@/api/upload";
68
69
  export default {
70
    name: "MultipleUpload",
71
    props: {
72
      // 最大上传文件的大小
73
      maxFileSize: {
74
        type: Number,
75
        default: 5
76
      },
77
      buttonStyle: {
78
       type: Object,
79
       default: () => ({})
80
     }
81
    },
82
    data() {
83
      return {
84
        dialogVisible: false,
85
        loading: false,
86
        imgSrcList: []
87
      };
88
    },
89
    methods: {
90
      // 打开文件
91
      handleOpenFile() {
92
        const input = this.$refs.fileElem;
93
        // 解决同一个文件不能监听的问题
94
        input.addEventListener(
95
          "click",
96
          function() {
97
            this.value = "";
98
          },
99
          false
100
        );
101
        // 点击input
102
        input.click();
103
      },
104
105
      // 显示弹窗
106
      showDialog() {
107
        this.dialogVisible = true;
108
        this.imgSrcList = [];
109
      },
110
111
      //  监听input上传
112
      async onchange() {
113
        try {
114
          // 文件列表
115
          const files = this.$refs.fileElem.files;
116
          // 文件所有尺寸
117
          const sizes = [];
118
          // 所有文件的base64位地址
119
          const allReadFile = [];
120
          for (let index = 0; index < files.length; index++) {
121
            const item = files[index];
122
            sizes.push(item.size);
123
            allReadFile.push(readFile(item));
124
          }
125
          // 获取最大尺寸检验
126
          const maxSize = Math.max.apply(null, sizes);
127
          if (maxSize > 1024 * 1024 * this.maxFileSize) {
128
            this.$message({
129
              message: `图片不得大于${this.maxFileSize}M`,
130
              type: "warning",
131
              duration: 2000
132
            });
133
            return;
134
          }
135
          // 读取所有文件为base64数据
136
          const base64List = await Promise.all(allReadFile);
137
          this.imgSrcList = [...this.imgSrcList, ...base64List];
138
        } catch (error) {
139
          console.log(error);
140
        }
141
      },
142
143
      // 确定上传
144
      async handleSubmit() {
145
        if (!this.imgSrcList.length) {
146
          this.$message({
147
            message: "请上传图片!",
148
            type: "error"
149
          });
150
          return;
151
        }
152
        // 添加页面loading
153
        const loadingInstance = Loading.service({
154
          fullscreen: true,
155
          text: "上传中..."
156
        });
157
        // 添加按钮loading
158
        this.loading = true;
159
        try {
160
          // 所有blob文件
161
          const blobFiles = [];
162
          // 所有上传图片请求
163
          const allRequest = [];
164
          // Base64 数据转成blob数据
165
          this.imgSrcList.forEach(item => {
166
            const blobFile = Base64ToBlob(item);
167
            blobFiles.push(blobFile);
168
          });
169
170
          // 添加请求
171
          blobFiles.forEach(item => {
172
            allRequest.push(uploadImage(item));
173
          });
174
          // 执行请求拿到结果
175
          const urlList = await Promise.all(allRequest);
176
          // 分发事件
177
          this.$emit("success", urlList);
178
        } catch (error) {
179
          console.log(error, error);
180
        }
181
        // 停止loading关闭弹窗
182
        this.loading = false;
183
        this.dialogVisible = false;
184
        loadingInstance.close();
185
      },
186
187
      // 删除图片
188
      deleteImage(index) {
189
        this.imgSrcList.splice(index, 1);
190
      }
191
    }
192
  };
193
</script>
194
195
<style rel="stylesheet/scss" lang="scss" scoped>
196
  .btn-box {
197
    text-align: right !important;
198
  }
199
200
  .img-box {
201
    position: relative;
202
    display: inline-block;
203
    width: 120px;
204
    margin-right: 10px;
205
    margin-bottom: 10px;
206
    text-align: center;
207
    .img {
208
      width: 100%;
209
    }
210
    .delete-image {
211
      display: none;
212
      .el-icon-delete-solid {
213
        width: 40px;
214
        height: 40px;
215
        line-height: 40px;
216
        position: absolute;
217
        top: 50%;
218
        left: 50%;
219
        transform: translate(-50%, -50%);
220
        font-size: 20px;
221
        color: #000;
222
        font-weight: 900;
223
      }
224
    }
225
226
    &:hover {
227
      .delete-image {
228
        cursor: pointer;
229
        width: 100%;
230
        height: 100%;
231
        position: absolute;
232
        background-color: rgba(255, 255, 255, 0.6);
233
        display: inline-block;
234
      }
235
    }
236
  }
237
  .uploadImg-box {
238
    width: 100%;
239
    height: 40px;
240
    margin: 0 auto;
241
    border-radius: 6px;
242
    position: relative;
243
    margin: 20px 0;
244
    .img-input {
245
      display: none;
246
    }
247
    .btn {
248
      position: absolute;
249
      left: 50%;
250
      top: 50%;
251
      transform: translate(-50%, -50%);
252
    }
253
  }
254
</style>

2.使用

1
<template>
2
  <div>
3
    <multiple-upload @success="handleImageSuccess" />
4
  </div>
5
</template>
6
7
<script>
8
  import MultipleUpload from "@/components/MultipleUpload";
9
10
  export default {
11
    name: "AppForm",
12
    components: {
13
      MultipleUpload
14
    },
15
    methods: {
16
      handleImageSuccess(urlList) {
17
        console.log(urlList);
18
      }
19
    }
20
  };
21
</script>

3.补充 src/utils/upload.js 文件 readFile 方法

1
/**
2
 *
3
 * @param {file} file 源文件
4
 * @desc 读取图片文件为base64文件格式
5
 * @retutn 返回base64文件
6
 */
7
export const readFile = file => {
8
  return new Promise((resolve, reject) => {
9
    const reader = new FileReader();
10
    reader.onload = e => {
11
      const data = e.target.result;
12
      resolve(data);
13
    };
14
    reader.onerror = () => {
15
      const err = new Error("读取图片失败");
16
      reject(err.message);
17
    };
18
19
    reader.readAsDataURL(file);
20
  });
21
};

4.补充 src/utils/cos 文件 Base64ToBlob 方法

1
// base64转换成file文件
2
export function Base64ToBlob(urlData) {
3
  // 去掉url的头,并转换为byte
4
  const bytes = window.atob(urlData.split(",")[1]);
5
6
  // 处理异常,将ascii码小于0的转换为大于0
7
  const ab = new ArrayBuffer(bytes.length);
8
  const ia = new Uint8Array(ab);
9
  for (let i = 0; i < bytes.length; i++) {
10
    ia[i] = bytes.charCodeAt(i);
11
  }
12
  return new Blob([ab], {
13
    type: "image/png"
14
  });
15
}

5.使用效果
在这里插入图片描述