上传图片获取宽高
在项目中上传图片需要同时把图片原始宽高传过去, 就用到了下面的方法
getWidthHeight (file) { // 获取附件图片宽高
let that = this
let fileReader = new FileReader()
fileReader.readAsDataURL(file) // 根据图片路径读取图片
fileReader.onload = function (e) {
let base64 = this.result
let img = new Image()
img.src = base64
img.onload = function () {
console.log('宽' + img.naturalWidth + ' 高:' + img.naturalHeight)
}
}
}
element表格拖拽 右击显示菜单
需要表格每行能上下拖拽,并且每行右击能显示一个操作栏,如下图,我点击了第一行
这里使用的是element的表格,拖拽需要安装插件 sortablejs,引入后在mounted中调用rowDrop()方法,** 注意表格的数据时tableData,而在rowDrop()中改变的数据时tableData1, 因为表格拖拽默认不会改变原数据,只是改变了页面二姨而已,如果你在rowDrop()中改变的数据是tableData 这样拖拽后就会发现拖拽后不是你想要的结果**
<template>
<div class="sortTable">
<el-table ref="contentTable"
:data="tableData"
@row-contextmenu="rihgtClick"
:header-cell-style="{'background': '#f0f1f4'}"
:row-class-name="tableRowClassName"
border>
<el-table-column
prop="id"
label="ID"
width="180">
</el-table-column>
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
<!--鼠标右键点击出现页面-->
<div v-show="menuVisible">
<el-menu
id = "rightClickMenu"
class="el-menu-vertical"
@select="handleRightSelect"
active-text-color="#fff"
text-color="#fff">
<el-menu-item index="1" v-show="!ifTop" class="menuItem">
<span slot="title">置顶</span>
</el-menu-item>
<el-menu-item index="2" v-show="ifTop" class="menuItem">
<span slot="title">取消置顶</span>
</el-menu-item>
<el-menu-item index="3" class="menuItem">
<span slot="title">复制</span>
</el-menu-item>
<el-menu-item index="4" class="menuItem">
<span slot="title">移动</span>
</el-menu-item>
</el-menu>
</div>
</div>
</template>
<script>
import Sortable from 'sortablejs'
export default {
name: 'sortTable',
props: {
},
data () {
return {
tableData: [],
tableData1: [], // 排序后的数据
menuVisible: false, // 右击菜单
ifTop: false, // 当前行是否置顶
rightRow: 0 // 右击行数据
}
},
created () {
this.getListData()
},
mounted () {
this.rowDrop()
},
methods: {
getListData () {
setTimeout(() => {
this.tableData = [
{
id: 1,
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
id: 2,
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}
]
this.tableData1 = JSON.parse(JSON.stringify(this.tableData))
}, 500)
},
rowDrop () { // 拖拽排序
let _this = this
const tbody = document.querySelector('.el-table__body-wrapper tbody')
Sortable.create(tbody, {
onEnd ({ newIndex, oldIndex }) {
console.log(_this.tableData)
const currRow = _this.tableData1.splice(oldIndex, 1)[0]
_this.tableData1.splice(newIndex, 0, currRow)
}
})
},
rihgtClick (row, column, event) { // 表格右击
this.$refs.contentTable.clearSelection()
this.$refs.contentTable.toggleRowSelection(row, true)
if (row.top === 1) {
this.ifTop = true
} else {
this.ifTop = false
}
this.rightRow = row
this.menuVisible = true
document.addEventListener('click', (e) => {
console.log(e)
this.menuVisible = false
})
let menu = document.querySelector('#rightClickMenu')
/* 菜单定位基于鼠标点击位置 */
menu.style.left = event.clientX + 'px'
menu.style.top = event.clientY + 'px'
menu.style.position = 'absolute' // 为新创建的DIV指定绝对定位
menu.style.width = 160 + 'px'
// 阻止右键默认行为
event.preventDefault()
},
handleRightSelect (key) { // 右击弹框选择
if (key === '1') { // 置顶
this.top()
this.menuVisible = false
} else if (key === '2') { // 取消置顶
this.cancelTop()
this.menuVisible = false
} else if (key === '3') { // 复制
this.$refs.copy.clickCopy([this.rightRow])
this.menuVisible = false
} else if (key === '4') { // 移动
this.$refs.remove.clickRemove([this.rightRow])
this.menuVisible = false
}
},
tableRowClassName ({ rowIndex }) {
if (rowIndex % 2 === 1) {
return 'b_f8f8f8'
} else {
return 'b_fcfcfd'
}
}
}
}
</script>
<style scoped>
.sortTable {
background-color: #fff;
}
h3 {
height: 30px;
}
/*************************标签鼠标右击页面样式******************************/
.el-menu-vertical{
border: 3px solid rgb(84, 92, 100);
border-radius: 10px;
z-index: 100;
}
.el-menu-vertical i{
color: #777777;
}
.menuItem{
height: 40px;
line-height: 40px;
background-color: #545c64;
font-size: 1.2rem;
}
.menuItem:hover{
background-color: #409EFF;
}
</style>
上传图片截取
上传图片可能需要对图片宽高做个限制, 这里使用插件 vue-cropperjs, 直接贴代码
index.vue
<template>
<div>
<el-upload class="avatar-uploader"
action="#"
:show-file-list="false"
:before-upload="beforeAvatarUpload">
<img v-if="uploadUrl"
:src="uploadUrl"
class="avatar">
<i v-else
class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<Cropper title="设置封面"
:visible="cropperVisible"
@close="cropperClose"
:cropImgSrc="imageUrl"
@upCover="uploadFile"
@upCoverImgShow="handleupCoverImgShow" />
</div>
</template>
<script>
import Cropper from './cropper'
export default {
props: ['src'],
data () {
return {
uploadUrl: this.src,
imageUrl: '',
cropperVisible: false,
imgShow: ''
}
},
watch: {
src () {
this.uploadUrl = this.src
}
},
components: {
Cropper
},
methods: {
beforeAvatarUpload (file) {
if (typeof FileReader === 'function') {
const reader = new FileReader()
reader.onload = (event) => {
this.imageUrl = event.target.result
}
reader.readAsDataURL(file)
this.cropperOpen()
} else {
alert('Sorry, FileReader API not supported')
}
},
cropperOpen () {
this.cropperVisible = true
},
cropperClose () {
this.cropperVisible = false
},
// 封面上传
uploadFile (file) {
console.log('调用接口传递的文件:', file)
},
handleupCoverImgShow (img) {
this.imgShow = img
}
}
}
</script>
<style>
.avatar-uploader {
display: flex;
justify-content: center;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 320px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 320px;
height: 178px;
display: block;
}
</style>
cropper.vue
<template>
<el-dialog :title="title"
:visible.sync="dialogVisible"
width="1000px"
:before-close="handleClose"
:close-on-click-modal="false">
<div class="uploadFile">
<el-upload action="#"
multiple
accept=".jpg,.jpeg,.png,.gif,.bmp,.JPG,.JPEG,.PBG,.GIF,.BMP"
:show-file-list="false"
:before-upload="beforeAvatarUpload">
<el-button size="small"
type="primary">点击上传</el-button>
</el-upload>
</div>
<div class="content">
<section class="cropper-area">
<div class="img-cropper">
<vue-cropper v-if="isHead"
ref="cropper"
:aspect-ratio=1
:src="imgSrc"
preview=".preview" />
<vue-cropper v-else
ref="cropper"
:src="imgSrc"
preview=".preview" />
</div>
<div class="actions">
<el-button size="small"
type="primary"
icon="el-icon-zoom-in"
@click.prevent="zoom(0.2)"></el-button>
<el-button size="small"
type="primary"
icon="el-icon-zoom-out"
@click.prevent="zoom(-0.2)"></el-button>
<el-button size="small"
type="primary"
icon="el-icon-refresh-left"
@click.prevent="rotate(-90)"></el-button>
<el-button size="small"
type="primary"
icon="el-icon-refresh-right"
@click.prevent="rotate(90)"></el-button>
<el-button size="small"
type="primary"
icon="el-icon-refresh"
@click.prevent="reset"></el-button>
</div>
</section>
<section class="preview-area">
<p>预览效果</p>
<div class="preview" />
</section>
</div>
<span slot="footer"
class="dialog-footer">
<el-button @click="submitNatural">使用原图</el-button>
<el-button type="primary"
@click="submitCroped">确 定</el-button>
</span>
</el-dialog>
</template>
<script>
import VueCropper from 'vue-cropperjs'
import 'cropperjs/dist/cropper.css'
export default {
components: {
VueCropper
},
props: ['title', 'visible', 'cropImgSrc', 'isHead'],
data () {
return {
imgSrc: this.cropImgSrc,
// cropImg: '',
data: null,
dialogVisible: this.visible,
cropData: '',
naturalData: ''
}
},
watch: {
visible () {
this.dialogVisible = this.visible
},
cropImgSrc () {
// if (this.$refs.cropper) {
this.imgSrc = this.cropImgSrc
this.$refs.cropper.replace(this.cropImgSrc)
// }
}
},
methods: {
reset () {
this.$refs.cropper.reset()
},
rotate (deg) {
this.$refs.cropper.rotate(deg)
},
zoom (percent) {
this.$refs.cropper.relativeZoom(percent)
},
handleClose (done) {
this.$emit('close')
// this.$confirm('确认关闭?')
// .then(_ => {
// this.$emit('close')
// })
// .catch(_ => { })
},
beforeAvatarUpload (file) {
if (typeof FileReader === 'function') {
const reader = new FileReader()
reader.onload = (event) => {
this.imgSrc = event.target.result
// rebuild cropperjs with the updated source
this.$refs.cropper.replace(event.target.result)
}
reader.readAsDataURL(file)
} else {
alert('Sorry, FileReader API not supported')
}
},
submitNatural () {
let imgFile = ''
if (this.imgSrc.startsWith('data')) {
imgFile = this.imgToFile(this.imgSrc)
this.$emit('upCover', imgFile)
} else {
const getBase64Image = img => {
var canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
var ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, img.width, img.height)
var ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase()
var dataURL = canvas.toDataURL('image/' + ext)
return dataURL
}
var tempImage = new Image()
tempImage.crossOrigin = '*'
tempImage.src = this.imgSrc
tempImage.onload = () => {
var base64 = getBase64Image(tempImage)
imgFile = this.imgToFile(base64)
this.$emit('upCover', imgFile)
}
}
},
imgToFile (cropImg) {
this.$emit('upCoverImgShow', cropImg)
const dataURLtoFile = (dataurl, filename) => { // 将base64转换为文件
var arr = dataurl.split(','); var mime = arr[0].match(/:(.*?);/)[1]
var bstr = atob(arr[1]); var n = bstr.length; var u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
}
const file = dataURLtoFile(cropImg, 'jpg')
return file
},
submitCroped () {
const cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL()
this.$emit('upCover', this.imgToFile(cropImg))
// this.$refs.cropper.getCroppedCanvas().toBlob((blob) => {
// this.$emit('upCoverblob', blob)
// })
}
}
}
</script>
<style scoped>
.uploadFile {
margin-bottom: 20px;
}
.content {
display: flex;
justify-content: space-between;
}
.cropper-area {
width: 614px;
}
.actions {
margin-top: 1rem;
}
.actions a {
display: inline-block;
padding: 5px 15px;
background: #0062cc;
color: white;
text-decoration: none;
border-radius: 3px;
margin-right: 1rem;
margin-bottom: 1rem;
}
textarea {
width: 100%;
height: 100px;
}
.preview-area {
width: 307px;
}
.preview-area p {
font-size: 1.25rem;
margin: 0;
margin-bottom: 1rem;
}
.preview-area p:last-of-type {
margin-top: 1rem;
}
.preview {
width: 100%;
height: calc(372px * (9 / 16));
overflow: hidden;
}
.crop-placeholder {
width: 100%;
height: 200px;
background: #ccc;
}
.cropped-image img {
max-width: 100%;
}
.el-button--small,.el-button--small.is-round {
padding: 5px 12px !important;
margin-right: 10px !important;
}
.el-button--mini,.el-button--small {
font-size: 20px !important;
border-radius: 3px !important;
}
</style>