Commit 6a94a83d27b8402297500a3177910b6c6e5f85d1
1 parent
0ab68841
单图片上传组件
Showing
3 changed files
with
189 additions
and
21 deletions
src/components/Pagination/index.vue
src/components/Uploader/index.vue
0 → 100644
| 1 | +<script setup lang="ts"> | |
| 2 | +/** | |
| 3 | + * 单个图片上传公共组件 | |
| 4 | + * @module Pagination | |
| 5 | + */ | |
| 6 | +import { ref, reactive } from "vue"; | |
| 7 | +import * as store from "@/pinia/index"; | |
| 8 | +import { ElMessage } from "element-plus"; | |
| 9 | +import { Plus } from "@element-plus/icons-vue"; | |
| 10 | +import type { | |
| 11 | + ElUploadProgressEvent, | |
| 12 | + ElFile, | |
| 13 | +} from "element-plus/es/components/upload/src/upload.type"; | |
| 14 | +/** | |
| 15 | + * @description 组件属性 | |
| 16 | + * @prop {string | null} tagName 标签 | |
| 17 | + * @prop {number | null} maxSize 允许上传的最大图片尺寸 default = 1 means 1MB | |
| 18 | + * @prop {array<string>} formats 允许上传的图片格式 | |
| 19 | + * @prop {string | null} defaultUrl 默认图片 | |
| 20 | + * @prop {string | null} moduleName 上传接口需要传的module值 | |
| 21 | + * @prop {string | null} showType 组件类型,横图=horizontal(4:3),竖图=vertical(3:4),正图=square(1:1),小正图=mini(1:1),default=null=""=horizontal | |
| 22 | + * @prop {string} tip 上传须知 | |
| 23 | + */ | |
| 24 | +interface Props { | |
| 25 | + tagName: string; | |
| 26 | + maxSize?: number; | |
| 27 | + formats?: string[]; | |
| 28 | + defaultUrl?: object; | |
| 29 | + showType?: string; | |
| 30 | + moduleName?: string; | |
| 31 | + tip?: string; | |
| 32 | +} | |
| 33 | +const props = withDefaults(defineProps<Props>(), { | |
| 34 | + formats: () => [ | |
| 35 | + "image/jpeg", | |
| 36 | + "image/gif", | |
| 37 | + "image/png", | |
| 38 | + "image/x-icon", | |
| 39 | + "image/svg+xml", | |
| 40 | + "image/webp", | |
| 41 | + ], | |
| 42 | + maxSize: 1, | |
| 43 | + defaultUrl: { | |
| 44 | + type: Object, | |
| 45 | + default: { | |
| 46 | + value: '' | |
| 47 | + } | |
| 48 | + }, | |
| 49 | + showType: "horizontal", | |
| 50 | + moduleName: "test", | |
| 51 | + tip: "支持16:9 的图片,建议最佳尺寸:750*422,大小不能超过500KB", | |
| 52 | +}); | |
| 53 | +/** | |
| 54 | + * @description 上传图片接口配置 | |
| 55 | + */ | |
| 56 | +const uploadSettings = reactive({ | |
| 57 | + uploadUrl: `${import.meta.env.VITE_BASE_URL}upload/uploadPicSource?module=${ | |
| 58 | + props.moduleName | |
| 59 | + }`, | |
| 60 | + headers: { | |
| 61 | + type: "formData", | |
| 62 | + "X-Token": store.family.loginStore().token, | |
| 63 | + }, | |
| 64 | +}); | |
| 65 | +/** | |
| 66 | + * @method handleAvatarSuccess | |
| 67 | + * @description 图片上传成功 | |
| 68 | + * @param {object} res 接口返回信息 | |
| 69 | + */ | |
| 70 | +const handleAvatarSuccess = (res: ElUploadProgressEvent) => { | |
| 71 | + ElMessage.success("图片上传成功"); | |
| 72 | + props.defaultUrl.value = res?.data; | |
| 73 | + emit("imageChange", res?.data, props.tagName); | |
| 74 | +}; | |
| 75 | +/** | |
| 76 | + * @method beforeAvatarUpload | |
| 77 | + * @description 图片上传之前检查格式、最大尺寸 | |
| 78 | + * @param {ElFile} file 文件对象 | |
| 79 | + */ | |
| 80 | +const beforeAvatarUpload = (file: ElFile) => { | |
| 81 | + const isFormatAllowed = props.formats.includes(file.type); | |
| 82 | + const isSizeAllowed = file.size / 1024 / 1024 < props.maxSize; | |
| 83 | + if (!isFormatAllowed) { | |
| 84 | + ElMessage.error("图片格式不支持"); | |
| 85 | + } | |
| 86 | + if (!isSizeAllowed) { | |
| 87 | + ElMessage.error("图片尺寸过大"); | |
| 88 | + } | |
| 89 | + return isFormatAllowed && isSizeAllowed; | |
| 90 | +}; | |
| 91 | +const emit = defineEmits<{ | |
| 92 | + (e: "imageChange", url: string, tagName: string): void; | |
| 93 | +}>(); | |
| 94 | +</script> | |
| 95 | + | |
| 96 | +<template> | |
| 97 | + <div class="uploader-container"> | |
| 98 | + <el-upload | |
| 99 | + class="avatar-uploader" | |
| 100 | + :with-credentials="true" | |
| 101 | + :show-file-list="false" | |
| 102 | + :action="uploadSettings.uploadUrl" | |
| 103 | + :headers="uploadSettings.headers" | |
| 104 | + name="multipartFile" | |
| 105 | + :on-success="handleAvatarSuccess" | |
| 106 | + :before-upload="beforeAvatarUpload" | |
| 107 | + > | |
| 108 | + <img | |
| 109 | + v-if="props.defaultUrl.value" | |
| 110 | + :src="props.defaultUrl.value" | |
| 111 | + class="avatar" | |
| 112 | + :class="props.showType" | |
| 113 | + /> | |
| 114 | + <el-icon v-else class="avatar-uploader-icon" :class="props.showType" | |
| 115 | + ><plus | |
| 116 | + /></el-icon> | |
| 117 | + </el-upload> | |
| 118 | + <div class="tip">{{ props.tip }}</div> | |
| 119 | + </div> | |
| 120 | +</template> | |
| 121 | + | |
| 122 | +<style scoped lang="scss"> | |
| 123 | +.avatar-uploader .el-upload { | |
| 124 | + border: 1px dashed #d9d9d9; | |
| 125 | + border-radius: 6px; | |
| 126 | + cursor: pointer; | |
| 127 | + position: relative; | |
| 128 | + overflow: hidden; | |
| 129 | +} | |
| 130 | +.avatar-uploader .el-upload:hover { | |
| 131 | + border-color: #409eff; | |
| 132 | +} | |
| 133 | +.avatar-uploader-icon { | |
| 134 | + font-size: 28px; | |
| 135 | + color: #8c939d; | |
| 136 | + text-align: center; | |
| 137 | + border: 1px dashed #d9d9d9; | |
| 138 | +} | |
| 139 | +.avatar { | |
| 140 | + display: block; | |
| 141 | +} | |
| 142 | +.tip { | |
| 143 | + color: #606266; | |
| 144 | + font-size: 12px; | |
| 145 | +} | |
| 146 | + | |
| 147 | +.square { | |
| 148 | + width: 178px; | |
| 149 | + height: 178px; | |
| 150 | + line-height: 178px; | |
| 151 | +} | |
| 152 | +.mini { | |
| 153 | + width: 90px; | |
| 154 | + height: 90px; | |
| 155 | + line-height: 90px; | |
| 156 | +} | |
| 157 | +.horizontal { | |
| 158 | + width: 180px; | |
| 159 | + height: 135px; | |
| 160 | + line-height: 135px; | |
| 161 | +} | |
| 162 | +.vertical { | |
| 163 | + width: 135px; | |
| 164 | + height: 180px; | |
| 165 | + line-height: 180px; | |
| 166 | +} | |
| 167 | +</style> | ... | ... |
src/views/property/edit.vue
| ... | ... | @@ -2,8 +2,9 @@ |
| 2 | 2 | import { reactive, ref, getCurrentInstance, onMounted } from "vue"; |
| 3 | 3 | import { propertyAdd } from "@/api/property"; |
| 4 | 4 | import { useRoute } from "vue-router"; |
| 5 | -import { back } from '@/utils/tool'; | |
| 5 | +import { back } from "@/utils/tool"; | |
| 6 | 6 | import { validMobile } from "@/utils/validate"; |
| 7 | +import Uploader from "@/components/Uploader/index.vue"; | |
| 7 | 8 | import type { ElForm } from "element-plus"; |
| 8 | 9 | const { |
| 9 | 10 | appContext: { |
| ... | ... | @@ -27,6 +28,9 @@ let form = reactive({ |
| 27 | 28 | status: 0, |
| 28 | 29 | }, |
| 29 | 30 | }); |
| 31 | +const handleImageChange = (url: string, tagName: string) => { | |
| 32 | + form.data[tagName] = url; | |
| 33 | +}; | |
| 30 | 34 | const ruleFormRef = ref<InstanceType<typeof ElForm>>(); |
| 31 | 35 | /** |
| 32 | 36 | * 手机号校验 |
| ... | ... | @@ -187,11 +191,7 @@ onMounted(() => { |
| 187 | 191 | placeholder="请选择地区" |
| 188 | 192 | clearable |
| 189 | 193 | > |
| 190 | - <el-option | |
| 191 | - label="北京" | |
| 192 | - value="北京" | |
| 193 | - > | |
| 194 | - </el-option> | |
| 194 | + <el-option label="北京" value="北京"> </el-option> | |
| 195 | 195 | </el-select> |
| 196 | 196 | </el-form-item> |
| 197 | 197 | <el-form-item label="详细地址" prop="address" style="width: 100%"> |
| ... | ... | @@ -206,18 +206,18 @@ onMounted(() => { |
| 206 | 206 | prop="businessLicense" |
| 207 | 207 | style="width: 50%" |
| 208 | 208 | > |
| 209 | - <el-input | |
| 210 | - v-model.trim="form.data.businessLicense" | |
| 211 | - placeholder="请填写营业执照" | |
| 212 | - maxlength="1500" | |
| 213 | - /> | |
| 209 | + <Uploader | |
| 210 | + :defaultUrl="{ value: form.data.businessLicense }" | |
| 211 | + tagName="businessLicense" | |
| 212 | + @imageChange="handleImageChange" | |
| 213 | + ></Uploader> | |
| 214 | 214 | </el-form-item> |
| 215 | 215 | <el-form-item label="物业LOGO" prop="logo" style="width: 50%"> |
| 216 | - <el-input | |
| 217 | - v-model.trim="form.data.logo" | |
| 218 | - placeholder="请填写物业LOGO" | |
| 219 | - maxlength="1500" | |
| 220 | - /> | |
| 216 | + <Uploader | |
| 217 | + :defaultUrl="{ value: form.data.logo }" | |
| 218 | + tagName="logo" | |
| 219 | + @imageChange="handleImageChange" | |
| 220 | + ></Uploader> | |
| 221 | 221 | </el-form-item> |
| 222 | 222 | <el-form-item label="合同编号" prop="contractNumber" style="width: 50%"> |
| 223 | 223 | <el-input |
| ... | ... | @@ -234,7 +234,10 @@ onMounted(() => { |
| 234 | 234 | /> |
| 235 | 235 | </el-form-item> |
| 236 | 236 | <el-form-item label="类型" prop="type" style="width: 50%"> |
| 237 | - <el-select v-model="form.data.type" :disabled="form.dataType === 'edit'"> | |
| 237 | + <el-select | |
| 238 | + v-model="form.data.type" | |
| 239 | + :disabled="form.dataType === 'edit'" | |
| 240 | + > | |
| 238 | 241 | <el-option label="物业" value="1"></el-option> |
| 239 | 242 | <el-option label="公益" value="2"></el-option> |
| 240 | 243 | </el-select> |
| ... | ... | @@ -246,9 +249,7 @@ onMounted(() => { |
| 246 | 249 | <el-button type="info" @click="resetForm(ruleFormRef)" |
| 247 | 250 | >取消</el-button |
| 248 | 251 | > |
| 249 | - <el-button type="info" @click="back" | |
| 250 | - >返回</el-button | |
| 251 | - > | |
| 252 | + <el-button type="info" @click="back">返回</el-button> | |
| 252 | 253 | </el-form-item> |
| 253 | 254 | </el-form> |
| 254 | 255 | </el-card> | ... | ... |