Commit bc400cf6466031bc159d3eed0da5d0b55b4c1c2c
1 parent
4f5964f2
v3.6.0 ImageUpload组件支持多图片上传
Showing
2 changed files
with
163 additions
and
52 deletions
yudao-ui-admin/src/components/FileUpload/index.vue
| @@ -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 | }; |
yudao-ui-admin/src/components/ImageUpload/index.vue
| @@ -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 { | ||
| 89 | - 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 | - } | 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; | ||
| 99 | } | 203 | } |
| 100 | -</style> | ||
| 101 | \ No newline at end of file | 204 | \ No newline at end of file |
| 205 | + | ||
| 206 | +::v-deep .el-list-enter, .el-list-leave-active { | ||
| 207 | + opacity: 0; | ||
| 208 | + transform: translateY(0); | ||
| 209 | +} | ||
| 210 | +</style> | ||
| 211 | + |