167 lines
4.0 KiB
Plaintext
167 lines
4.0 KiB
Plaintext
<template>
|
|
<div class="img-cropper" :class="{ 'img-cropper-auto': $slots.default }">
|
|
<slot name="default"></slot>
|
|
<template v-if="!$slots.default">
|
|
<template v-if="src">
|
|
<div class="img-cropper-img" @click="openCropper">
|
|
<div class="img-boxs">
|
|
<img :src="src" :class="{ circled: circled }" />
|
|
</div>
|
|
<div class="mask" :class="{ circled: circled }" @click.stop>
|
|
<div class="handle-icon" @click="openCropper">
|
|
<span class="icon"><FormOutlined /></span>
|
|
</div>
|
|
<div class="handle-icon" @click="viewImg">
|
|
<span class="icon"><ZoomInOutlined /></span>
|
|
</div>
|
|
<div class="handle-icon" @click="deleteImg">
|
|
<span class="icon"><DeleteOutlined /></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template v-else>
|
|
<div class="img-cropper-img" @click="openCropper">
|
|
<div class="addImg">
|
|
<span class="icon"><PlusOutlined /></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
<CropperModal
|
|
ref="cropperRef"
|
|
:title="title"
|
|
subBtuText="确认上传"
|
|
:uploadApi="uploadApi"
|
|
:circled="circled"
|
|
@upload-success="handleSuccess"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref } from 'vue';
|
|
import CropperModal from './CropperModal.vue';
|
|
import { UploadOutlined } from '@ant-design/icons-vue';
|
|
import {
|
|
PlusOutlined,
|
|
ZoomInOutlined,
|
|
DeleteOutlined,
|
|
FormOutlined,
|
|
} from '@ant-design/icons-vue';
|
|
import { cssUnit } from '@/utils';
|
|
|
|
const cropperRef = ref();
|
|
|
|
const props = defineProps({
|
|
title: { type: String, default: '图片上传' },
|
|
src: { type: String, required: true },
|
|
circled: { type: Boolean, default: false },
|
|
width: { type: [String, Number], default: 120 },
|
|
uploadApi: {
|
|
type: Function as PropType<(params) => Promise<any>>,
|
|
},
|
|
});
|
|
|
|
const getWidth = cssUnit(props.width);
|
|
|
|
const iconSize = cssUnit(Math.ceil(parseInt(getWidth) / 4));
|
|
|
|
// const emit = defineEmits(['change']);
|
|
const emit = defineEmits(['uploadSuccess']);
|
|
const viewImg = () => {
|
|
window.open(props.src);
|
|
};
|
|
const deleteImg = () => {
|
|
emit('uploadSuccess', { fileUrl: '' });
|
|
};
|
|
function handleSuccess(value) {
|
|
emit('uploadSuccess', value);
|
|
}
|
|
function openCropper() {
|
|
cropperRef.value.openModal();
|
|
}
|
|
|
|
defineExpose({
|
|
openCropper,
|
|
});
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.img-cropper {
|
|
width: v-bind(getWidth);
|
|
height: v-bind(getWidth);
|
|
overflow: hidden;
|
|
text-align: center;
|
|
position: relative;
|
|
|
|
&-img {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 50%;
|
|
.img-boxs {
|
|
border: 1px dashed #999999;
|
|
padding: 5px;
|
|
}
|
|
.addImg {
|
|
width: v-bind(getWidth);
|
|
height: v-bind(getWidth);
|
|
border: 1px dashed #999999;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
.icon {
|
|
color: #999999;
|
|
font-size: v-bind(iconSize);
|
|
}
|
|
}
|
|
|
|
.mask {
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 10;
|
|
left: 0;
|
|
top: 0;
|
|
opacity: 0;
|
|
transition: opacity 0.4s;
|
|
.handle-icon {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0 6%;
|
|
color: aliceblue;
|
|
.icon {
|
|
color: #fff;
|
|
font-size: 20px;
|
|
}
|
|
}
|
|
}
|
|
|
|
&:hover {
|
|
cursor: pointer;
|
|
|
|
.mask {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.img-cropper-auto {
|
|
width: auto;
|
|
height: auto;
|
|
display: inline-block;
|
|
}
|
|
|
|
.circled {
|
|
border-radius: 50%;
|
|
}
|
|
</style>
|