vue项目使用pcl.js展示.pcd/.bin点云文件开发原文
以下是全部展示部分的代码
可以对照上面的原文进行理解
<template>
<div class="file-table-wrapper">
<!-- 文件表格 -->
<el-table
class="file-table"
:class="['file-type-' + fileType, routeName === 'Share' ? 'share' : '']"
ref="multipleTable"
fit
v-loading="loading"
element-loading-text="文件加载中……"
tooltip-effect="dark"
:data="fileList"
:highlight-current-row="true"
@selection-change="handleSelectRow"
@sort-change="handleSortChange"
@row-contextmenu="handleContextMenu"
>
<el-table-column
type="selection"
key="selection"
width="56"
align="center"
v-if="fileType !== 8"
></el-table-column>
<el-table-column
label
prop="isDir"
key="isDir"
:width="screenWidth <= 768 ? 40 : 56"
align="center"
class-name="file-icon-column"
>
<template slot-scope="scope">
<video
style="width: 30px; max-height: 30px; cursor: pointer"
v-if="$file.isVideoFile(scope.row)"
:src="$file.setFileImg(scope.row)"
></video>
<img
:src="$file.setFileImg(scope.row)"
:title="`${scope.row.isDir ? '' : '点击预览'}`"
style="width: 30px; max-height: 30px; cursor: pointer"
@click="
$file.handleFileNameClick(scope.row, scope.$index, sortedFileList)
"
v-else
/>
</template>
</el-table-column>
<el-table-column
prop="fileName"
key="fileName"
:sort-by="['isDir', 'fileName']"
sortable
show-overflow-tooltip
>
<template slot="header">
<span>文件名</span>
</template>
<template slot-scope="scope">
<span @click="FileNameClick(scope.row, scope.$index, sortedFileList)">
<span
class="file-name"
style="cursor: pointer"
:title="`${scope.row.isDir ? '' : '点击预览'}`"
v-html="$file.getFileNameComplete(scope.row, true)"
></span>
<div class="file-info" v-if="screenWidth <= 768">
{{ scope.row.uploadTime }}
<span class="file-size">
{{
scope.row.isDir === 0
? $file.calculateFileSize(scope.row.fileSize)
: ''
}}
</span>
</div>
</span>
</template>
</el-table-column>
<el-table-column
:label="fileType === 6 ? '原路径' : '路径'"
prop="filePath"
key="filePath"
show-overflow-tooltip
v-if="
![0, 8].includes(Number($route.query.fileType)) &&
routeName !== 'Share' &&
screenWidth > 768
"
>
<template slot-scope="scope">
<span
style="cursor: pointer"
title="点击跳转"
@click="
$router.push({
query: { filePath: scope.row.filePath, fileType: 0 }
})
"
>{{ scope.row.filePath }}</span
>
</template>
</el-table-column>
<el-table-column
label="类型"
width="80"
prop="extendName"
key="extendName"
:sort-by="['isDir', 'extendName']"
sortable
show-overflow-tooltip
v-if="selectedColumnList.includes('extendName') && screenWidth > 768"
>
<template slot-scope="scope">
<span>{{ $file.getFileType(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column
label="大小"
width="100"
prop="fileSize"
key="fileSize"
:sort-by="['isDir', 'fileSize']"
sortable
align="right"
v-if="selectedColumnList.includes('fileSize') && screenWidth > 768"
>
<template slot-scope="scope">
{{
scope.row.isDir === 0
? $file.calculateFileSize(scope.row.fileSize)
: ''
}}
</template>
</el-table-column>
<el-table-column
label="修改日期"
prop="uploadTime"
key="uploadTime"
width="160"
:sort-by="['isDir', 'uploadTime']"
sortable
align="center"
v-if="
selectedColumnList.includes('uploadTime') &&
![7, 8].includes(fileType) &&
!['Share'].includes(this.routeName) &&
screenWidth > 768
"
></el-table-column>
<el-table-column
label="删除日期"
prop="deleteTime"
key="deleteTime"
width="160"
:sort-by="['isDir', 'deleteTime']"
sortable
align="center"
v-if="
fileType === 6 &&
selectedColumnList.includes('deleteTime') &&
screenWidth > 768
"
></el-table-column>
<el-table-column
label="分享类型"
prop="shareType"
key="shareType"
width="100"
align="center"
v-if="fileType === 8 && screenWidth > 768"
>
<template slot-scope="scope">
{{ scope.row.shareType === 1 ? '私密' : '公共' }}
</template>
</el-table-column>
<el-table-column
label="分享时间"
prop="shareTime"
key="shareTime"
width="160"
:sort-by="['isDir', 'shareTime']"
show-overflow-tooltip
sortable
align="center"
v-if="fileType === 8 && screenWidth > 768"
></el-table-column>
<el-table-column
label="过期时间"
prop="endTime"
key="endTime"
width="190"
:sort-by="['isDir', 'endTime']"
show-overflow-tooltip
sortable
align="center"
v-if="fileType === 8 && screenWidth > 768"
>
<template slot-scope="scope">
<div>
<i
class="el-icon-warning"
v-if="$file.getFileShareStatus(scope.row.endTime)"
></i>
<i class="el-icon-time" v-else></i>
{{ scope.row.endTime }}
</div>
</template>
</el-table-column>
<el-table-column
label=""
key="operation"
width="48"
v-if="screenWidth <= 768"
>
<template slot-scope="scope">
<i
class="file-operate el-icon-more"
:class="`operate-more-${scope.$index}`"
@click="handleClickMore(scope.row, $event)"
></i>
</template>
</el-table-column>
</el-table>
<el-dialog
title="预览"
:visible.sync="openFile.visible"
width="80%"
v-loading="loadingBin"
>
<canvas id="canvas" style="padding: 10px"></canvas>
<span slot="footer" class="dialog-footer">
<el-button @click="openFile.visible = false">取 消</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import globalConfig from '@/config/index.js'
import common from '@/libs/globalFunction/common.js'
import fileFunction from '@/libs/globalFunction/file.js'
import * as PCL from 'pcl.js'
import { loadPCDData } from 'pcl.js'
// import { binToPng } from '_r/file.js'
import PointCloudViewer from 'pcl.js/dist/visualization/PointCloudViewer.esm.js'
let cloud
let cloudFiltered
let viewer
let cloudPoint
var _ = require('lodash')
export default {
name: 'FileTable',
props: {
// 文件类型
fileType: {
required: true,
type: Number
},
// 文件路径
filePath: {
required: true,
type: String
},
// 文件列表
fileList: {
required: true,
type: Array
},
// 文件加载状态
loading: {
required: true,
type: Boolean
}
},
data() {
return {
loadingBin: false,
openFile: {
visible: false
},
officeFileType: ['ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx'],
sortedFileList: [] // 排序后的表格数据
}
},
computed: {
// selectedColumnList: 判断当前用户设置的左侧栏是否折叠
...mapGetters(['selectedColumnList']),
// 路由名称
routeName() {
return this.$route.name
},
// 屏幕宽度
screenWidth() {
return this.$store.state.common.screenWidth
}
},
watch: {
/**
* 文件路径变化时清空表格已选行
*/
filePath() {
this.clearSelectedTable()
this.$refs.multipleTable.clearSort()
},
/**
* 文件类型变化时清空表格已选行
*/
fileType() {
this.clearSelectedTable()
this.$refs.multipleTable.clearSort()
},
/**
* 文件列表变化时清空表格已选行
*/
fileList() {
this.clearSelectedTable()
this.$refs.multipleTable.clearSort()
this.sortedFileList = this.fileList
}
},
methods: {
FileNameClick(row, index, fileListSorted) {
if (row.extendName.toLowerCase() === 'bin') {
console.log(
row.extendName.toLowerCase(),
'row.extendName.toLowerCase()'
)
this.openFile.visible = true
this.main(row, index, fileListSorted)
} else {
fileFunction.handleFileNameClick(row, index, fileListSorted)
}
},
async main(row) {
this.loadingBin = true
await PCL.init({
url: '/pcl-core.wasm'
}).catch((e) => {
console.log(e)
})
// 获取 PCD 文件
// console.log('this.getViewFilePath(row)', this.getViewFilePath(row))
const data = await fetch('/api/bin2x/pcd?binPath=' + row.fileUrl, {
headers: {
token: common.getCookies(globalConfig.tokenKeyName)
}
}).then((res) => {
console.log('res', res)
return res.arrayBuffer()
})
console.log(data)
cloudPoint = _.cloneDeep(PCL.PointXYZ)
cloudPoint.name = 'PointXYZ'
// 加载PCD文件数据,返回点云对象
cloud = PCL.loadPCDData(data, cloudPoint)
// 使用 StatisticalOutlierRemoval 过滤器去除异常值
// 参考: https://pcl.readthedocs.io/projects/tutorials/en/master/statistical_outlier.html#statistical-outlier-removal
const sor = new PCL.StatisticalOutlierRemoval(cloudPoint)
sor.setInputCloud(cloud)
sor.setMeanK(40)
sor.setStddevMulThresh(1.0)
console.log('sor', sor)
cloudFiltered = sor.filter()
console.log(cloudFiltered)
// 将过滤后的点云对象保存为PCD文件,内容为ArrayBuffer
// const cloudFilteredData = PCL.savePCDDataASCII(cloudFiltered)
// console.log(cloudFilteredData)
this.$nextTick(() => {
this.showPointCloud()
})
},
/**
* 画canvas 展示pcd id=“canvas”
*/
showPointCloud() {
this.loadingBin = false
viewer = new PointCloudViewer(
document.getElementById('canvas'),
1000,
500
)
viewer.addPointCloud(cloud)
viewer.setPointCloudProperties({ color: '#F00' })
viewer.setAxesHelper({ visible: true, size: 1 })
viewer.setCameraParameters({ position: { x: 0, y: 0, z: 1.5 } })
window.addEventListener('resize', () => {
viewer.setSize(1000, 500)
})
},
/**
* 当表格的排序条件发生变化的时候会触发该事件
*/
handleSortChange() {
this.sortedFileList = this.$refs.multipleTable.tableData
},
/**
* 表格某一行右键事件
* @description 打开右键菜单
* @param {object} row 当前行数据
* @param {object} column 当前列数据
* @param {object} event 当前右键元素
*/
handleContextMenu(row, column, event) {
// 阻止右键事件冒泡
event.cancelBubble = true
// xs 以上的屏幕
if (this.screenWidth > 768) {
event.preventDefault()
this.$refs.multipleTable.setCurrentRow(row) // 选中当前行
this.$openBox
.contextMenu({
selectedFile: row,
domEvent: event
})
.then((res) => {
this.$refs.multipleTable.setCurrentRow() // 取消当前选中行
if (res === 'confirm') {
this.$emit('getTableDataByType') // 刷新文件列表
this.$store.dispatch('showStorage') // 刷新存储容量
}
})
}
},
/**
* 清空表格已选行
* @description 用于父组件调用 | 本组件调用,请勿删除
*/
clearSelectedTable() {
this.$refs.multipleTable.clearSelection()
},
/**
* 表格选择项发生变化时的回调函数
* @param {[]} selection 勾选的行数据
*/
handleSelectRow(selection) {
this.$store.commit('changeSelectedFiles', selection)
this.$store.commit('changeIsBatchOperation', selection.length !== 0)
},
/**
* 更多图标点击事件
* @description 打开右键菜单
* @param {object} row 当前行数据
* @param {object} event 当前右键元素
*/
handleClickMore(row, event) {
this.$refs.multipleTable.setCurrentRow(row) // 选中当前行
this.$openBox
.contextMenu({
selectedFile: row,
domEvent: event
})
.then((res) => {
this.$refs.multipleTable.setCurrentRow() // 取消当前选中行
if (res === 'confirm') {
this.$emit('getTableDataByType') // 刷新文件列表
this.$store.dispatch('showStorage') // 刷新存储容量
}
})
}
}
}
</script>
<style lang="stylus" scoped>
@import '~_a/styles/varibles.styl';
@import '~_a/styles/mixins.styl';
.file-table-wrapper {
margin-top: 2px;
.file-type-0 {
height: calc(100vh - 206px) !important;
>>> .el-table__body-wrapper {
height: calc(100vh - 262px) !important;
}
}
.file-type-6 {
height: calc(100vh - 211px) !important;
>>> .el-table__body-wrapper {
height: calc(100vh - 263px) !important;
}
}
.file-table.share {
height: calc(100vh - 109px) !important;
>>> .el-table__body-wrapper {
height: calc(100vh - 161px) !important;
}
}
.file-table {
width: 100% !important;
height: calc(100vh - 203px);
>>> .el-table__header-wrapper {
th {
// background: $tabBackColor;
padding: 4px 0;
color: $RegularText;
}
.el-icon-circle-plus, .el-icon-remove {
margin-left: 6px;
cursor: pointer;
font-size: 16px;
&:hover {
color: $Primary;
}
}
}
>>> .el-table__body-wrapper {
height: calc(100vh - 255px);
overflow-y: auto;
setScrollbar(6px, transparent, #C0C4CC);
td {
padding: 8px 0;
.file-name {
.keyword {
color: $Danger;
}
}
}
.el-icon-warning {
font-size: 16px;
color: $Warning;
}
.el-icon-time {
font-size: 16px;
color: $Success;
}
}
}
}
.right-menu-list {
position: fixed;
display: flex;
flex-direction: column;
background: #fff;
border: 1px solid $BorderLighter;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 2;
padding: 4px 0;
color: $RegularText;
.right-menu-item,
.unzip-item {
padding: 0 16px;
height: 36px;
line-height: 36px;
cursor: pointer;
&:hover {
background: $PrimaryHover;
color: $Primary;
}
i {
margin-right: 8px;
}
}
.unzip-menu-item {
position: relative;
&:hover {
.unzip-list {
display: block;
}
}
.unzip-list {
position: absolute;
display: none;
.unzip-item {
width: 200px;
setEllipsis(1)
}
}
}
}
.right-menu-list,
.unzip-list {
background: #fff;
border: 1px solid $BorderLighter;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 2;
padding: 4px 0;
color: $RegularText;
}
</style>