主要逻辑:保存地图中心点、缩放级别、当前的地图图片与图层。
<template>
<!-- 书签管理 -->
<div class="bookmark">
<ag-panel-title title="书签" :show-close="true" @close="handleClose" />
<div v-if="pageType==='list'" class="bookmark-list">
<div style="margin: 10px;">
<el-input
placeholder="输入关键字搜索"
prefix-icon="el-icon-search"
v-model="search"
clearable
@input="searchBookmark"
/>
</div>
<ul>
<li v-for="(item,index) in bookmarkList" :key="index" @click="handleClick(item)">
<div class="left">
<i class="el-icon-menu" style="margin-right: 6px;" />
<ag-image
:src="item.fileUrl"
/>
<span :class="{'active':item.id===currentRow.id}" style="margin-left: 6px;">{{ item.markName }}</span>
</div>
<div class="el-icon-edit" @click="openAddForm('edit',item)" style="cursor: pointer;" />
</li>
</ul>
<div style="text-align: center;">
<el-button type="primary" size="small" @click="openAddForm('add',{})">添加书签</el-button>
</div>
</div>
<div v-if="pageType==='add'||pageType==='edit'">
<div class="sub-title">
<div @click="goBack" title="返回" class="el-icon-arrow-left" style="margin: 10px 0 0 8px; cursor: pointer;" />
<ag-panel-title :title="pageType==='add'?'添加书签':'编辑'" />
</div>
<el-form style="padding: 10px;">
<el-form-item label="标题">
<el-input v-model="form.markName" />
</el-form-item>
<el-form-item label="图片" v-if="pageType==='edit'">
<ag-image
:src="form.fileUrl"
:preview-src-list="[{attPath:form.fileUrl}]"
/>
</el-form-item>
</el-form>
<div style="text-align: center;">
<el-button type="danger" size="small" @click="deleteBookMark" v-if="pageType==='edit'">删 除</el-button>
<el-button size="small" @click="goBack">取 消</el-button>
<el-button type="primary" size="small" :loading="loading" @click="addBookMark" v-if="pageType==='add'">添 加</el-button>
<el-button type="primary" size="small" @click="updateBookMark" v-if="pageType==='edit'">保 存</el-button>
</div>
</div>
</div>
</template>
<script>
import { queryByPage, addBookMark, updateBookMark, deleteBookMark } from '@/api/fisheryMap/bookmark'
import html2canvas from 'html2canvas'
import { mapState } from 'vuex'
export default {
props: {
mapAndView: {
type: Object,
default: () => {}
}
},
data() {
return {
search: '',
loading: false,
form: {
markName: '',
remark: ''
},
list: [],
bookmarkList: [],
pageType: 'list',
currentRow: {},
zoom: -1,
stationaryListener: null
}
},
computed: {
...mapState({
checkedLayerIds: state => state.awater.checkedLayerIds,
userInfo: state => state.user.userInfo
})
},
watch: {
},
beforeDestroy() {
this.stationaryListener.remove()
},
mounted() {
this.getList()
this.zoom = this.mapAndView.zoom
this.stationaryListener = this.mapAndView.watch('stationary', event => {
if (event) {
let x = this.mapAndView.center.x.toString()
let y = this.mapAndView.center.y.toString()
if (x !== this.currentRow.coox || y !== this.currentRow.cooy) {
this.currentRow = {}
}
}
})
},
methods: {
handleClose() {
this.$emit('close')
},
// 获取书签列表
async getList() {
const { content } = await queryByPage({
pageNum: 1,
pageSize: 9999,
userId: this.userInfo.userId
})
this.list = content.list
this.bookmarkList = JSON.parse(JSON.stringify(content.list))
},
// 书签列表行点击
async handleClick(row) {
this.currentRow = row
this.$emit('row-click', row)
if (row.remark) {
await this.$store.commit('awater/setCheckedLayerIds', [])
let layers = row.remark.split(',')
layers.map(item => {
this.$bus.$emit('set-layer-switch', { layer: item, visible: true })
})
}
},
openAddForm(type, row) {
this.pageType = type
this.currentRow = row
if (type === 'edit') {
this.form = { ...this.currentRow }
}
},
goBack() {
this.form.markName = ''
this.pageType = 'list'
},
// 搜索书签
searchBookmark() {
this.bookmarkList = this.list.filter(item => {
return item.markName.indexOf(this.search) !== -1
})
},
// 添加书签
addBookMark() {
this.loading = true
html2canvas(this.$parent.$refs['mapPanel'].$el,
{
width: this.$parent.$refs['mapPanel'].$el.clientWidth - 600
// height:''
}
).then(canvas => {
const base64Data = canvas.toDataURL('image/png')
const blob = this.base64ToBlob(base64Data)
// 将Blob对象转换为File对象
let fileName = Math.floor((Math.random() * (10000 - 1000))) + 1000 // 1000到10000的随机整数(四位数)
fileName += this.form.markName + '.png'
const file = new File([blob], fileName, { type: 'image/png' })
let formData = new FormData()
formData.append('file', file)
formData.append('markName', this.form.markName)
formData.append('markLevel', this.mapAndView.zoom)
formData.append('coox', this.mapAndView.center.x)
formData.append('cooy', this.mapAndView.center.y)
formData.append('remark', this.checkedLayerIds.join(','))
formData.append('userId', this.userInfo.userId)
addBookMark(formData).then(res => {
if (res.success) {
this.$message.success('操作成功')
this.goBack()
this.getList()
}
this.loading = false
}).catch(err => {
this.loading = false
}).finally(() => {
this.loading = false
})
})
},
// 编辑书签
updateBookMark() {
updateBookMark({
...this.form
}).then(res => {
if (res.success) {
this.$message.success('操作成功')
this.goBack()
this.getList()
}
})
},
// 删除书签
deleteBookMark() {
deleteBookMark({
id: this.form.id
}).then(res => {
if (res.success) {
this.$message.success('删除成功')
this.goBack()
this.getList()
}
})
},
// Base64字符串转换为二进制数据
base64ToBlob(base64Data) {
let byteString = base64Data
if (base64Data.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(base64Data.split(',')[1]) // base64 解码
} else {
byteString = unescape(base64Data.split(',')[1])
}
// 获取文件类型
const mimeString = base64Data.split(';')[0].split(':')[1] // mime类型
// ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区
// let arrayBuffer = new ArrayBuffer(byteString.length) // 创建缓冲数组
// let uintArr = new Uint8Array(arrayBuffer) // 创建视图
const uintArr = new Uint8Array(byteString.length) // 创建视图
for (let i = 0; i < byteString.length; i += 1) {
uintArr[i] = byteString.charCodeAt(i)
}
// 生成blob
const blob = new Blob([uintArr], {
type: mimeString
})
// 使用 Blob 创建一个指向类型化数组的URL, URL.createObjectURL是new Blob文件的方法,可以生成一个普通的url,可以直接使用,比如用在img.src上
return blob
}
}
}
</script>
<style lang="scss" scoped>
.bookmark {
position: absolute;
top: 0;
// right: 665px;
left: 2px;
width: 300px;
max-height: 100%;
padding-bottom: 20px;
background: #fff;
.bookmark-list {
height: calc(100% - 50px);
}
ul {
max-height: calc(100% - 100px);
padding: 0;
overflow: auto;
li {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 10px;
cursor: pointer;
border-bottom: 1px solid #eee;
.active {
color: #409eff;
}
.left {
display: flex;
align-items: center;
}
}
}
.sub-title {
display: flex;
align-items: center;
border-bottom: 1px solid #ccc;
}
}
</style>