Ver código fonte

ImageUpload组件支持多图片上传

RuoYi 4 anos atrás
pai
commit
7e2dbc5608

+ 150 - 45
ruoyi-ui/src/components/ImageUpload/index.vue

@@ -5,33 +5,38 @@
5 5
       list-type="picture-card"
6 6
       :on-success="handleUploadSuccess"
7 7
       :before-upload="handleBeforeUpload"
8
+      :limit="limit"
8 9
       :on-error="handleUploadError"
10
+      :on-exceed="handleExceed"
9 11
       name="file"
10
-      :show-file-list="false"
12
+      :on-remove="handleRemove"
13
+      :show-file-list="true"
11 14
       :headers="headers"
12
-      style="display: inline-block; vertical-align: top"
15
+      :file-list="fileList"
16
+      :on-preview="handlePictureCardPreview"
17
+      :class="{hide: this.fileList.length >= this.limit}"
13 18
     >
14
-      <el-image v-if="!value" :src="value">
15
-        <div slot="error" class="image-slot">
16
-          <i class="el-icon-plus" />
17
-        </div>
18
-      </el-image>
19
-      <div v-else class="image">
20
-        <el-image :src="value" :style="`width:150px;height:150px;`" fit="fill"/>
21
-        <div class="mask">
22
-          <div class="actions">
23
-            <span title="预览" @click.stop="dialogVisible = true">
24
-              <i class="el-icon-zoom-in" />
25
-            </span>
26
-            <span title="移除" @click.stop="removeImage">
27
-              <i class="el-icon-delete" />
28
-            </span>
29
-          </div>
30
-        </div>
31
-      </div>
19
+      <i class="el-icon-plus"></i>
32 20
     </el-upload>
33
-    <el-dialog :visible.sync="dialogVisible" title="预览" width="800" append-to-body>
34
-      <img :src="value" style="display: block; max-width: 100%; margin: 0 auto;">
21
+    
22
+    <!-- 上传提示 -->
23
+    <div class="el-upload__tip" slot="tip" v-if="showTip">
24
+      请上传
25
+      <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
26
+      <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
27
+      的文件
28
+    </div>
29
+
30
+    <el-dialog
31
+      :visible.sync="dialogVisible"
32
+      title="预览"
33
+      width="800"
34
+      append-to-body
35
+    >
36
+      <img
37
+        :src="dialogImageUrl"
38
+        style="display: block; max-width: 100%; margin: 0 auto"
39
+      />
35 40
     </el-dialog>
36 41
   </div>
37 42
 </template>
@@ -40,36 +45,123 @@
40 45
 import { getToken } from "@/utils/auth";
41 46
 
42 47
 export default {
48
+  props: {
49
+    value: [String, Object, Array],
50
+    // 图片数量限制
51
+    limit: {
52
+      type: Number,
53
+      default: 5,
54
+    },
55
+    // 大小限制(MB)
56
+    fileSize: {
57
+       type: Number,
58
+      default: 5,
59
+    },
60
+    // 文件类型, 例如['png', 'jpg', 'jpeg']
61
+    fileType: {
62
+      type: Array,
63
+      default: () => ["png", "jpg", "jpeg"],
64
+    },
65
+    // 是否显示提示
66
+    isShowTip: {
67
+      type: Boolean,
68
+      default: true
69
+    }
70
+  },
43 71
   data() {
44 72
     return {
73
+      dialogImageUrl: "",
45 74
       dialogVisible: false,
75
+      hideUpload: false,
46 76
       uploadImgUrl: process.env.VUE_APP_BASE_API + "/file/upload", // 上传的图片服务器地址
47 77
       headers: {
48 78
         Authorization: "Bearer " + getToken(),
49 79
       },
80
+      fileList: []
50 81
     };
51 82
   },
52
-  props: {
83
+  watch: {
53 84
     value: {
54
-      type: String,
55
-      default: "",
85
+      handler(val) {
86
+        if (val) {
87
+          // 首先将值转为数组
88
+          const list = Array.isArray(val) ? val : this.value.split(',');
89
+          // 然后将数组转为对象数组
90
+          this.fileList = list.map(item => {
91
+            if (typeof item === "string") {
92
+              item = { name: item, url: item };
93
+            }
94
+            return item;
95
+          });
96
+        } else {
97
+          this.fileList = [];
98
+          return [];
99
+        }
100
+      },
101
+      deep: true,
102
+      immediate: true
103
+    }
104
+  },
105
+  computed: {
106
+    // 是否显示提示
107
+    showTip() {
108
+      return this.isShowTip && (this.fileType || this.fileSize);
56 109
     },
57 110
   },
58 111
   methods: {
59
-    removeImage() {
60
-      this.$emit("input", "");
112
+    // 删除图片
113
+    handleRemove(file, fileList) {
114
+      const findex = this.fileList.indexOf(file.name);
115
+      this.fileList.splice(findex, 1);
116
+      this.$emit("input", this.listToString(this.fileList));
61 117
     },
118
+    // 上传成功回调
62 119
     handleUploadSuccess(res) {
63
-      this.$emit("input", res.data.url);
120
+      this.fileList.push({ name: res.data.url, url: res.data.url });
121
+      this.$emit("input", this.listToString(this.fileList));
64 122
       this.loading.close();
65 123
     },
66
-    handleBeforeUpload() {
124
+    // 上传前loading加载
125
+    handleBeforeUpload(file) {
126
+      let isImg = false;
127
+      if (this.fileType.length) {
128
+        let fileExtension = "";
129
+        if (file.name.lastIndexOf(".") > -1) {
130
+          fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
131
+        }
132
+        isImg = this.fileType.some(type => {
133
+          if (file.type.indexOf(type) > -1) return true;
134
+          if (fileExtension && fileExtension.indexOf(type) > -1) return true;
135
+          return false;
136
+        });
137
+      } else {
138
+        isImg = file.type.indexOf("image") > -1;
139
+      }
140
+
141
+      if (!isImg) {
142
+        this.$message.error(
143
+          `文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
144
+        );
145
+        return false;
146
+      }
147
+      if (this.fileSize) {
148
+        const isLt = file.size / 1024 / 1024 < this.fileSize;
149
+        if (!isLt) {
150
+          this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
151
+          return false;
152
+        }
153
+      }
67 154
       this.loading = this.$loading({
68 155
         lock: true,
69 156
         text: "上传中",
70 157
         background: "rgba(0, 0, 0, 0.7)",
71 158
       });
72 159
     },
160
+    // 文件个数超出
161
+    handleExceed() {
162
+      this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
163
+    },
164
+    // 上传失败
73 165
     handleUploadError() {
74 166
       this.$message({
75 167
         type: "error",
@@ -77,24 +169,37 @@ export default {
77 169
       });
78 170
       this.loading.close();
79 171
     },
80
-  },
81
-  watch: {},
172
+    // 预览
173
+    handlePictureCardPreview(file) {
174
+      this.dialogImageUrl = file.url;
175
+      this.dialogVisible = true;
176
+    },
177
+    // 对象转成指定字符串分隔
178
+    listToString(list, separator) {
179
+      let strs = "";
180
+      separator = separator || ",";
181
+      for (let i in list) {
182
+        strs += list[i].url + separator;
183
+      }
184
+      return strs != '' ? strs.substr(0, strs.length - 1) : '';
185
+    }
186
+  }
82 187
 };
83 188
 </script>
84
-
85 189
 <style scoped lang="scss">
86
-.image {
87
-  position: relative;
88
-  .mask {
190
+// .el-upload--picture-card 控制加号部分
191
+::v-deep.hide .el-upload--picture-card {
192
+    display: none;
193
+}
194
+// 去掉动画效果
195
+::v-deep .el-list-enter-active,
196
+::v-deep .el-list-leave-active {
197
+    transition: all 0s;
198
+}
199
+
200
+::v-deep .el-list-enter, .el-list-leave-active {
89 201
     opacity: 0;
90
-    position: absolute;
91
-    top: 0;
92
-    width: 100%;
93
-    background-color: rgba(0, 0, 0, 0.5);
94
-    transition: all 0.3s;
95
-  }
96
-  &:hover .mask {
97
-    opacity: 1;
98
-  }
202
+    transform: translateY(0);
99 203
 }
100
-</style>
204
+</style>
205
+

+ 2 - 2
ruoyi-ui/src/components/TopNav/index.vue

@@ -73,9 +73,9 @@ export default {
73 73
             if(router.path === "/") {
74 74
               router.children[item].path = "/redirect/" + router.children[item].path;
75 75
             } else {
76
-			  if(!this.ishttp(router.children[item].path)) {
76
+              if(!this.ishttp(router.children[item].path)) {
77 77
                 router.children[item].path = router.path + "/" + router.children[item].path;
78
-			  }
78
+              }
79 79
             }
80 80
             router.children[item].parentPath = router.path;
81 81
           }