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,8 +2,9 @@ | ||
| 2 | import { reactive, ref, getCurrentInstance, onMounted } from "vue"; | 2 | import { reactive, ref, getCurrentInstance, onMounted } from "vue"; |
| 3 | import { propertyAdd } from "@/api/property"; | 3 | import { propertyAdd } from "@/api/property"; |
| 4 | import { useRoute } from "vue-router"; | 4 | import { useRoute } from "vue-router"; |
| 5 | -import { back } from '@/utils/tool'; | 5 | +import { back } from "@/utils/tool"; |
| 6 | import { validMobile } from "@/utils/validate"; | 6 | import { validMobile } from "@/utils/validate"; |
| 7 | +import Uploader from "@/components/Uploader/index.vue"; | ||
| 7 | import type { ElForm } from "element-plus"; | 8 | import type { ElForm } from "element-plus"; |
| 8 | const { | 9 | const { |
| 9 | appContext: { | 10 | appContext: { |
| @@ -27,6 +28,9 @@ let form = reactive({ | @@ -27,6 +28,9 @@ let form = reactive({ | ||
| 27 | status: 0, | 28 | status: 0, |
| 28 | }, | 29 | }, |
| 29 | }); | 30 | }); |
| 31 | +const handleImageChange = (url: string, tagName: string) => { | ||
| 32 | + form.data[tagName] = url; | ||
| 33 | +}; | ||
| 30 | const ruleFormRef = ref<InstanceType<typeof ElForm>>(); | 34 | const ruleFormRef = ref<InstanceType<typeof ElForm>>(); |
| 31 | /** | 35 | /** |
| 32 | * 手机号校验 | 36 | * 手机号校验 |
| @@ -187,11 +191,7 @@ onMounted(() => { | @@ -187,11 +191,7 @@ onMounted(() => { | ||
| 187 | placeholder="请选择地区" | 191 | placeholder="请选择地区" |
| 188 | clearable | 192 | clearable |
| 189 | > | 193 | > |
| 190 | - <el-option | ||
| 191 | - label="北京" | ||
| 192 | - value="北京" | ||
| 193 | - > | ||
| 194 | - </el-option> | 194 | + <el-option label="北京" value="北京"> </el-option> |
| 195 | </el-select> | 195 | </el-select> |
| 196 | </el-form-item> | 196 | </el-form-item> |
| 197 | <el-form-item label="详细地址" prop="address" style="width: 100%"> | 197 | <el-form-item label="详细地址" prop="address" style="width: 100%"> |
| @@ -206,18 +206,18 @@ onMounted(() => { | @@ -206,18 +206,18 @@ onMounted(() => { | ||
| 206 | prop="businessLicense" | 206 | prop="businessLicense" |
| 207 | style="width: 50%" | 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 | </el-form-item> | 214 | </el-form-item> |
| 215 | <el-form-item label="物业LOGO" prop="logo" style="width: 50%"> | 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 | </el-form-item> | 221 | </el-form-item> |
| 222 | <el-form-item label="合同编号" prop="contractNumber" style="width: 50%"> | 222 | <el-form-item label="合同编号" prop="contractNumber" style="width: 50%"> |
| 223 | <el-input | 223 | <el-input |
| @@ -234,7 +234,10 @@ onMounted(() => { | @@ -234,7 +234,10 @@ onMounted(() => { | ||
| 234 | /> | 234 | /> |
| 235 | </el-form-item> | 235 | </el-form-item> |
| 236 | <el-form-item label="类型" prop="type" style="width: 50%"> | 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 | <el-option label="物业" value="1"></el-option> | 241 | <el-option label="物业" value="1"></el-option> |
| 239 | <el-option label="公益" value="2"></el-option> | 242 | <el-option label="公益" value="2"></el-option> |
| 240 | </el-select> | 243 | </el-select> |
| @@ -246,9 +249,7 @@ onMounted(() => { | @@ -246,9 +249,7 @@ onMounted(() => { | ||
| 246 | <el-button type="info" @click="resetForm(ruleFormRef)" | 249 | <el-button type="info" @click="resetForm(ruleFormRef)" |
| 247 | >取消</el-button | 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 | </el-form-item> | 253 | </el-form-item> |
| 253 | </el-form> | 254 | </el-form> |
| 254 | </el-card> | 255 | </el-card> |