feat:增加iframe,优化裁剪组件

This commit is contained in:
zouzhibing 2022-11-12 21:44:06 +08:00
parent 940d9af6fb
commit 55bba0f121
14 changed files with 402 additions and 203 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,86 @@
<template>
<vue-cropper
ref="cropper"
:img="avatarUrl"
:output-size="defaultOptions.outputSize"
:output-type="defaultOptions.outputType"
:info="defaultOptions.info"
:full="defaultOptions.full"
:fixed="defaultOptions.fixed"
:autoCropWidth="defaultOptions.autoCropWidth"
:autoCropHeight="defaultOptions.autoCropHeight"
:fixed-box="defaultOptions.fixedBox"
:auto-crop="defaultOptions.autoCrop"
:center-box="defaultOptions.centerBox"
@real-time="realTime"
/>
</template>
<script lang="ts" setup>
import {defineExpose, reactive, ref} from 'vue'
import { VueCropper } from 'vue-cropper'
import 'vue-cropper/dist/index.css'
const emit = defineEmits(['change'])
let props = defineProps({
avatarUrl:{
type:String
},
options:{
type:Object,
default:()=>{}
}
})
let cropper = ref()
const defaultOptions = reactive({
outputSize:0.8, //
outputType: 'png', //
info: true, //
fixed: true, //
autoCrop: true, //
anMoveBox: true, //
original: false, //
autoCropWidth: 300, //
autoCropHeight: 300, //
//
centerBox: false, //
high: true, // dpr
fixedBox: false, //
full: false, //
...props.options
})
const getBase64 = ()=>{
return new Promise(resolve => {
cropper.value.getCropData((data) => {
resolve(data)
})
})
}
const rotateLeft = () => {
cropper.value.rotateLeft()
}
const rotateRight = () => {
cropper.value.rotateRight()
}
const zoom = (percent) => {
cropper.value.changeScale(percent)
}
//
const realTime = (data)=>{
emit('change',data)
}
defineExpose({
getBase64,
rotateLeft,
rotateRight,
zoom
})
</script>

View File

@ -2,7 +2,7 @@ import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import pinia from "./store";
import 'default-passive-events'
// import 'default-passive-events'
// 权限路由
import './permission'

View File

@ -16,6 +16,7 @@ import chatRouter from './modules/chat'
import othersRouter from './modules/other'
import externalLink from './modules/externalLink'
import formRouter from './modules/from'
import iframeRouter from './modules/iframe'
// 异步组件
export const asyncRoutes = [
@ -30,6 +31,7 @@ export const asyncRoutes = [
...errorRouter,
...externalLink,
...systemRouter,
...iframeRouter,
{
path: '/:pathMatch(.*)',
redirect: '/404'

View File

@ -0,0 +1,24 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from "@/layout/index.vue";
const iframeRouter = [{
path: '/iframe',
component: Layout,
redirect: '/iframe/embedded',
name: 'iframe',
meta: {
title: '内嵌 iframe',
icon: 'Memo'
},
children: [
{
path: 'embedded',
component: () => import('@/views/iframe/embedded/index.vue'),
name: 'embedded',
meta: { title: '内嵌 iframe', icon: 'Postcard' }
},
]
}]
export default iframeRouter

View File

@ -94,7 +94,7 @@ const othersRouter = [{
path: 'count',
component: () => import('@/views/other/count/index.vue'),
name: 'count',
meta: { title: '数字自增长', icon: 'MenuIcon' }
meta: { title: '数字动画', icon: 'MenuIcon' }
}
]
}]

View File

@ -5,7 +5,7 @@
<el-card class="box-card" style="height: 100%">
<div class="personal">
<div>
<el-avatar :size="50" :src="circleUrl" />
<el-avatar :size="50" :src="AvatarLogo" />
</div>
<div class="name"></div>
<div class="description"></div>
@ -155,9 +155,9 @@
<script setup lang="ts">
import { User } from '@element-plus/icons-vue'
import { ref } from 'vue'
import AvatarLogo from '@/assets/image/avatar.png'
import CountTo from '@/components/CountTo/index.vue'
import BarCharts from '@/views/charts/components/simple/bar.vue'
const circleUrl = ref('https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png')
const goTo = (url) => {
window.open(url, '_blank')

View File

@ -0,0 +1,15 @@
<template>
<div class="app-container">
<div class="app-container-inner">
<iframe src="https://ext.dcloud.net.cn/plugin?id=7511"
frameborder="0" class="full-iframe"></iframe>
</div>
</div>
</template>
<script setup lang="ts" name="embedded"></script>
<style scoped lang="scss">
.full-iframe{
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,101 @@
<template>
<div class="m-custom-cropper">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>自定义裁剪</span>
</div>
</template>
<div class="content">
<div class="side-a">
<div class="avatar-cropper">
<AvatarCropper
:options="{
autoCropWidth:200,
autoCropHeight:200
}"
:avatarUrl="avatarUrl"
@change="realTime" ref="avatar"/>
</div>
<div>
<el-button type="primary" @click.prevent="rotateLeft">向左旋转</el-button>
<el-button type="primary" @click.prevent="rotateRight">向右旋转</el-button>
<el-button type="primary" @click.prevent="zoom(1)">放大</el-button>
<el-button type="primary" @click.prevent="zoom(-1)">缩小</el-button>
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
</div>
</div>
<div class="side-b">
<div class="preview-wrap" :style="previewStyle">
<img :src="previewsOption.url" class="uploadImg" :style="previewsOption.img"/>
</div>
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import {reactive, ref} from 'vue'
import AvatarCropper from '@/components/AvatarCropper/index.vue'
import avatarUrlLogo from '@/assets/image/cro-avatar.jpg'
import {ElMessage} from "element-plus";
let defaultUrl = ref<string>(avatarUrlLogo)
let avatarUrl = ref<string>(avatarUrlLogo)
let previewStyle = ref({})
let previewsOption = ref({})
let avatar = ref()
const realTime = (data)=>{
previewsOption.value = data
let options = {
width: data.w + "px",
height: data.h + "px",
overflow: "hidden",
}
previewStyle.value = {
...options,
}
}
const rotateLeft = () => {
avatar.value.rotateLeft()
}
const rotateRight = () => {
avatar.value.rotateRight()
}
const zoom = (percent) => {
avatar.value.zoom(percent)
}
const down = async (type) => {
let aLink = document.createElement('a')
aLink.download = '下载裁剪图片'
let data = await avatar.value.getBase64()
aLink.href = data
aLink.click()
}
</script>
<style lang="scss" scoped>
.m-custom-cropper{
margin-top: 20px;
.content{
display: flex;
}
.side-a{
margin-right: 100px;
.avatar-cropper{
width: 500px;
height: 400px;
margin-bottom: 20px;
}
}
.side-b{
}
}
</style>

View File

@ -0,0 +1,164 @@
<template>
<div class="m-header-avatar">
<el-card class="box-card">
<template #header>
<span>头像上传裁剪</span>
</template>
<div class="avatar" @click="avatarUpload">
<img :src="defaultUrl" class="avatar"/>
<el-icon class="icon-upload"><Plus /></el-icon>
</div>
</el-card>
<el-dialog
v-model="dialogVisible"
title="头像上传"
top="30px"
width="700px"
>
<div class="upload-dialog">
<div class="slide-a">
<div class="avatar-cropper" >
<AvatarCropper :avatarUrl="avatarUrl" @change="realTime" ref="avatar"/>
</div>
<div style="display: flex;justify-content: center;margin-top: 10px">
<el-upload action="/" multiple :before-upload="beforeUploadAction" accept="image/*">
<el-button type="primary">点击上传图片<el-icon><Upload /></el-icon></el-button>
</el-upload>
</div>
</div>
<div class="slide-b">
<div class="preview-wrap" :style="previewStyle">
<img :src="previewsOption.url" class="uploadImg" :style="previewsOption.img"/>
</div>
<div class="preview-wrap" :style="previewStyle1">
<img :src="previewsOption.url" class="uploadImg" :style="previewsOption.img"/>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="configUpload">
确认
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import {reactive, ref} from 'vue'
import AvatarCropper from '@/components/AvatarCropper/index.vue'
import avatarUrlLogo from '@/assets/image/cro-avatar.jpg'
import {ElMessage} from "element-plus";
let defaultUrl = ref<string>(avatarUrlLogo)
let avatarUrl = ref<string>(avatarUrlLogo)
let dialogVisible = ref<boolean>(false)
let avatar = ref()
let previewsOption = ref({})
let previewStyle = ref({})
let previewStyle1 = ref({})
const avatarUpload = ()=>{
dialogVisible.value = true
}
const beforeUploadAction = (file, fileLi) => {
return new Promise((resolve, reject) => {
var reader = new FileReader()
let reg = /\.jpg$|\.jpeg$|\.gif$|\.png$/i
reader.readAsDataURL(file)
let name = file.name
if (reg.test(name)) {
reader.onload = (e: FileReader) => {
avatarUrl.value = e.target.result
resolve(e.target.result)
}
} else {
ElMessage.error('请上传图片')
reject()
}
})
}
const configUpload = async ()=>{
defaultUrl.value = await avatar.value.getBase64()
dialogVisible.value = false
ElMessage.success('裁剪成功')
}
const realTime = (data)=>{
previewsOption.value = data
let options = {
width: data.w + "px",
height: data.h + "px",
overflow: "hidden",
}
previewStyle.value = {
...options,
zoom: (200 / data.w)
}
previewStyle1.value = {
...options,
zoom: (120 / data.w)
}
}
</script>
<style lang="scss" scoped>
.m-header-avatar{
width: 100%;
.avatar{
border-radius: 50%;
width: 160px;
height: 160px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
img{
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.icon-upload{
color: white;
font-size: 60px;
position: absolute;
display: none;
}
.avatar:hover{
.icon-upload{
display: block;
}
}
.avatar-cropper{
width: 400px;
height: 400px;
}
.upload-dialog{
display: flex;
justify-content: space-between;
}
.preview-wrap{
width: 160px;
height: 160px;
overflow: hidden;
border-radius: 50%;
margin-bottom: 40px;
}
.slide-b{
display: flex;
flex-direction: column;
align-items: center;
}
}
</style>

View File

View File

@ -1,207 +1,14 @@
<template>
<u-container-layout class="m-cropper">
<div style="display: flex">
<div class="left">
<div class="cropper-content">
<vue-cropper
ref="cropper"
:img="option.img"
:output-size="option.size"
:output-type="option.outputType"
:info="true"
:full="option.full"
:fixed="option.fixed"
:fixed-number="option.fixedNumber"
:can-move="option.canMove"
:can-move-box="option.canMoveBox"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:fixed-box="option.fixedBox"
:auto-crop="option.autoCrop"
:auto-crop-height="option.autoCropHeight"
:center-box="option.centerBox"
@real-time="realTime"
/>
</div>
<el-button type="primary" @click.prevent="zoom(1)">放大</el-button>
<el-button type="primary" @click.prevent="zoom(-1)">缩小</el-button>
<el-button type="primary" @click.prevent="rotateLeft">向左旋转</el-button>
<el-button type="primary" @click.prevent="rotateRight">向右旋转</el-button>
<el-button type="primary" @click.prevent="reset">清除</el-button>
<el-button type="primary" @click.prevent="cropImage">获取结果</el-button>
<el-button type="primary" @click.prevent="down('base64')">下载图片</el-button>
<el-upload class="upload-demo" :show-file-list="false" action :before-upload="beforeUpload">
<el-button type="primary">选择上传图片</el-button>
</el-upload>
</div>
<div>
<h4 style="margin-top: 10px">实时预览</h4>
<div
class="show-preview"
:style="{
width: option.previews.w + 'px',
height: option.previews.h + 'px',
overflow: 'hidden',
margin: '5px',
}"
>
<div :style="option.previews.div">
<img :src="option.previews.url" :style="option.previews.img" />
</div>
</div>
<h4 style="margin-top: 10px">获取结果</h4>
<div style="background: #ccc; width: 100px; height: 100px">
<img :src="option.cropImg" style="width: 100%; height: 100%" v-if="option.cropImg" />
</div>
</div>
</div>
</u-container-layout>
<div class="app-container">
<HeaderAvatar/>
<CustomCropper/>
</div>
</template>
<script lang="ts" setup>
// http://github.xyxiao.cn/vue-cropper/example/
// https://codepen.io/xyxiao001/pen/yLooYKg
import 'vue-cropper/dist/index.css'
import { VueCropper } from 'vue-cropper'
import imgSrc from '@/assets/image/cro.jpg'
import { reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { UploadProps, UploadUserFile } from 'element-plus'
const cropper = ref()
const option = reactive({
img: imgSrc,
size: 1,
full: false, //
outputType: 'png',
canMove: true,
fixed: true, //
info: true, //
outputSize: 0.8, //
fixedBox: false, //
original: false, //
canMoveBox: true, //
autoCrop: true, //
autoCropWidth: 300, //
autoCropHeight: 300, //
//
centerBox: false, //
high: true,
max: 99999,
previews: {
url: '',
},
cropImg: '',
})
const realTime = (data) => {
option.previews = data
}
const cropmove = (e) => {
// console.log('eeeeee',e)
}
const zoom = (percent) => {
cropper.value.changeScale(percent)
}
const rotateLeft = () => {
cropper.value.rotateLeft()
}
const rotateRight = () => {
cropper.value.rotateRight()
}
const move = (offsetX, offsetY) => {
cropper.value.move(offsetX, offsetY)
}
const down = (type) => {
// event.preventDefault()
var aLink = document.createElement('a')
aLink.download = 'demo'
//
if (type === 'blob') {
this.$refs.cropper.getCropBlob((data) => {
// this.downImg = window.URL.createObjectURL(data);
aLink.href = window.URL.createObjectURL(data)
aLink.click()
})
} else {
cropper.value.getCropData((data) => {
// this.downImg = data;
aLink.href = data
aLink.click()
})
}
}
const rotate = (deg) => {
cropper.value.rotate(deg)
}
const cropImage = () => {
cropper.value.getCropData((data) => {
option.cropImg = data
})
}
const reset = () => {
option.img = ''
option.cropImg = ''
}
const beforeUpload = (file, fileList) => {
return new Promise((resolve, reject) => {
var reader = new FileReader()
// let res = !/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/
let reg = /\.jpg$|\.jpeg$|\.gif$|\.png$/i
reader.readAsDataURL(file)
let name = file.name
if (reg.test(name)) {
reader.onload = (e: FileReader) => {
let data
if (typeof e.target.result === 'object') {
// Array Bufferblob base64
data = window.URL.createObjectURL(new Blob([e.target.result]))
} else {
data = e.target.result
}
resolve(e.target.result)
option.imgSrc = data
option.img = data
}
} else {
ElMessage.error('请上传图片')
reject()
}
})
}
import HeaderAvatar from './components/HeaderAvatar'
import CustomCropper from './components/CustomCropper'
</script>
<style lang="scss" scoped>
.cropper-content {
display: flex;
width: 400px;
height: 400px;
text-align: center;
}
::v-deep(.el-button) {
margin-top: 15px;
}
.m-cropper {
width: 100%;
/*height: 500px;*/
display: flex;
.left {
width: 400px;
margin-right: 30px;
}
.preview {
width: 200px;
height: 200px;
margin-top: 20px;
overflow: hidden;
}
}
</style>