作者 RuoYi

ImageUpload组件支持多图片上传

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