添加灯箱预览功能
This commit is contained in:
@@ -126,7 +126,16 @@ func ApiFiles(r *gin.RouterGroup) {
|
|||||||
models.DB.Create(&fund_file_info) // 传入指针
|
models.DB.Create(&fund_file_info) // 传入指针
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnJson(ctx, "apiOK", nil)
|
//返回后台存储的URL
|
||||||
|
download_URL := path.Join("/api/files/download/", hash_str)
|
||||||
|
get_URL := path.Join("/api/files/get/", hash_str)
|
||||||
|
re := map[string]interface{}{
|
||||||
|
"download": download_URL,
|
||||||
|
"get": get_URL,
|
||||||
|
"hash": hash_str,
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnJson(ctx, "apiOK", re)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ReturnJson(ctx, "file_hash_err", nil)
|
ReturnJson(ctx, "file_hash_err", nil)
|
||||||
@@ -155,7 +164,7 @@ func ApiFiles(r *gin.RouterGroup) {
|
|||||||
ReturnJson(ctx, "userCookieError", nil)
|
ReturnJson(ctx, "userCookieError", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnJson(ctx, "apiErr", nil)
|
//ReturnJson(ctx, "apiErr", nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
// r.GET("/upload", func(ctx *gin.Context) {
|
// r.GET("/upload", func(ctx *gin.Context) {
|
||||||
|
|||||||
Generated
+17
@@ -38,6 +38,8 @@
|
|||||||
"filepond-plugin-image-preview": "^4.6.12",
|
"filepond-plugin-image-preview": "^4.6.12",
|
||||||
"filepond-plugin-image-resize": "^2.0.10",
|
"filepond-plugin-image-resize": "^2.0.10",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
|
"fslightbox": "^3.7.4",
|
||||||
|
"fslightbox-vue": "^3.0.3",
|
||||||
"litepicker": "^2.0.12",
|
"litepicker": "^2.0.12",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"tom-select": "^2.4.3",
|
"tom-select": "^2.4.3",
|
||||||
@@ -2833,6 +2835,21 @@
|
|||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fslightbox": {
|
||||||
|
"version": "3.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fslightbox/-/fslightbox-3.7.4.tgz",
|
||||||
|
"integrity": "sha512-zQqMHxiYkR0W/xrWQlchoO626C5KCM6rabpMWiJsy+MZCMHo7zlywsGAOGeOahRUqBZzXT9OeMddiVSfW77gaA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/fslightbox-vue": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fslightbox-vue/-/fslightbox-vue-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-+INqEhEqQ6U2380GrMHcO5KVRNjeYLWGd0l+EOIgXklY8V94gwgF9lXLMtgp0sHxMl6LSfm56IV/LcKbldPf9A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": ">=2.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
"filepond-plugin-image-preview": "^4.6.12",
|
"filepond-plugin-image-preview": "^4.6.12",
|
||||||
"filepond-plugin-image-resize": "^2.0.10",
|
"filepond-plugin-image-resize": "^2.0.10",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
|
"fslightbox": "^3.7.4",
|
||||||
|
"fslightbox-vue": "^3.0.3",
|
||||||
"litepicker": "^2.0.12",
|
"litepicker": "^2.0.12",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"tom-select": "^2.4.3",
|
"tom-select": "^2.4.3",
|
||||||
|
|||||||
@@ -1,219 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import "cropperjs";
|
|
||||||
import { computed, ref } from "vue";
|
|
||||||
|
|
||||||
const fileObj = ref({});
|
|
||||||
|
|
||||||
const croppercanvas = ref();
|
|
||||||
const cropperimage = ref();
|
|
||||||
const cropperselection = ref();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 选区逻辑
|
|
||||||
*/
|
|
||||||
// 是否正在开始选区
|
|
||||||
const isCropperSelection = ref(false);
|
|
||||||
const isCropperMove = ref(true);
|
|
||||||
|
|
||||||
// 判断当前是移动还是选区
|
|
||||||
const currentType = computed(() => (isCropperMove.value ? "move" : "select"));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 按钮方法
|
|
||||||
*/
|
|
||||||
// 旋转
|
|
||||||
function handleRotate() {
|
|
||||||
cropperimage.value.$rotate("90deg");
|
|
||||||
cropperimage.value.$center("contain");
|
|
||||||
}
|
|
||||||
// 裁剪
|
|
||||||
function handleCropper() {
|
|
||||||
isCropperMove.value = false;
|
|
||||||
if (isCropperMove.value) {
|
|
||||||
cropperselection.value.$clear();
|
|
||||||
} else {
|
|
||||||
const cropperCanvas = croppercanvas.value;
|
|
||||||
const cropperCanvasRect = cropperCanvas.getBoundingClientRect();
|
|
||||||
|
|
||||||
const cropperImage = cropperimage.value;
|
|
||||||
const cropperImageRect = cropperImage.getBoundingClientRect();
|
|
||||||
const maxSelection = {
|
|
||||||
x: cropperImageRect.left - cropperCanvasRect.left,
|
|
||||||
y: cropperImageRect.top - cropperCanvasRect.top,
|
|
||||||
width: cropperImageRect.width,
|
|
||||||
height: cropperImageRect.height,
|
|
||||||
};
|
|
||||||
cropperselection.value.$change(
|
|
||||||
maxSelection.x,
|
|
||||||
maxSelection.y,
|
|
||||||
maxSelection.width,
|
|
||||||
maxSelection.height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 移动
|
|
||||||
function handleMove() {
|
|
||||||
if (!isCropperMove.value) {
|
|
||||||
isCropperMove.value = true;
|
|
||||||
// 如果想要点击移动,清除选区,可以打开下面的代码注释
|
|
||||||
// cropperselection.value.$clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 监听选择区变化
|
|
||||||
* @param event
|
|
||||||
*/
|
|
||||||
function onCropperSelectionChange(event) {
|
|
||||||
if (event.detail.width && event.detail.height) {
|
|
||||||
isCropperSelection.value = true;
|
|
||||||
} else {
|
|
||||||
isCropperSelection.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 确认裁剪
|
|
||||||
*/
|
|
||||||
const emit = defineEmits(["success"]);
|
|
||||||
async function handleConfirm() {
|
|
||||||
if (isCropperSelection.value) {
|
|
||||||
const res = await cropperselection.value.$toCanvas();
|
|
||||||
|
|
||||||
const dataImage = res.toDataURL("image/png");
|
|
||||||
const file = dataURLtoFile(dataImage, fileObj.value.name);
|
|
||||||
emit("success", {
|
|
||||||
...fileObj.value,
|
|
||||||
file: file,
|
|
||||||
fileShow: dataImage,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 将data:image转成新的file
|
|
||||||
function dataURLtoFile(dataurl, filename) {
|
|
||||||
var arr = dataurl.split(","),
|
|
||||||
mime = arr[0].match(/:(.*?);/)[1],
|
|
||||||
bstr = atob(arr[1]),
|
|
||||||
n = bstr.length,
|
|
||||||
u8arr = new Uint8Array(n);
|
|
||||||
while (n--) {
|
|
||||||
u8arr[n] = bstr.charCodeAt(n);
|
|
||||||
}
|
|
||||||
const blob = new Blob([u8arr], { type: mime });
|
|
||||||
const file = new File([blob], filename, { type: mime });
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件上传
|
|
||||||
*/
|
|
||||||
const input_form = ref();
|
|
||||||
function handleUploadSuccess() {
|
|
||||||
const files = input_form.value.files;
|
|
||||||
|
|
||||||
if (files.length) {
|
|
||||||
fileObj.value = {
|
|
||||||
name: files[0].name,
|
|
||||||
file: files[0],
|
|
||||||
fileShow: URL.createObjectURL(files[0]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="basic_container">
|
|
||||||
<div class="tool_wrap">
|
|
||||||
<button :class="{ active: isCropperMove }" @click="handleMove">
|
|
||||||
移动
|
|
||||||
</button>
|
|
||||||
<button @click="handleRotate">旋转</button>
|
|
||||||
<button :class="{ active: isCropperSelection }" @click="handleCropper">
|
|
||||||
{{ isCropperSelection ? "重置选区" : "裁剪" }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="dialog_wrap">
|
|
||||||
<div class="image_wrap" ref="imageWrap">
|
|
||||||
<cropper-canvas ref="croppercanvas" background>
|
|
||||||
<cropper-image
|
|
||||||
:src="fileObj.fileShow"
|
|
||||||
alt="Picture"
|
|
||||||
ref="cropperimage"
|
|
||||||
rotatable
|
|
||||||
scalable
|
|
||||||
skewable
|
|
||||||
translatable
|
|
||||||
></cropper-image>
|
|
||||||
|
|
||||||
|
|
||||||
<cropper-shade hidden ref="cropperShade"></cropper-shade>
|
|
||||||
<cropper-handle :action="currentType" plain></cropper-handle>
|
|
||||||
<cropper-selection
|
|
||||||
id="cropperSelection"
|
|
||||||
ref="cropperselection"
|
|
||||||
movable
|
|
||||||
resizable
|
|
||||||
hidden
|
|
||||||
outlined
|
|
||||||
@change="onCropperSelectionChange"
|
|
||||||
>
|
|
||||||
<cropper-crosshair centered />
|
|
||||||
<cropper-handle
|
|
||||||
action="move"
|
|
||||||
theme-color="rgba(255, 255, 255, 0.35)"
|
|
||||||
/>
|
|
||||||
<cropper-handle action="n-resize" />
|
|
||||||
<cropper-handle action="e-resize" />
|
|
||||||
<cropper-handle action="s-resize" />
|
|
||||||
<cropper-handle action="w-resize" />
|
|
||||||
<cropper-handle action="ne-resize" />
|
|
||||||
<cropper-handle action="nw-resize" />
|
|
||||||
<cropper-handle action="se-resize" />
|
|
||||||
<cropper-handle action="sw-resize" />
|
|
||||||
</cropper-selection>
|
|
||||||
</cropper-canvas>
|
|
||||||
</div>
|
|
||||||
<div class="info_wrap">
|
|
||||||
<div class="cropper_preview">
|
|
||||||
<cropper-viewer
|
|
||||||
selection="#cropperSelection"
|
|
||||||
style="width: 200px"
|
|
||||||
></cropper-viewer>
|
|
||||||
</div>
|
|
||||||
<div class="btn_wrap">
|
|
||||||
<input type="file" ref="input_form" @change="handleUploadSuccess" />
|
|
||||||
<button type="primary" @click="handleConfirm">确 认</button>
|
|
||||||
</div>
|
|
||||||
点击确认后,看控制台,有信息
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.dialog_wrap {
|
|
||||||
display: flex;
|
|
||||||
.image_wrap {
|
|
||||||
width: 400px;
|
|
||||||
height: 300px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
cropper-canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.info_wrap {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
& + button {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button.active {
|
|
||||||
background-color: #c6dff8;
|
|
||||||
border-color: #409eff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
|
|
||||||
import { Modal } from "@tabler/core";
|
|
||||||
import { onMounted, ref } from "vue";
|
|
||||||
|
|
||||||
import "@/libs/cropper.min.js"
|
|
||||||
|
|
||||||
|
|
||||||
const avata_toolt=ref()
|
|
||||||
|
|
||||||
const avatar_toolt_moda=ref()
|
|
||||||
|
|
||||||
const cropper=ref()
|
|
||||||
|
|
||||||
const image = document.getElementById('cropper-image');
|
|
||||||
// 初始化Cropper
|
|
||||||
function initCropper(imageSrc) {
|
|
||||||
if (cropper.value) {
|
|
||||||
cropper.value.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
image.src = imageSrc;
|
|
||||||
cropper.value = new Cropper(image, {
|
|
||||||
aspectRatio: 1,
|
|
||||||
viewMode: 2,
|
|
||||||
autoCropArea: 0.8,
|
|
||||||
zoomable: true,
|
|
||||||
zoomOnWheel: true,
|
|
||||||
zoomOnTouch: true,
|
|
||||||
wheelZoomRatio: 0.1,
|
|
||||||
//minCanvasWidth: 400,
|
|
||||||
//minCanvasHeight: 400,
|
|
||||||
crop: updatePreview,
|
|
||||||
ready() {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function inputfile(e){
|
|
||||||
const file = e.target.files[0];
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
if (!file.type.startsWith('image/')) {
|
|
||||||
showMessage('⚠️ 请选择有效的图片文件', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = () => {
|
|
||||||
initCropper(reader.result);
|
|
||||||
currentScale = 1;
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(()=>{
|
|
||||||
//console.log(avata_toolt)
|
|
||||||
avatar_toolt_moda.value=new Modal(avata_toolt.value)
|
|
||||||
avatar_toolt_moda.value.show()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
|
|
||||||
<div class="modal modal-blur fade" ref="avata_toolt" tabindex="-1" role="dialog" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="container">
|
|
||||||
<h1>头像裁剪工具</h1>
|
|
||||||
|
|
||||||
<div class="flex-wrapper">
|
|
||||||
<!-- 左侧裁剪区 -->
|
|
||||||
<div class="crop-section">
|
|
||||||
<div id="image-wrapper">
|
|
||||||
<img id="cropper-image"
|
|
||||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
|
|
||||||
</div>
|
|
||||||
<!-- 上传进度 -->
|
|
||||||
<div class="progress-container">
|
|
||||||
<div class="progress-bar"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 消息提示 -->
|
|
||||||
<div id="message" class="alertavater"></div>
|
|
||||||
|
|
||||||
<div class="preview-stats">
|
|
||||||
<!-- <p>当前缩放: <span id="zoomValue">100%</span></p> -->
|
|
||||||
<p>图片尺寸: <span id="imageSize">0 x 0</span></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 右侧预览区 -->
|
|
||||||
<div class="preview-section">
|
|
||||||
<h3>实时预览</h3>
|
|
||||||
<div class="preview-box">
|
|
||||||
<img id="preview-img"
|
|
||||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 控制按钮 -->
|
|
||||||
<div class="controls">
|
|
||||||
<label class="btn btn-primary">
|
|
||||||
📁 选择图片
|
|
||||||
<input type="file" accept="image/*" @change="inputfile">
|
|
||||||
</label>
|
|
||||||
<button class="btn btn-secondary " onclick="rotateImage(-90)">↩️ 左旋</button>
|
|
||||||
<button class="btn btn-success " id="uploadBtn">✂️ 裁剪头像</button>
|
|
||||||
|
|
||||||
<button class="btn btn-danger " style="margin-top: 150px;" onclick="avatar_toolt.hide()">❌ 取消</button>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* 头像裁剪器样式*/
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 95%;
|
|
||||||
/* 改为百分比宽度 */
|
|
||||||
margin: 20px auto;
|
|
||||||
/* 增加上下边距 */
|
|
||||||
max-width: 1200px;
|
|
||||||
/* 保留最大宽度 */
|
|
||||||
background: white;
|
|
||||||
padding: 30px;
|
|
||||||
border-radius: 12px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-wrapper {
|
|
||||||
display: flex;
|
|
||||||
gap: 30px;
|
|
||||||
margin-top: 20px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
/* 添加换行支持 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 裁剪区域 */
|
|
||||||
.crop-section {
|
|
||||||
flex: 1 1 60%;
|
|
||||||
/* 弹性布局基础宽度 */
|
|
||||||
min-width: 300px;
|
|
||||||
/* 降低最小宽度 */
|
|
||||||
height: auto;
|
|
||||||
/* 移除固定高度 */
|
|
||||||
min-height: 400px;
|
|
||||||
/* 设置最小高度 */
|
|
||||||
}
|
|
||||||
|
|
||||||
#image-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
height: 60vh;
|
|
||||||
/* 改用视窗单位 */
|
|
||||||
max-height: 600px;
|
|
||||||
/* 设置最大高度 */
|
|
||||||
background: #f8f9fa;
|
|
||||||
border: 2px dashed #ddd;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 预览区域自适应 */
|
|
||||||
.preview-section {
|
|
||||||
flex: 1 1 35%;
|
|
||||||
/* 弹性布局基础宽度 */
|
|
||||||
min-width: 250px;
|
|
||||||
/* 设置合理最小宽度 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移动端适配 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.container {
|
|
||||||
padding: 15px;
|
|
||||||
/* 减少内边距 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-wrapper {
|
|
||||||
flex-direction: column;
|
|
||||||
/* 垂直排列 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.crop-section,
|
|
||||||
.preview-section {
|
|
||||||
width: 100% !important;
|
|
||||||
/* 强制全宽 */
|
|
||||||
min-width: unset;
|
|
||||||
/* 移除最小宽度 */
|
|
||||||
}
|
|
||||||
|
|
||||||
#image-wrapper {
|
|
||||||
height: 50vh;
|
|
||||||
/* 调整移动端高度 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-box {
|
|
||||||
width: 120px;
|
|
||||||
/* 缩小预览区域 */
|
|
||||||
height: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
flex-direction: column;
|
|
||||||
/* 垂直排列按钮 */
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#cropper-image {
|
|
||||||
max-width: none !important;
|
|
||||||
max-height: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 控制区域 */
|
|
||||||
.controls {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 预览区域 */
|
|
||||||
.preview-section {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-box {
|
|
||||||
width: 150px;
|
|
||||||
height: 150px;
|
|
||||||
/* border-radius: 50%; */
|
|
||||||
border: 3px solid var(--primary-color);
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 0 auto 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#preview-img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* 上传进度 */
|
|
||||||
.progress-container {
|
|
||||||
height: 8px;
|
|
||||||
background: #eee;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-top: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar {
|
|
||||||
width: 0%;
|
|
||||||
height: 100%;
|
|
||||||
background: var(--primary-color);
|
|
||||||
transition: width 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 消息提示 */
|
|
||||||
.alertavater {
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-top: 20px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alertavater-success {
|
|
||||||
background: #dff0d8;
|
|
||||||
color: #3c763d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alertavater-error {
|
|
||||||
background: #f2dede;
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
input[type="file"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,16 +1,41 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted,defineProps } from "vue";
|
import { ref, onMounted, onUnmounted, defineProps, reactive } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
import Dropzone from "dropzone";
|
import Dropzone from "dropzone";
|
||||||
import "dropzone/dist/dropzone.css";
|
import "dropzone/dist/dropzone.css";
|
||||||
|
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
|
|
||||||
|
import "fslightbox";
|
||||||
|
const lightbox = new FsLightbox();
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const dropzoneElement = ref(null);
|
const dropzoneElement = ref(null);
|
||||||
let dropzoneInstance = null;
|
let dropzoneInstance = null;
|
||||||
const files = ref([]);
|
|
||||||
|
const files = reactive([]);
|
||||||
|
|
||||||
|
function get_file_from_uuid(uuid) {
|
||||||
|
if (files.length != 0) {
|
||||||
|
for(let i=0;i<files.length;i++){
|
||||||
|
if(files[i].uuid==uuid){
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_file_from_uuie(uuid) {
|
||||||
|
//delete files[uuid]
|
||||||
|
var id=get_file_from_uuid(uuid)
|
||||||
|
if(id>=0){
|
||||||
|
files.splice(id, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const prop = defineProps({
|
const prop = defineProps({
|
||||||
maxFiles: {
|
maxFiles: {
|
||||||
@@ -89,11 +114,60 @@ const initDropzone = () => {
|
|||||||
// 处理点击事件
|
// 处理点击事件
|
||||||
console.log("缩略图被点击", file);
|
console.log("缩略图被点击", file);
|
||||||
|
|
||||||
|
//动态把文件载入灯箱
|
||||||
|
//先移除原有数据
|
||||||
|
lightbox.props.sources.splice(0,lightbox.props.sources.length)
|
||||||
|
|
||||||
|
var dis_id=0;
|
||||||
|
var dis_id_t=0;
|
||||||
|
for (let i=0;i<files.length;i++){
|
||||||
|
if(files[i]["is_upload"]==true){
|
||||||
|
lightbox.props.sources.push(files[i]["get_url"])
|
||||||
|
if(files[i]["uuid"]==file.upload.uuid){
|
||||||
|
dis_id=dis_id_t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dis_id_t+=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lightbox.open(dis_id);
|
||||||
|
|
||||||
// 可以在这里实现:
|
// 可以在这里实现:
|
||||||
// 1. 预览大图
|
// 1. 预览大图
|
||||||
// 2. 显示文件详情
|
// 2. 显示文件详情
|
||||||
// 3. 触发自定义操作
|
// 3. 触发自定义操作
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//将后台接收到的url添加到文件列表
|
||||||
|
// var t = {
|
||||||
|
// //uuid:file.upload.uuid,
|
||||||
|
// hash: response.return.hash,
|
||||||
|
// get_url: response.return.get,
|
||||||
|
// download_url: response.return.download,
|
||||||
|
// name: file.name,
|
||||||
|
// size: file.size,
|
||||||
|
// };
|
||||||
|
|
||||||
|
var file_id=get_file_from_uuid(file.upload.uuid)
|
||||||
|
if(file_id>=0)
|
||||||
|
{
|
||||||
|
files[file_id]["hash"]=response.return.hash;
|
||||||
|
files[file_id]["get_url"]=response.return.get;
|
||||||
|
files[file_id]["download_url"]=response.return.download;
|
||||||
|
files[file_id]["file_name"]=file.name;
|
||||||
|
files[file_id]["file_size"]=file.size;
|
||||||
|
|
||||||
|
files[file_id]["is_upload"]=true;
|
||||||
|
|
||||||
|
console.log(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
//files.push(t)
|
||||||
|
// files[file.upload.uuid]=t
|
||||||
|
// console.log(files)
|
||||||
|
|
||||||
|
// lightbox.props.sources.push(t.get_url)
|
||||||
|
// console.log(lightbox)
|
||||||
});
|
});
|
||||||
this.on("error", (file, errorMessage) => {
|
this.on("error", (file, errorMessage) => {
|
||||||
console.error("上传失败:", file.name, errorMessage);
|
console.error("上传失败:", file.name, errorMessage);
|
||||||
@@ -101,15 +175,24 @@ const initDropzone = () => {
|
|||||||
this.on("removedfile", (file) => {
|
this.on("removedfile", (file) => {
|
||||||
console.log("remove:", file);
|
console.log("remove:", file);
|
||||||
//files.value = files.value.filter(f => f.name !== file.name)
|
//files.value = files.value.filter(f => f.name !== file.name)
|
||||||
|
remove_file_from_uuie(file.upload.uuid)
|
||||||
|
console.log(files)
|
||||||
});
|
});
|
||||||
this.on("addedfile", (file) => {
|
this.on("addedfile", (file) => {
|
||||||
|
//添加文件
|
||||||
console.log("addfile", file);
|
console.log("addfile", file);
|
||||||
|
//控制排序 需要从添加文件开始操作
|
||||||
|
var t = {
|
||||||
|
uuid: file.upload.uuid,
|
||||||
|
is_upload: false,
|
||||||
|
};
|
||||||
|
files.push(t);
|
||||||
|
console.log(files);
|
||||||
});
|
});
|
||||||
this.on("sending", function (file, xhr, formData) {
|
this.on("sending", function (file, xhr, formData) {
|
||||||
// 获取表单值并添加到 FormData
|
// 获取表单值并添加到 FormData
|
||||||
//console.log(userStore.userCookie.Value)
|
//console.log(userStore.userCookie.Value)
|
||||||
formData.append("cookie", userStore.userCookie.Value);
|
formData.append("cookie", userStore.userCookie.Value);
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -149,6 +232,8 @@ const initDropzone = () => {
|
|||||||
// 组件挂载时初始化
|
// 组件挂载时初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initDropzone();
|
initDropzone();
|
||||||
|
|
||||||
|
//console.log(lightbox)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件卸载时销毁
|
// 组件卸载时销毁
|
||||||
|
|||||||
Reference in New Issue
Block a user