parent
ef20389511
commit
6a655adaee
|
|
@ -1,10 +1,8 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-upload :action="`${path}/cms/mediaFile/upload?category=${props.category}`" :show-file-list="false"
|
||||
:multiple="false" :before-upload="beforeUpload" :on-error="uploadFailure" :on-success="uploadSuccess">
|
||||
<el-button type="primary" plain icon="upload">{{ props.label }}</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-upload :action="`${path}/cms/mediaFile/upload?category=${props.category}`" :show-file-list="false"
|
||||
:multiple="false" :before-upload="beforeUpload" :on-error="uploadFailure" :on-success="uploadSuccess">
|
||||
<el-button type="primary" plain icon="upload">{{ props.label }}</el-button>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,369 @@
|
|||
<template>
|
||||
<div class="gva-cropper-btn">
|
||||
<label :class="`btn btn-${props.btnType}`" :for="`uploads_${props.name}`">
|
||||
<el-icon>
|
||||
<Crop />
|
||||
</el-icon>
|
||||
<span>裁剪上传</span>
|
||||
</label>
|
||||
<input type="file" :id="`uploads_${props.name}`" style="display: none;"
|
||||
accept="image/png, image/jpeg, image/gif, image/jpg, image/webp" @change="handleSelectImg($event)">
|
||||
</div>
|
||||
|
||||
<el-drawer v-model="showDrawer" title="上传头像" direction="ltr" :show-close="false" :close-on-press-escape="false"
|
||||
:close-on-click-modal="false" :size="Math.max(800, Math.min(1500, props.imgWidth + 140))">
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-lg">裁剪上传图片</span>
|
||||
<div>
|
||||
<el-button type="primary" @click="handleConfirm('blob')">确 定</el-button>
|
||||
<el-button @click="showDrawer = false">取 消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="gva-cropper-content flex">
|
||||
<div class="cropper"
|
||||
:style="{ 'width': Math.max(760, Math.min(1500, props.imgWidth + 100)) + 'px', 'min-height': Math.max(Math.min(props.imgHeight + 200, 900), 500) + 'px' }">
|
||||
<div class="gva-btn-list">
|
||||
<el-button @click="changeScale(1)">
|
||||
<el-icon>
|
||||
<ZoomIn />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button @click="changeScale(-1)">
|
||||
<el-icon>
|
||||
<ZoomOut />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button @click="rotateLeft">
|
||||
<el-icon>
|
||||
<RefreshLeft />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button @click="rotateRight">
|
||||
<el-icon>
|
||||
<RefreshRight />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<vue-cropper v-if="showDrawer" ref="vueCropperRef" :outputSize="1" outputType="png" :img="vueCropperImg"
|
||||
:info="true" :full="true" autoCrop :centerBox="props.cropType === 'fixed' ? true : false"
|
||||
:autoCropWidth="props.imgWidth" :autoCropHeight="props.imgHeight"
|
||||
:fixedBox="props.cropType === 'fixed' ? true : false" :fixedNumber="props.fixedNumber" :maxImgSize="2000"
|
||||
@realTime="realTime" />
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import 'vue-cropper/dist/index.css'
|
||||
import VueCropper from "@/components/vueCropper";
|
||||
import { ElMessage } from 'element-plus'
|
||||
import axios from 'axios'
|
||||
|
||||
defineOptions({
|
||||
name: 'CropperImg',
|
||||
})
|
||||
|
||||
const showDrawer = ref(false)
|
||||
const vueCropperImg = ref(null)
|
||||
const vueCropperRef = ref(null)
|
||||
|
||||
const props = defineProps({
|
||||
imgWidth: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
imgHeight: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
category: {
|
||||
required: true,
|
||||
type: String
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: 'media'
|
||||
},
|
||||
cropType: {
|
||||
type: String,
|
||||
default: 'fixed'
|
||||
},
|
||||
outputSize: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
fixedNumber: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [1, 1]
|
||||
}
|
||||
},
|
||||
btnType: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits(['on-success', 'update:modelValue'])
|
||||
|
||||
const changeScale = (num) => {
|
||||
num = num || 1
|
||||
vueCropperRef.value.changeScale(num)
|
||||
}
|
||||
const rotateLeft = () => {
|
||||
vueCropperRef.value.rotateLeft()
|
||||
}
|
||||
const rotateRight = () => {
|
||||
vueCropperRef.value.rotateRight()
|
||||
}
|
||||
// 实时预览函数
|
||||
// const realTime = (data) => {
|
||||
// previews.value = data
|
||||
// }
|
||||
const handleConfirm = (type) => {
|
||||
// 输出
|
||||
if (type === 'blob') {
|
||||
vueCropperRef.value.getCropBlob(async (data) => {
|
||||
doConfirm(data)
|
||||
})
|
||||
} else {
|
||||
vueCropperRef.getCropData(async (data) => {
|
||||
doConfirm(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
// 判断是哪种缩放方式
|
||||
const doConfirm = (cropData) => {
|
||||
const originalBlob = cropData // 原始 Blob 对象
|
||||
const desiredWidth = props.imgWidth// 所需的宽度
|
||||
const desiredHeight = props.imgHeight // 所需的高度
|
||||
if (props.cropType == 'fixed') {
|
||||
// 如果是需要固定宽高的图片,直接调用方法缩放图片
|
||||
handleResizeBlob(originalBlob, desiredWidth, desiredHeight)
|
||||
} else if (props.cropType == 'max') {
|
||||
// 如果是需要不超过xxx宽高的图片,先做判断
|
||||
var blob = new Blob([cropData], { type: 'image/png' })
|
||||
var img = new Image()
|
||||
var url = window.URL.createObjectURL(blob)
|
||||
img.src = url
|
||||
img.onload = function () {
|
||||
// 获取图像的宽度和高度
|
||||
var width = img.width
|
||||
var height = img.height
|
||||
|
||||
if (width <= desiredWidth && height <= desiredHeight) {
|
||||
// 如果裁剪完未超过限制,则直接去上传
|
||||
uploadImage(cropData)
|
||||
} else {
|
||||
// 如果裁剪完超过限制,则调用方法缩放图片
|
||||
handleResizeBlob(originalBlob, desiredWidth, desiredHeight)
|
||||
}
|
||||
// 清理 URL 对象
|
||||
window.URL.revokeObjectURL(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 缩放图片
|
||||
const resizeBlob = (blob, desiredWidth, desiredHeight) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
// 计算缩放比例
|
||||
const scaleX = desiredWidth / img.width
|
||||
const scaleY = desiredHeight / img.height
|
||||
const scale = Math.min(scaleX, scaleY)
|
||||
|
||||
// 设置 Canvas 的宽度和高度
|
||||
canvas.width = desiredWidth
|
||||
canvas.height = desiredHeight
|
||||
|
||||
// 绘制图片到 Canvas 上,并进行缩放
|
||||
ctx.drawImage(img, 0, 0, img.width * scale, img.height * scale)
|
||||
|
||||
// 将 Canvas 中的图像转换为 Blob 对象
|
||||
canvas.toBlob((resizedBlob) => {
|
||||
resolve(resizedBlob)
|
||||
}, blob.type)
|
||||
}
|
||||
|
||||
img.onerror = (error) => {
|
||||
reject(error)
|
||||
}
|
||||
|
||||
img.src = window.URL.createObjectURL(blob)
|
||||
})
|
||||
}
|
||||
// 缩放后上传
|
||||
const handleResizeBlob = (originalBlob, desiredWidth, desiredHeight) => {
|
||||
resizeBlob(originalBlob, desiredWidth, desiredHeight)
|
||||
.then((resizedBlob) => {
|
||||
uploadImage(resizedBlob)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error resizing Blob:', error)
|
||||
})
|
||||
}
|
||||
|
||||
const uploadImage = (data) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', data, props.name + '_image.png'); // 假设使用'image.jpg'作为上传的文件名
|
||||
const path = import.meta.env.VITE_BASE_API + '/cms/mediaFile/upload?category=' + props.category
|
||||
axios.post(path, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
// 处理上传成功的响应
|
||||
if (res.data.code === 0) {
|
||||
emits('on-success', res.data)
|
||||
emits('update:modelValue', res.data.data.url)
|
||||
showDrawer.value = false
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '上传失败'
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理上传失败的错误
|
||||
console.error('上传失败', error);
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '上传失败'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleSelectImg = (e) => {
|
||||
var file = e.target.files[0]
|
||||
if (! /\.(gif|jpg|jpeg|png|bmp|webp|GIF|JPG|PNG)$/.test(e.target.value)) {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '图片类型必须是.gif,jpeg,jpg,png,bmp中的一种'
|
||||
})
|
||||
return false
|
||||
}
|
||||
var reader = new FileReader()
|
||||
reader.onloadend = (e) => {
|
||||
let data
|
||||
if (typeof e.target.result === 'object') {
|
||||
// 把Array Buffer转化为blob 如果是base64不需要
|
||||
data = window.URL.createObjectURL(new Blob([e.target.result]))
|
||||
} else {
|
||||
data = e.target.result
|
||||
}
|
||||
vueCropperImg.value = data
|
||||
showDrawer.value = true
|
||||
|
||||
}
|
||||
reader.readAsArrayBuffer(file)
|
||||
e.target.value = ''
|
||||
}
|
||||
|
||||
// 初始化方法
|
||||
const open = async (data) => {
|
||||
showDrawer.value = true
|
||||
vueCropperImg.value = data
|
||||
}
|
||||
|
||||
// 对外暴露方法
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.gva-cropper-content {
|
||||
justify-content: flex-start;
|
||||
-webkit-justify-content: flex-start;
|
||||
|
||||
.cropper {
|
||||
width: 565px;
|
||||
height: 600px;
|
||||
|
||||
.gva-btn-list {
|
||||
justify-content: center;
|
||||
-webkit-justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.show-preview {
|
||||
margin-right: 15px;
|
||||
|
||||
.preview-title {
|
||||
line-height: 42px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
/*border-radius: 50%;*/
|
||||
border: 2px solid var(--el-border-color-dark);
|
||||
background: #cccccc;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gva-cropper-btn {
|
||||
display: inline-flex;
|
||||
|
||||
.btn {
|
||||
min-width: 108px;
|
||||
display: inline-flex;
|
||||
box-sizing: border-box;
|
||||
line-height: 1;
|
||||
height: 32px;
|
||||
padding: 8px 15px;
|
||||
border-radius: var(--el-border-radius-base);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
outline: none;
|
||||
|
||||
>span {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
border: 1px solid var(--el-border-color);
|
||||
color: var(--el-text-color-regular);
|
||||
background-color: var(--el-fill-color-blank);
|
||||
}
|
||||
|
||||
.btn-default:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
border: 1px solid var(--el-color-primary-light-5);
|
||||
color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
const Exif = {};
|
||||
|
||||
Exif.getData = (img) => new Promise((reslove, reject) => {
|
||||
let obj = {};
|
||||
getImageData(img).then(data => {
|
||||
obj.arrayBuffer = data;
|
||||
obj.orientation = getOrientation(data);
|
||||
reslove(obj)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
|
||||
// 这里的获取exif要将图片转ArrayBuffer对象,这里假设获取了图片的baes64
|
||||
// 步骤一
|
||||
// base64转ArrayBuffer对象
|
||||
function getImageData(img) {
|
||||
let data = null;
|
||||
return new Promise((reslove, reject) => {
|
||||
if (img.src) {
|
||||
if (/^data\:/i.test(img.src)) { // Data URI
|
||||
data = base64ToArrayBuffer(img.src);
|
||||
reslove(data)
|
||||
} else if (/^blob\:/i.test(img.src)) { // Object URL
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function (e) {
|
||||
data = e.target.result;
|
||||
reslove(data)
|
||||
};
|
||||
objectURLToBlob(img.src, function (blob) {
|
||||
fileReader.readAsArrayBuffer(blob);
|
||||
});
|
||||
} else {
|
||||
var http = new XMLHttpRequest();
|
||||
http.onload = function () {
|
||||
if (this.status == 200 || this.status === 0) {
|
||||
data = http.response
|
||||
reslove(data)
|
||||
} else {
|
||||
throw "Could not load image";
|
||||
}
|
||||
http = null;
|
||||
};
|
||||
http.open("GET", img.src, true);
|
||||
http.responseType = "arraybuffer";
|
||||
http.send(null);
|
||||
}
|
||||
} else {
|
||||
reject('img error')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function objectURLToBlob(url, callback) {
|
||||
var http = new XMLHttpRequest();
|
||||
http.open("GET", url, true);
|
||||
http.responseType = "blob";
|
||||
http.onload = function () {
|
||||
if (this.status == 200 || this.status === 0) {
|
||||
callback(this.response);
|
||||
}
|
||||
};
|
||||
http.send();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function base64ToArrayBuffer(base64) {
|
||||
base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
|
||||
var binary = atob(base64);
|
||||
var len = binary.length;
|
||||
var buffer = new ArrayBuffer(len);
|
||||
var view = new Uint8Array(buffer);
|
||||
for (var i = 0; i < len; i++) {
|
||||
view[i] = binary.charCodeAt(i);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
// 步骤二,Unicode码转字符串
|
||||
// ArrayBuffer对象 Unicode码转字符串
|
||||
function getStringFromCharCode(dataView, start, length) {
|
||||
var str = '';
|
||||
var i;
|
||||
for (i = start, length += start; i < length; i++) {
|
||||
str += String.fromCharCode(dataView.getUint8(i));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// 步骤三,获取jpg图片的exif的角度(在ios体现最明显)
|
||||
function getOrientation(arrayBuffer) {
|
||||
var dataView = new DataView(arrayBuffer);
|
||||
var length = dataView.byteLength;
|
||||
var orientation;
|
||||
var exifIDCode;
|
||||
var tiffOffset;
|
||||
var firstIFDOffset;
|
||||
var littleEndian;
|
||||
var endianness;
|
||||
var app1Start;
|
||||
var ifdStart;
|
||||
var offset;
|
||||
var i;
|
||||
// Only handle JPEG image (start by 0xFFD8)
|
||||
if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
|
||||
offset = 2;
|
||||
while (offset < length) {
|
||||
if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
|
||||
app1Start = offset;
|
||||
break;
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
if (app1Start) {
|
||||
exifIDCode = app1Start + 4;
|
||||
tiffOffset = app1Start + 10;
|
||||
if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
|
||||
endianness = dataView.getUint16(tiffOffset);
|
||||
littleEndian = endianness === 0x4949;
|
||||
|
||||
if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
|
||||
if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
|
||||
firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
|
||||
|
||||
if (firstIFDOffset >= 0x00000008) {
|
||||
ifdStart = tiffOffset + firstIFDOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ifdStart) {
|
||||
length = dataView.getUint16(ifdStart, littleEndian);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
offset = ifdStart + i * 12 + 2;
|
||||
if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {
|
||||
|
||||
// 8 is the offset of the current tag's value
|
||||
offset += 8;
|
||||
|
||||
// Get the original orientation value
|
||||
orientation = dataView.getUint16(offset, littleEndian);
|
||||
|
||||
// Override the orientation with its default value for Safari (#120)
|
||||
// if (IS_SAFARI_OR_UIWEBVIEW) {
|
||||
// dataView.setUint16(offset, 1, littleEndian);
|
||||
// }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return orientation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default Exif
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// 导入组件,组件必须声明 name
|
||||
import VueCropper from './src/vue-cropper.vue'
|
||||
|
||||
VueCropper.install = function (Vue) {
|
||||
Vue.component(VueCropper.name, VueCropper)
|
||||
}
|
||||
|
||||
export default VueCropper
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -690,7 +690,6 @@ li {
|
|||
@apply mb-3 flex gap-2 items-center;
|
||||
}
|
||||
|
||||
|
||||
#nprogress .bar {
|
||||
background: #29d !important;
|
||||
}
|
||||
|
|
@ -753,7 +752,7 @@ li {
|
|||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
color: var(--el-text-color-secondary);
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
text-align: center;
|
||||
|
|
|
|||
|
|
@ -57,41 +57,55 @@
|
|||
<el-form-item label="头像" prop="avatar" style="width: 100%">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 10px;"
|
||||
:description="`类型为:${avatarInfo.mediaTypeInfo}, 大小不超过${avatarInfo.fileSizeInfo}, 宽*高: ${avatarInfo.mediaWidth}px*${avatarInfo.mediaHeight}px`" />
|
||||
<el-upload class="avatar-uploader"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=academician_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'avatar') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, avatarInfo) }">
|
||||
<img v-if="editForm.avatar" :src="editForm.avatar" class="avatar" style="object-fit: cover;">
|
||||
<el-icon v-else class="avatar-uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<div class="gva-btn-list" style="width: 100%;">
|
||||
<el-upload class="avatar-uploader"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=academician_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'avatar') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, avatarInfo) }">
|
||||
<el-button type="default" plain icon="upload">直接上传</el-button>
|
||||
</el-upload>
|
||||
<CropperImg name="avatar" :img-width="avatarInfo.mediaWidth" :img-height="avatarInfo.mediaHeight"
|
||||
category="academician_imgs" @on-success="(res) => { uploadSuccess(res, 'avatar') }" />
|
||||
</div>
|
||||
<div>
|
||||
<img v-if="editForm.avatar" :src="editForm.avatar" class="avatar"
|
||||
:style="`width:${avatarInfo.mediaWidth}px;height:${avatarInfo.mediaHeight}px`">
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="介绍视频" prop="videoUrl" style="width: 100%">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 10px;"
|
||||
:description="`类型为:${videoInfo.mediaTypeInfo}, 大小不超过${videoInfo.fileSizeInfo}, 宽*高: ${videoInfo.mediaWidth}px*${videoInfo.mediaHeight}px`" />
|
||||
<el-upload class="avatar-uploader"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=academician_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'video') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, videoInfo) }">
|
||||
<el-button type="primary" plain icon="upload">上传</el-button>
|
||||
</el-upload>
|
||||
<video ref="videoRef" controls="controls" :width="300">
|
||||
<source src />
|
||||
</video>
|
||||
<div class="gva-btn-list" style="width: 100%;">
|
||||
<el-upload class="avatar-uploader"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=academician_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'video') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, videoInfo) }">
|
||||
<el-button type="default" plain icon="upload">上传</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div>
|
||||
<video ref="videoRef" controls="controls" :width="300">
|
||||
<source src />
|
||||
</video>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="企业二维码" prop="qrcodeUrl" style="width: 100%">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 10px;"
|
||||
:title="`类型为:${qrcodeInfo.mediaTypeInfo}, 大小不超过${qrcodeInfo.fileSizeInfo}, 宽*高: ${qrcodeInfo.mediaWidth}px*${qrcodeInfo.mediaHeight}px`" />
|
||||
<el-upload class="avatar-uploader" style="width: 100px; height: 100px"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=academician_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'qrcode') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, qrcodeInfo) }">
|
||||
<img v-if="editForm.qrcodeUrl" :src="editForm.qrcodeUrl" class="avatar" style="object-fit: cover;">
|
||||
<el-icon v-else class="avatar-uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<div class="gva-btn-list" style="width: 100%;">
|
||||
<el-upload class="avatar-uploader"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=academician_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'qrcode') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, qrcodeInfo) }">
|
||||
<el-button type="default" plain icon="upload">直接上传</el-button>
|
||||
</el-upload>
|
||||
<CropperImg name="qrcode" :img-width="qrcodeInfo.mediaWidth" :img-height="qrcodeInfo.mediaHeight"
|
||||
category="academician_imgs" @on-success="(res) => { uploadSuccess(res, 'qrcode') }" />
|
||||
</div>
|
||||
<div>
|
||||
<img v-if="editForm.qrcodeUrl" :src="editForm.qrcodeUrl" class="avatar"
|
||||
:style="`width:${qrcodeInfo.mediaWidth}px;height:${qrcodeInfo.mediaHeight}px`">
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
@ -105,6 +119,7 @@ import { ref, reactive } from 'vue'
|
|||
import { ElMessage } from 'element-plus'
|
||||
import RichEdit from '@/components/richtext/rich-edit.vue'
|
||||
import { isImageMime, isVideoMime, isGifMime, checkImageWHEqual } from '@/utils/image'
|
||||
import CropperImg from '@/components/upload/cropperImg.vue'
|
||||
import {
|
||||
addAcademician,
|
||||
updateAcademician,
|
||||
|
|
@ -120,6 +135,7 @@ const props = defineProps({
|
|||
title: { type: String, default: '' }
|
||||
})
|
||||
|
||||
const cropperImgRef = ref(null)
|
||||
const showDrawer = ref(false)
|
||||
const showErrMessage = ref('')
|
||||
const fullscreenLoading = ref(true)
|
||||
|
|
@ -232,6 +248,7 @@ const qrcodeInfo = {
|
|||
}
|
||||
const videoRef = ref(null)
|
||||
const imgUploadPath = import.meta.env.VITE_BASE_API
|
||||
|
||||
const beforeUpload = (file, info) => {
|
||||
const isLtSize = file.size / 1024 < info.fileSize
|
||||
const isVideo = isVideoMime(file.type)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
:close-on-press-escape="false">
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-lg">{{ !isEdit ? '添加' : '修改' }}企业家</span>
|
||||
<span class="text-lg">{{ !isEdit ? '添加' : '修改' }}人物</span>
|
||||
<div>
|
||||
<el-button type="primary" @click="handleFormSubmit">确 定</el-button>
|
||||
<el-button @click="handleFormClose">取 消</el-button>
|
||||
|
|
@ -57,25 +57,30 @@
|
|||
<el-form-item label="头像" prop="avatar" style="width: 100%">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 10px;"
|
||||
:description="`类型为:${avatarInfo.mediaTypeInfo}, 大小不超过${avatarInfo.fileSizeInfo}, 宽*高: ${avatarInfo.mediaWidth}px*${avatarInfo.mediaHeight}px`" />
|
||||
<el-upload class="avatar-uploader"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=entrepreneur_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'avatar') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, avatarInfo) }">
|
||||
<img v-if="editForm.avatar" :src="editForm.avatar" class="avatar" style="object-fit: cover;">
|
||||
<el-icon v-else class="avatar-uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<div class="gva-btn-list" style="width: 100%;">
|
||||
<el-upload :action="`${imgUploadPath}/cms/mediaFile/upload?category=entrepreneur_imgs`"
|
||||
style="height: 32px;" :show-file-list="false" :on-success="(res) => { uploadSuccess(res, 'avatar') }"
|
||||
:on-error="uploadFailure" :before-upload="(file) => { return beforeUpload(file, avatarInfo) }">
|
||||
<el-button type="default" plain icon="upload">直接上传</el-button>
|
||||
</el-upload>
|
||||
<CropperImg name="avatar" :img-width="avatarInfo.mediaWidth" :img-height="avatarInfo.mediaHeight"
|
||||
category="entrepreneur_imgs" @on-success="(res) => { uploadSuccess(res, 'avatar') }" />
|
||||
</div>
|
||||
<div>
|
||||
<img v-if="editForm.avatar" :src="editForm.avatar" class="avatar"
|
||||
:style="`width:${avatarInfo.mediaWidth}px;height:${avatarInfo.mediaHeight}px`">
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="企业视频" prop="videoUrl" style="width: 100%">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 10px;"
|
||||
:description="`类型为:${videoInfo.mediaTypeInfo}, 大小不超过${videoInfo.fileSizeInfo}, 宽*高: ${videoInfo.mediaWidth}px*${videoInfo.mediaHeight}px`" />
|
||||
<el-upload class="avatar-uploader"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=entrepreneur_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'video') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, videoInfo) }">
|
||||
<el-button type="primary" plain icon="upload">上传</el-button>
|
||||
</el-upload>
|
||||
<div class="gva-btn-list" style="width: 100%;">
|
||||
<el-upload :action="`${imgUploadPath}/cms/mediaFile/upload?category=entrepreneur_imgs`"
|
||||
style="height: 32px;" :show-file-list="false" :on-success="(res) => { uploadSuccess(res, 'video') }"
|
||||
:on-error="uploadFailure" :before-upload="(file) => { return beforeUpload(file, videoInfo) }">
|
||||
<el-button type="default" plain icon="upload">上传</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<video ref="videoRef" controls="controls" :width="300">
|
||||
<source src />
|
||||
</video>
|
||||
|
|
@ -83,15 +88,19 @@
|
|||
<el-form-item label="企业二维码" prop="qrcodeUrl" style="width: 100%">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 10px;"
|
||||
:title="`类型为:${qrcodeInfo.mediaTypeInfo}, 大小不超过${qrcodeInfo.fileSizeInfo}, 宽*高: ${qrcodeInfo.mediaWidth}px*${qrcodeInfo.mediaHeight}px`" />
|
||||
<el-upload class="avatar-uploader" style="width: 100px; height: 100px"
|
||||
:action="`${imgUploadPath}/cms/mediaFile/upload?category=entrepreneur_imgs`" :show-file-list="false"
|
||||
:on-success="(res) => { uploadSuccess(res, 'qrcode') }" :on-error="uploadFailure"
|
||||
:before-upload="(file) => { return beforeUpload(file, qrcodeInfo) }">
|
||||
<img v-if="editForm.qrcodeUrl" :src="editForm.qrcodeUrl" class="avatar" style="object-fit: cover;">
|
||||
<el-icon v-else class="avatar-uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<div class="gva-btn-list" style="width: 100%;">
|
||||
<el-upload :action="`${imgUploadPath}/cms/mediaFile/upload?category=entrepreneur_imgs`"
|
||||
style="height: 32px;" :show-file-list="false" :on-success="(res) => { uploadSuccess(res, 'qrcode') }"
|
||||
:on-error="uploadFailure" :before-upload="(file) => { return beforeUpload(file, qrcodeInfo) }">
|
||||
<el-button type="default" plain icon="upload">直接上传</el-button>
|
||||
</el-upload>
|
||||
<CropperImg name="qrcode" :img-width="qrcodeInfo.mediaWidth" :img-height="qrcodeInfo.mediaHeight"
|
||||
category="entrepreneur_imgs" @on-success="(res) => { uploadSuccess(res, 'qrcode') }" />
|
||||
</div>
|
||||
<div>
|
||||
<img v-if="editForm.qrcodeUrl" :src="editForm.qrcodeUrl" class="avatar"
|
||||
:style="`width:${qrcodeInfo.mediaWidth}px;height:${qrcodeInfo.mediaHeight}px`">
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
@ -102,9 +111,10 @@
|
|||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import RichEdit from '@/components/richtext/rich-edit.vue'
|
||||
import { isImageMime, isVideoMime, isGifMime, checkImageWHEqual } from '@/utils/image'
|
||||
import CropperImg from '@/components/upload/cropperImg.vue'
|
||||
import {
|
||||
addEntrepreneur,
|
||||
updateEntrepreneur,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
</div>
|
||||
<div class="gva-table-box">
|
||||
<div class="gva-btn-list">
|
||||
<el-button type="primary" icon="plus" @click="handleAdd">新增企业家</el-button>
|
||||
<el-button type="primary" icon="plus" @click="handleAdd">新增人物</el-button>
|
||||
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
||||
@click="handleMultiDelete">删除</el-button>
|
||||
</div>
|
||||
|
|
@ -162,7 +162,7 @@ const handleMultiDelete = () => {
|
|||
}
|
||||
// 删除
|
||||
const handleRowDelete = (ID) => {
|
||||
ElMessageBox.confirm('此操作将删除企业家, 是否继续?', '请确认', {
|
||||
ElMessageBox.confirm('此操作将删除人物, 是否继续?', '请确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
|
|
@ -185,12 +185,12 @@ const elEditRef = ref(null)
|
|||
const editTitle = ref('')
|
||||
// 添加
|
||||
const handleAdd = () => {
|
||||
editTitle.value = '添加企业家'
|
||||
editTitle.value = '添加人物'
|
||||
elEditRef.value.openPage({ ID: 0 })
|
||||
}
|
||||
// 修改
|
||||
const handleRowEdit = async (ID) => {
|
||||
editTitle.value = '修改企业家-' + ID
|
||||
editTitle.value = '修改人物-' + ID
|
||||
elEditRef.value.openPage({ ID: ID })
|
||||
}
|
||||
// 保存后
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const toolCards = ref([
|
|||
bg: 'rgba(105, 192, 255,.3)'
|
||||
},
|
||||
{
|
||||
label: '企业家管理',
|
||||
label: '人物管理',
|
||||
icon: 'coordinate',
|
||||
name: 'entrepreneur',
|
||||
color: '#b37feb',
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
@on-before-upload="beforeUpload" />
|
||||
<upload-image category="media" label="图片压缩上传" :file-size="512" :max-w-h="1080" @on-success="uploadSuccess"
|
||||
@on-failure="uploadFailure" @on-before-upload="beforeUpload" />
|
||||
<cropper-img btn-type="primary" :img-width="1080" :img-height="800" category="media" crop-type="max"
|
||||
@on-success="uploadSuccess" />
|
||||
<el-select v-model="search.category" clearable placeholder="请选择分类" style="width: 350px;"
|
||||
@clear="() => { search.category = undefined }">
|
||||
<el-option v-for="(item, key) in mediaFileCategoryOpts" :key="key" :label="item.label" :value="item.value" />
|
||||
|
|
@ -16,7 +18,7 @@
|
|||
</div>
|
||||
|
||||
<el-table :data="tableData">
|
||||
<el-table-column align="left" label="预览" width="150">
|
||||
<el-table-column align="left" fixed label="预览" width="150">
|
||||
<template #default="scope">
|
||||
<el-image v-if="isImage(scope.row.tag)" :src="getUrl(scope.row.url)" class="gva-image" fit="contain"
|
||||
:preview-src-list="[getUrl(scope.row.url)]" :preview-teleported="true"
|
||||
|
|
@ -34,11 +36,6 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" label="日期" prop="UpdatedAt" width="180">
|
||||
<template #default="scope">
|
||||
<div>{{ formatDate(scope.row.UpdatedAt) }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" label="文件名/备注" prop="name" min-width="280" class-name="gva-text-truncate">
|
||||
<template #default="scope">
|
||||
<div class="name" @click="editFileNameFunc(scope.row)">{{ scope.row.name }}</div>
|
||||
|
|
@ -61,7 +58,12 @@
|
|||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" label="操作" width="160">
|
||||
<el-table-column align="left" label="日期" prop="UpdatedAt" width="180">
|
||||
<template #default="scope">
|
||||
<div>{{ formatDate(scope.row.UpdatedAt) }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" fixed="right" label="操作" width="80">
|
||||
<template #default="scope">
|
||||
<!-- <el-button icon="download" type="primary" link @click="downloadFile(scope.row)">下载</el-button> -->
|
||||
<el-button icon="delete" type="danger" link @click="deleteFileFunc(scope.row)">删除</el-button>
|
||||
|
|
@ -81,11 +83,12 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getUrl, isVideoExt } from '@/utils/image'
|
||||
import { getUrl } from '@/utils/image'
|
||||
import { getFileList, deleteFile, editFileName } from '@/api/mediaFile'
|
||||
import { downloadImage } from '@/utils/downloadImg'
|
||||
import UploadImage from '@/components/upload/image.vue'
|
||||
import UploadCommon from '@/components/upload/common.vue'
|
||||
import CropperImg from '@/components/upload/cropperImg.vue'
|
||||
import { getDictFunc, formatDate, filterDict } from '@/utils/format'
|
||||
import WarningBar from '@/components/warningBar/warningBar.vue'
|
||||
import { ref } from 'vue'
|
||||
|
|
|
|||
Loading…
Reference in New Issue