parent
250c14b3da
commit
149e71e481
16 changed files with 152 additions and 769 deletions
@ -1,118 +0,0 @@ |
||||
<template> |
||||
<el-upload |
||||
:action="action" |
||||
:headers="{ ...getAuthHeaders(), ...getSiteHeaders() }" |
||||
:accept="accept" |
||||
:before-upload="beforeUpload" |
||||
:on-progress="(event, file) => (progressFile = file)" |
||||
:show-file-list="false" |
||||
:disabled="disabled" |
||||
:multiple="multiple" |
||||
> |
||||
<!-- |
||||
// 用于测试上传进度条 |
||||
action="https://jsonplaceholder.typicode.com/posts/" |
||||
--> |
||||
<el-button type="primary">{{ $t('clickToUpload') }}</el-button> |
||||
</el-upload> |
||||
<el-progress v-if="progressFile.status === 'uploading'" :percentage="parseInt(progressFile.percentage, 10)"></el-progress> |
||||
</template> |
||||
|
||||
<script lang="ts"> |
||||
import { defineComponent, onMounted, ref, toRefs, computed } from 'vue'; |
||||
import { ElMessage } from 'element-plus'; |
||||
import { useI18n } from 'vue-i18n'; |
||||
import { getAuthHeaders } from '@/utils/auth'; |
||||
import { getSiteHeaders } from '@/utils/common'; |
||||
import { imageUploadUrl, videoUploadUrl, docUploadUrl, fileUploadUrl, queryGlobalSettings } from '@/api/config'; |
||||
|
||||
export default defineComponent({ |
||||
name: 'BaseUpload', |
||||
props: { |
||||
type: { |
||||
type: String, |
||||
default: 'file', |
||||
validator: (value: string) => ['image', 'video', 'doc', 'file'].includes(value), |
||||
}, |
||||
uploadAction: { type: String }, |
||||
fileAccept: { type: String }, |
||||
fileMaxSize: { type: Number }, |
||||
multiple: { type: Boolean }, |
||||
disabled: { type: Boolean, default: false }, |
||||
'on-success': { type: Function }, |
||||
}, |
||||
setup(props) { |
||||
const { type, uploadAction, fileAccept, fileMaxSize } = toRefs(props); |
||||
const { t } = useI18n(); |
||||
const progressFile = ref<any>({}); |
||||
const global = ref<any>(); |
||||
const fetchGlobalSettings = async () => { |
||||
global.value = await queryGlobalSettings(); |
||||
}; |
||||
onMounted(() => { |
||||
fetchGlobalSettings(); |
||||
}); |
||||
const action = computed(() => { |
||||
if (uploadAction?.value != null) { |
||||
return uploadAction.value; |
||||
} |
||||
switch (type.value) { |
||||
case 'image': |
||||
return imageUploadUrl; |
||||
case 'video': |
||||
return videoUploadUrl; |
||||
case 'doc': |
||||
return docUploadUrl; |
||||
case 'file': |
||||
return fileUploadUrl; |
||||
default: |
||||
throw new Error(`Type not support: ${type.value}`); |
||||
} |
||||
}); |
||||
const accept = computed(() => { |
||||
if (fileAccept?.value != null) { |
||||
return fileAccept.value; |
||||
} |
||||
switch (type.value) { |
||||
case 'image': |
||||
return global?.value?.upload?.imageInputAccept ?? '.jpg,.jpeg,.png,.gif'; |
||||
case 'video': |
||||
return global?.value?.upload?.videoInputAccept ?? '.mp4,.m3u8'; |
||||
case 'doc': |
||||
return global?.value?.upload?.docInputAccept ?? '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'; |
||||
case 'file': |
||||
return global?.value?.upload?.fileInputAccept ?? '.zip,.7z,.gz,.bz2,.iso,.rar,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.jpg,.jpeg,.png,.gif,.mp4,.m3u8,.mp3,.ogg'; |
||||
default: |
||||
throw new Error(`Type not support: ${type.value}`); |
||||
} |
||||
}); |
||||
const maxSize = computed(() => { |
||||
if (fileMaxSize?.value != null) { |
||||
return fileMaxSize.value; |
||||
} |
||||
switch (type.value) { |
||||
case 'image': |
||||
return global?.value?.upload?.imageLimitByte ?? 0; |
||||
case 'video': |
||||
return global?.value?.upload?.videoLimitByte ?? 0; |
||||
case 'doc': |
||||
return global?.value?.upload?.docLimitByte ?? 0; |
||||
case 'file': |
||||
return global?.value?.upload?.fileLimitByte ?? 0; |
||||
default: |
||||
throw new Error(`Type not support: ${type.value}`); |
||||
} |
||||
}); |
||||
const beforeUpload = (file: any) => { |
||||
if (maxSize.value > 0 && file.size > maxSize.value) { |
||||
ElMessage.error(t('error.fileMaxSize', { size: `${maxSize.value / 1024 / 1024}MB` })); |
||||
return false; |
||||
} |
||||
return true; |
||||
}; |
||||
return { progressFile, getAuthHeaders, getSiteHeaders, action, accept, beforeUpload }; |
||||
}, |
||||
}); |
||||
</script> |
||||
|
||||
<style lang="scss" scoped></style> |
@ -1,136 +0,0 @@ |
||||
<template> |
||||
<div class="w-full"> |
||||
<el-upload :action="fileUploadUrl" |
||||
:headers="{ ...getAuthHeaders(), ...getSiteHeaders() }" |
||||
:accept="accept" |
||||
:before-upload="beforeUpload" |
||||
:on-success="(res) => fileList.push({ name: res.name, url: res.url, length: res.size })" |
||||
:on-progress="(event, file) => (progressFile = file)" |
||||
:show-file-list="false" |
||||
multiple> |
||||
<!-- |
||||
action="https://jsonplaceholder.typicode.com/posts/" |
||||
--> |
||||
<el-button type="primary">{{ $t('clickToUpload') }}</el-button> |
||||
</el-upload> |
||||
<el-progress v-if="progressFile.status === 'uploading'" |
||||
:percentage="parseInt(progressFile.percentage, 10)"></el-progress> |
||||
<transition-group tag="ul" |
||||
:class="['el-upload-list', 'el-upload-list--text', { 'is-disabled': disabled }]" |
||||
name="el-list"> |
||||
<li v-for="file in fileList" |
||||
:key="file.url" |
||||
class="el-upload-list__item is-success"> |
||||
<a class="el-upload-list__item-name" |
||||
@click="handlePreview(file)"> |
||||
<el-icon class="el-icon--document"> |
||||
<Document /> |
||||
</el-icon>{{ file.name }} |
||||
</a> |
||||
<label class="el-upload-list__item-status-label"> |
||||
<el-icon class="el-icon--upload-success el-icon--circle-check"> |
||||
<CircleCheck /> |
||||
</el-icon> |
||||
</label> |
||||
<el-icon v-if="!disabled" |
||||
class="el-icon--close" |
||||
@click="fileList.splice(fileList.indexOf(file), 1)"> |
||||
<Close /> |
||||
</el-icon> |
||||
</li> |
||||
</transition-group> |
||||
<el-dialog :title="$t('article.fileList.attribute')" |
||||
v-model="previewVisible" |
||||
top="5vh" |
||||
:width="768" |
||||
append-to-body> |
||||
<el-form ref="form" |
||||
:model="previewFile" |
||||
label-width="150px"> |
||||
<el-form-item prop="name" |
||||
:label="$t('name')" |
||||
:rules="{ required: true, message: () => $t('v.required') }"> |
||||
<el-input v-model="previewFile.name" |
||||
maxlength="100"></el-input> |
||||
</el-form-item> |
||||
<el-form-item prop="length" |
||||
:label="$t('size')" |
||||
:rules="{ required: true, message: () => $t('v.required') }"> |
||||
<el-input v-model="previewFile.length" |
||||
maxlength="19"> |
||||
<template #append>Byte</template> |
||||
</el-input> |
||||
</el-form-item> |
||||
<el-form-item prop="url" |
||||
label="URL" |
||||
:rules="{ required: true, message: () => $t('v.required') }"> |
||||
<el-input v-model="previewFile.url" |
||||
maxlength="255"></el-input> |
||||
</el-form-item> |
||||
<el-button @click.prevent="handleSubmit()" |
||||
type="primary" |
||||
native-type="submit">{{ $t('submit') }}</el-button> |
||||
</el-form> |
||||
</el-dialog> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { onMounted, ref, toRefs, computed } from 'vue'; |
||||
import { ElMessage } from 'element-plus'; |
||||
import { Close, Document, CircleCheck } from '@element-plus/icons-vue'; |
||||
import { useI18n } from 'vue-i18n'; |
||||
import { getAuthHeaders } from '@/utils/auth'; |
||||
import { getSiteHeaders } from '@/utils/common'; |
||||
import { fileUploadUrl, queryGlobalSettings } from '@/api/config'; |
||||
|
||||
const props = defineProps({ |
||||
modelValue: { type: Array, default: () => [] }, |
||||
fileAccept: { type: String }, |
||||
fileMaxSize: { type: Number }, |
||||
disabled: { type: Boolean, default: false }, |
||||
}); |
||||
const emit = defineEmits({ 'update:modelValue': null }); |
||||
|
||||
const { fileAccept, fileMaxSize } = toRefs(props); |
||||
const { t } = useI18n(); |
||||
const { modelValue } = toRefs(props); |
||||
const progressFile = ref<any>({}); |
||||
const fileList = computed({ |
||||
get: (): any[] => modelValue.value, |
||||
set: (val) => emit('update:modelValue', val), |
||||
}); |
||||
const previewVisible = ref<boolean>(false); |
||||
const previewFile = ref<any>({}); |
||||
const form = ref<any>(); |
||||
|
||||
const handlePreview = (file: any) => { |
||||
previewFile.value = file; |
||||
previewVisible.value = true; |
||||
}; |
||||
const handleSubmit = () => { |
||||
form.value.validate(async (valid: boolean) => { |
||||
if (!valid) return; |
||||
previewVisible.value = false; |
||||
}); |
||||
}; |
||||
const global = ref<any>(); |
||||
const fetchGlobalSettings = async () => { |
||||
global.value = await queryGlobalSettings(); |
||||
}; |
||||
onMounted(() => { |
||||
fetchGlobalSettings(); |
||||
}); |
||||
const defaultAccept = '.zip,.7z,.gz,.bz2,.iso,.rar,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.jpg,.jpeg,.png,.gif,.mp4,.m3u8,.mp3,.ogg'; |
||||
const accept = computed(() => fileAccept?.value ?? global?.value?.upload?.fileInputAccept ?? defaultAccept); |
||||
const maxSize = computed(() => fileMaxSize?.value ?? global?.value?.upload?.fileLimitByte ?? 0); |
||||
const beforeUpload = (file: any) => { |
||||
if (maxSize.value > 0 && file.size > maxSize.value) { |
||||
ElMessage.error(t('error.fileMaxSize', { size: `${maxSize.value / 1024 / 1024}MB` })); |
||||
return false; |
||||
} |
||||
return true; |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped></style> |
@ -1,84 +0,0 @@ |
||||
<template> |
||||
<el-dialog :title="$t('imageCrop')" v-model="visible" @closed="destroyCropper()" top="5vh" :width="768" destroy-on-close append-to-body> |
||||
<div class="text-center"> |
||||
<img ref="imgRef" @load="initCropper()" :src="src" alt="" class="inline" style="max-height:410px" /> |
||||
</div> |
||||
<div class="text-right"> |
||||
<el-button @click.prevent="handleSubmit()" type="primary" native-type="submit" class="mt-4">{{ $t('submit') }}</el-button> |
||||
</div> |
||||
</el-dialog> |
||||
</template> |
||||
|
||||
<script lang="ts"> |
||||
import { computed, defineComponent, ref, toRefs } from 'vue'; |
||||
import Cropper from 'cropperjs'; |
||||
import 'cropperjs/dist/cropper.css'; |
||||
import { cropImage } from '@/api/config'; |
||||
|
||||
export default defineComponent({ |
||||
name: 'ImageCropper', |
||||
props: { |
||||
modelValue: { type: Boolean, required: true }, |
||||
src: { type: String, default: null }, |
||||
width: { type: Number }, |
||||
height: { type: Number }, |
||||
thumbnailWidth: { type: Number }, |
||||
thumbnailHeight: { type: Number }, |
||||
}, |
||||
emits: { 'update:modelValue': null, success: null }, |
||||
setup(props, { emit }) { |
||||
const { modelValue, src, width, height, thumbnailWidth, thumbnailHeight } = toRefs(props); |
||||
const visible = computed({ |
||||
get: () => modelValue.value, |
||||
set: (val) => emit('update:modelValue', val), |
||||
}); |
||||
|
||||
const imgRef = ref<any>(); |
||||
const cropper = ref<any>(); |
||||
const cropParam = ref<any>({}); |
||||
|
||||
const initCropper = () => { |
||||
if (imgRef.value) { |
||||
cropper.value = new Cropper(imgRef.value, { |
||||
aspectRatio: width?.value && height?.value ? width.value / height.value : NaN, |
||||
autoCropArea: width?.value && height?.value ? 1 : 0.8, |
||||
viewMode: 1, |
||||
minCropBoxWidth: width?.value ?? 16, |
||||
minCropBoxHeight: height?.value ?? 16, |
||||
zoomable: false, |
||||
crop(event) { |
||||
cropParam.value.url = src.value; |
||||
cropParam.value.x = Math.floor(event.detail.x); |
||||
cropParam.value.y = Math.floor(event.detail.y); |
||||
cropParam.value.width = Math.floor(event.detail.width); |
||||
cropParam.value.height = Math.floor(event.detail.height); |
||||
cropParam.value.maxWidth = width?.value; |
||||
cropParam.value.maxHeight = height?.value; |
||||
cropParam.value.thumbnailWidth = thumbnailWidth?.value; |
||||
cropParam.value.thumbnailHeight = thumbnailHeight?.value; |
||||
}, |
||||
}); |
||||
} |
||||
}; |
||||
const destroyCropper = () => { |
||||
if (cropper.value) { |
||||
cropper.value.destroy(); |
||||
} |
||||
}; |
||||
const handleSubmit = async () => { |
||||
visible.value = false; |
||||
emit('success', (await cropImage(cropParam.value)).url); |
||||
}; |
||||
return { imgRef, visible, initCropper, destroyCropper, handleSubmit }; |
||||
}, |
||||
}); |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
/* Ensure the size of the image fit the container perfectly */ |
||||
:deep(img) { |
||||
display: block; |
||||
/* This rule is very important, please don't ignore this */ |
||||
max-width: 100%; |
||||
} |
||||
</style> |
@ -1,157 +0,0 @@ |
||||
<template> |
||||
<div> |
||||
<!-- <transition-group tag="ul" :class="['el-upload-list', 'el-upload-list--picture-card', { 'is-disabled': disabled }]" name="el-list"> --> |
||||
<ul :class="['el-upload-list', 'el-upload-list--picture-card', { 'is-disabled': disabled }]"> |
||||
<li v-for="file in fileList" |
||||
:key="file.url" |
||||
class="el-upload-list__item is-success"> |
||||
<div class="w-full h-full bg-gray-50 flex justify-center items-center"> |
||||
<img class="max-w-full max-h-full block" |
||||
:src="file.url" |
||||
alt="" /> |
||||
<div class="full-flex-center absolute rounded-md cursor-default bg-black bg-opacity-50 opacity-0 hover:opacity-100 space-x-4" |
||||
@click.stop> |
||||
<el-icon class="image-action" |
||||
@click="(cropperVisible = true), (currentFile = file)" |
||||
:title="$t('cropImage')"> |
||||
<Crop /> |
||||
</el-icon> |
||||
<el-icon class="image-action" |
||||
@click="handlePreview(file)" |
||||
:title="$t('previewImage')"> |
||||
<View /> |
||||
</el-icon> |
||||
<el-icon class="image-action" |
||||
@click="fileList.splice(fileList.indexOf(file), 1)" |
||||
:title="$t('deleteImage')"> |
||||
<Delete /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
</li> |
||||
</ul> |
||||
<!-- </transition-group> --> |
||||
<el-upload :action="imageUploadUrl" |
||||
:headers="{ ...getAuthHeaders(), ...getSiteHeaders() }" |
||||
:data="getData()" |
||||
:accept="accept" |
||||
:before-upload="beforeUpload" |
||||
:on-success="(res, file) => fileList.push({ name: res.name, url: res.url })" |
||||
:on-progress="(event, file) => (progressFile = file)" |
||||
:show-file-list="false" |
||||
multiple |
||||
class="inline-block"> |
||||
<el-progress v-if="progressFile.status === 'uploading'" |
||||
type="circle" |
||||
:percentage="parseInt(progressFile.percentage, 10)" /> |
||||
<div v-else |
||||
class="el-upload--picture-card"> |
||||
<el-icon> |
||||
<Plus /> |
||||
</el-icon> |
||||
</div> |
||||
</el-upload> |
||||
<div> |
||||
<el-dialog v-model="previewVisible" |
||||
top="5vh" |
||||
:width="768"> |
||||
<el-input v-model="previewFile.url" |
||||
maxlength="255"> |
||||
<template #prepend>URL</template> |
||||
</el-input> |
||||
<el-input v-model="previewFile.description" |
||||
type="textarea" |
||||
:rows="2" |
||||
:placeholder="$t('article.imageList.description')" |
||||
class="mt-1"></el-input> |
||||
<img :src="previewFile.url" |
||||
alt="" |
||||
class="mt-1 border border-gray-300" /> |
||||
</el-dialog> |
||||
</div> |
||||
<image-cropper v-model="cropperVisible" |
||||
:src="currentFile.url" |
||||
:thumbnailWidth="thumbnailWidth" |
||||
:thumbnailHeight="thumbnailHeight" |
||||
@success="(url) => (currentFile.url = url)"></image-cropper> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { onMounted, ref, toRefs, computed } from 'vue'; |
||||
import { ElMessage } from 'element-plus'; |
||||
import { Plus, Crop, View, Delete } from '@element-plus/icons-vue'; |
||||
import { useI18n } from 'vue-i18n'; |
||||
import { getAuthHeaders } from '@/utils/auth'; |
||||
import { getSiteHeaders } from '@/utils/common'; |
||||
import { imageUploadUrl, queryGlobalSettings } from '@/api/config'; |
||||
import ImageCropper from './ImageCropper.vue'; |
||||
|
||||
const props = defineProps({ |
||||
modelValue: { type: Array, default: () => [] }, |
||||
fileAccept: { type: String }, |
||||
fileMaxSize: { type: Number }, |
||||
maxWidth: { type: Number }, |
||||
maxHeight: { type: Number }, |
||||
disabled: { type: Boolean, default: false }, |
||||
}); |
||||
|
||||
const emit = defineEmits({ 'update:modelValue': null }); |
||||
|
||||
const { modelValue, maxWidth, maxHeight, fileAccept, fileMaxSize } = toRefs(props); |
||||
const { t } = useI18n(); |
||||
const progressFile = ref<any>({}); |
||||
const currentFile = ref<any>({}); |
||||
const previewVisible = ref<boolean>(false); |
||||
const cropperVisible = ref<boolean>(false); |
||||
const previewFile = ref<any>({ src: 'data:;base64,=' }); |
||||
const fileList = computed({ |
||||
get: (): any => modelValue.value, |
||||
set: (val) => emit('update:modelValue', val), |
||||
}); |
||||
const handlePreview = (file: any) => { |
||||
previewFile.value = file; |
||||
previewVisible.value = true; |
||||
}; |
||||
const thumbnailWidth = 300; |
||||
const thumbnailHeight = 300; |
||||
const getData = () => { |
||||
const data: any = { isWatermark: true, thumbnailWidth, thumbnailHeight }; |
||||
if (maxWidth?.value != null) { |
||||
data.maxWidth = maxWidth.value; |
||||
} |
||||
if (maxHeight?.value != null) { |
||||
data.maxHeight = maxHeight.value; |
||||
} |
||||
return data; |
||||
}; |
||||
const global = ref<any>(); |
||||
const fetchGlobalSettings = async () => { |
||||
global.value = await queryGlobalSettings(); |
||||
}; |
||||
onMounted(() => { |
||||
fetchGlobalSettings(); |
||||
}); |
||||
const defaultAccept = 'image/jpg,image/jpeg,image/png,image/gif'; |
||||
const accept = computed(() => fileAccept?.value ?? global?.value?.upload?.imageInputAccept ?? defaultAccept); |
||||
const maxSize = computed(() => fileMaxSize?.value ?? global?.value?.upload?.imageLimitByte ?? 0); |
||||
const beforeUpload = (file: any) => { |
||||
if (maxSize.value > 0 && file.size > maxSize.value) { |
||||
ElMessage.error(t('error.fileMaxSize', { size: `${maxSize.value / 1024 / 1024}MB` })); |
||||
return false; |
||||
} |
||||
return true; |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
:deep(.el-dialog__headerbtn) { |
||||
top: 4px; |
||||
} |
||||
.full-flex-center { |
||||
@apply w-full h-full flex justify-center items-center; |
||||
} |
||||
.image-action { |
||||
@apply cursor-pointer text-xl text-white; |
||||
} |
||||
</style> |
@ -1,149 +0,0 @@ |
||||
<template> |
||||
<el-upload :action="imageUploadUrl" |
||||
:headers="{ ...getAuthHeaders(), ...getSiteHeaders() }" |
||||
:accept="accept" |
||||
:before-upload="beforeUpload" |
||||
:data="data" |
||||
:show-file-list="false" |
||||
:on-success="(res) => ((src = res.url), (cropperVisible = mode === 'manual'))" |
||||
:on-progress="(event, file) => (progressFile = file)"> |
||||
<!-- |
||||
// 用于测试上传进度条 |
||||
action="https://jsonplaceholder.typicode.com/posts/" |
||||
--> |
||||
<div v-if="src" |
||||
class="full-flex-center rounded-border relative hover:border-opacity-0"> |
||||
<img :src="src" |
||||
class="max-w-full max-h-full block" /> |
||||
<div class="full-flex-center absolute rounded-md cursor-default bg-black bg-opacity-50 opacity-0 hover:opacity-100 space-x-4" |
||||
@click.stop> |
||||
<el-icon class="image-action" |
||||
@click="cropperVisible = true" |
||||
:title="$t('cropImage')"> |
||||
<Crop /> |
||||
</el-icon> |
||||
<el-icon class="image-action" |
||||
@click="previewVisible = true" |
||||
:title="$t('previewImage')"> |
||||
<View /> |
||||
</el-icon> |
||||
<el-icon class="image-action" |
||||
@click="src = undefined" |
||||
:title="$t('deleteImage')"> |
||||
<Delete /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
<el-progress v-else-if="progressFile.status === 'uploading'" |
||||
type="circle" |
||||
:percentage="parseInt(progressFile.percentage, 10)" /> |
||||
<div v-else |
||||
class="el-upload--picture-card"> |
||||
<el-icon> |
||||
<plus /> |
||||
</el-icon> |
||||
</div> |
||||
</el-upload> |
||||
<div> |
||||
<el-dialog v-model="previewVisible" |
||||
top="5vh" |
||||
:width="768" |
||||
append-to-body |
||||
destroy-on-close> |
||||
<el-input v-model="src"> |
||||
<template #prepend>URL</template> |
||||
</el-input> |
||||
<img :src="src" |
||||
alt="" |
||||
class="mt-1 border border-gray-300" /> |
||||
</el-dialog> |
||||
</div> |
||||
<image-cropper v-model="cropperVisible" |
||||
:src="src" |
||||
:width="width" |
||||
:height="height" |
||||
@success="(url) => (src = url)"></image-cropper> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { computed, onMounted, ref, toRefs } from 'vue'; |
||||
import { ElMessage } from 'element-plus'; |
||||
import { Plus, Crop, View, Delete } from '@element-plus/icons-vue'; |
||||
import { useI18n } from 'vue-i18n'; |
||||
import { getAuthHeaders } from '@/utils/auth'; |
||||
import { getSiteHeaders } from '@/utils/common'; |
||||
import { imageUploadUrl, queryGlobalSettings } from '@/api/config'; |
||||
import ImageCropper from './ImageCropper.vue'; |
||||
|
||||
// 'image/jpg,image/jpeg,image/png,image/gif' |
||||
|
||||
const props = defineProps({ |
||||
modelValue: { type: String, default: null }, |
||||
fileAccept: { type: String }, |
||||
fileMaxSize: { type: Number }, |
||||
width: { type: Number }, |
||||
height: { type: Number }, |
||||
mode: { type: String, default: 'none' }, |
||||
}); |
||||
|
||||
const emit = defineEmits({ 'update:modelValue': null }); |
||||
|
||||
const { modelValue, width, height, mode, fileAccept, fileMaxSize } = toRefs(props); |
||||
const { t } = useI18n(); |
||||
const progressFile = ref<any>({}); |
||||
const previewVisible = ref<boolean>(false); |
||||
const cropperVisible = ref<boolean>(false); |
||||
const src = computed({ |
||||
get: (): string | undefined => modelValue.value, |
||||
set: (val: string | undefined) => emit('update:modelValue', val), |
||||
}); |
||||
const resizable = computed(() => ['cut', 'resize'].includes(mode.value)); |
||||
const data = computed(() => { |
||||
const params: any = { resizeMode: mode.value === 'cut' ? 'cut' : 'normal' }; |
||||
if (width?.value != null) { |
||||
// 为0不限制,为空则依然受全局图片宽高限制 |
||||
params.maxWidth = resizable.value ? width.value : 0; |
||||
} |
||||
if (height?.value != null) { |
||||
// 为0不限制,为空则依然受全局图片宽高限制 |
||||
params.maxHeight = resizable.value ? height.value : 0; |
||||
} |
||||
return params; |
||||
}); |
||||
const global = ref<any>(); |
||||
const fetchGlobalSettings = async () => { |
||||
global.value = await queryGlobalSettings(); |
||||
}; |
||||
onMounted(() => { |
||||
fetchGlobalSettings(); |
||||
}); |
||||
const accept = computed(() => fileAccept?.value ?? global?.value?.upload?.imageInputAccept ?? 'image/jpg,image/jpeg,image/png,image/gif'); |
||||
const maxSize = computed(() => fileMaxSize?.value ?? global?.value?.upload?.imageLimitByte ?? 0); |
||||
const beforeUpload = (file: any) => { |
||||
if (maxSize.value > 0 && file.size > maxSize.value) { |
||||
ElMessage.error(t('error.fileMaxSize', { size: `${maxSize.value / 1024 / 1024}MB` })); |
||||
return false; |
||||
} |
||||
return true; |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
:deep(.el-dialog__headerbtn) { |
||||
top: 4px; |
||||
} |
||||
:deep(.el-upload) { |
||||
width: 148px; |
||||
height: 148px; |
||||
} |
||||
.full-flex-center { |
||||
@apply w-full h-full flex justify-center items-center; |
||||
} |
||||
.rounded-border { |
||||
border: 1px solid #c0ccda; |
||||
@apply rounded-md bg-gray-50; |
||||
} |
||||
.image-action { |
||||
@apply cursor-pointer text-xl text-white; |
||||
} |
||||
</style> |
@ -1,4 +0,0 @@ |
||||
export { default as ImageUpload } from './ImageUpload.vue'; |
||||
export { default as ImageListUpload } from './ImageListUpload.vue'; |
||||
export { default as FileListUpload } from './FileListUpload.vue'; |
||||
export { default as BaseUpload } from './BaseUpload.vue'; |
@ -1,14 +1,10 @@ |
||||
import defaultSettings from '@/settings'; |
||||
import i18n from '@/i18n'; |
||||
|
||||
const { title } = defaultSettings; |
||||
|
||||
export default function getPageTitle(pageTitle: string | undefined): string { |
||||
if (pageTitle) { |
||||
const { |
||||
global: { t }, |
||||
} = i18n; |
||||
return `${t(pageTitle)} - ${title}`; |
||||
return `${pageTitle} - ${title}`; |
||||
} |
||||
return `${title}`; |
||||
} |
||||
|
Loading…
Reference in new issue