1112 lines
46 KiB
Vue
1112 lines
46 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<el-form size="small" :inline="true" ref="queryForm" :model="queryParams">
|
||
<el-form-item label="项目名称" prop="projectId">
|
||
<!-- <el-input
|
||
clearable
|
||
placeholder="请输入项目名称"
|
||
v-model="queryParams.proName"
|
||
@keyup.enter.native="handleQuery"
|
||
/> -->
|
||
|
||
<el-select
|
||
placeholder="请选择项目名称"
|
||
style="width: 240px"
|
||
@change="handleProjectChange"
|
||
v-model="queryParams.projectId"
|
||
>
|
||
<el-option :key="item.id" :value="item.id" :label="item.name" v-for="item in projectSelectList" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="所属单位" prop="unit">
|
||
<el-input
|
||
clearable
|
||
placeholder="请输入所属单位"
|
||
v-model="queryParams.unit"
|
||
@keyup.enter.native="handleQuery"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="负责人" prop="chargePerson">
|
||
<el-input
|
||
clearable
|
||
placeholder="请输入负责人"
|
||
v-model="queryParams.chargePerson"
|
||
@keyup.enter.native="handleQuery"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button>
|
||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAddLevelTwo">
|
||
新增
|
||
</el-button>
|
||
<el-button type="primary" plain icon="el-icon-view" size="mini" @click="handleModelPreview">
|
||
模型预览
|
||
</el-button>
|
||
</el-col>
|
||
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getModelList"></right-toolbar> -->
|
||
</el-row>
|
||
|
||
<el-table border row-key="id" v-loading="loading" :data="modelList" default-expand-all>
|
||
<el-table-column label="序号" align="center" type="index" />
|
||
<el-table-column label="分类名称" align="center">
|
||
<template slot-scope="{ row }">
|
||
<span>{{ row.nodeName }}</span>
|
||
<el-tag style="margin-left: 10px" size="mini" type="primary">节点{{ row.nodelevel }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="所属项目" align="center" prop="proName" />
|
||
<el-table-column label="层级名称" align="center" prop="levelName" />
|
||
<el-table-column label="操作" align="center" width="180">
|
||
<template slot-scope="{ row }">
|
||
<el-button
|
||
size="mini"
|
||
type="text"
|
||
icon="el-icon-plus"
|
||
@click="handleAddChild(row)"
|
||
v-if="row.nodelevel < row.nodeCount"
|
||
>
|
||
新增
|
||
</el-button>
|
||
|
||
<el-button
|
||
size="mini"
|
||
type="text"
|
||
:icon="row.modelUrl ? 'el-icon-view' : 'el-icon-upload'"
|
||
@click="row.modelUrl ? handleViewModel(row) : handleUploadModel(row)"
|
||
v-if="row.nodeCount == row.nodelevel"
|
||
>
|
||
{{ row.modelUrl ? '查看模型' : '上传模型' }}
|
||
</el-button>
|
||
|
||
<!-- <el-button
|
||
size="mini"
|
||
type="text"
|
||
icon="el-icon-edit"
|
||
@click="handleEdit(row)"
|
||
v-if="row.level != 1"
|
||
>
|
||
修改
|
||
</el-button> -->
|
||
|
||
<el-button
|
||
size="mini"
|
||
type="text"
|
||
icon="el-icon-delete"
|
||
style="color: #f56c6c"
|
||
@click="handleDelete(row.id)"
|
||
v-if="row.nodeCount == row.nodelevel || row.children.length == 0"
|
||
>
|
||
删除
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- <pagination
|
||
v-show="total > 0"
|
||
:total="total"
|
||
:page.sync="queryParams.pageNum"
|
||
:limit.sync="queryParams.pageSize"
|
||
@pagination="getModelList"
|
||
/> -->
|
||
|
||
<el-dialog :title="title" :visible.sync="open" width="550px" append-to-body>
|
||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||
<el-form-item label="项目名称" prop="proName">
|
||
<el-input v-model="form.proName" placeholder="请输入项目名称" />
|
||
</el-form-item>
|
||
<el-form-item label="层级配置" prop="level">
|
||
<el-select v-model="form.level" placeholder="请选择层级配置" style="width: 100%">
|
||
<el-option
|
||
:key="item.configId"
|
||
:value="item.configId"
|
||
:label="item.configName"
|
||
v-for="item in levelList"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="备注" prop="remark">
|
||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||
<el-button @click="cancel">取 消</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<el-dialog width="80%" append-to-body :title="useOrReturnFormTitle" :visible.sync="useOrReturnFormVisible">
|
||
<UseOrReturnForm
|
||
ref="useOrReturnFormComponentRef"
|
||
v-if="useOrReturnFormVisible"
|
||
:project-id="currentProjectId"
|
||
@closeUseOrReturnFormDialog="useOrReturnFormVisible = false"
|
||
@refreshParentList="getModelList"
|
||
/>
|
||
</el-dialog>
|
||
<!-- 添加或修改岗位对话框 -->
|
||
<el-dialog width="40%" append-to-body :title="addOrEditFormTitle" :visible.sync="addOrEditFormVisible">
|
||
<AddOrEditForm ref="addOrEditComponentRef" @closeAddOrEditFormDialog="closeAddOrEditFormDialog" />
|
||
</el-dialog>
|
||
<el-dialog
|
||
title="模型预览"
|
||
width="90%"
|
||
append-to-body
|
||
@close="onCloseMapView"
|
||
:visible.sync="modelPreviewVisible"
|
||
>
|
||
<!-- <dxf-viewer :entities="dxfPreviewUrl"></dxf-viewer> -->
|
||
|
||
<div id="map-container"> </div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import AddOrEditForm from './addOrEditForm.vue'
|
||
// import UseOrReturnForm from './useOrReturnForm.vue'
|
||
import UseOrReturnForm from './useOrReturnFormNew.vue'
|
||
import UseRecordTable from './useRecordTable.vue'
|
||
import DxfViewer from './DxfViewer.vue'
|
||
|
||
// 引入相关的 API 接口
|
||
import { getModelListApi, delModelApi, openView } from '@/api/basic/model'
|
||
import {
|
||
listProject,
|
||
getProject,
|
||
delProject,
|
||
addProject,
|
||
updateProject,
|
||
getProjectSelectListApi,
|
||
} from '@/api/basic/project'
|
||
import { getLevelListApi } from '@/api/basic/level-manage.js'
|
||
|
||
export default {
|
||
name: 'DeviceManage',
|
||
components: {
|
||
AddOrEditForm,
|
||
UseOrReturnForm,
|
||
UseRecordTable,
|
||
DxfViewer,
|
||
},
|
||
|
||
data() {
|
||
return {
|
||
// 用于新增/修改项目的对话框
|
||
open: false,
|
||
title: '', // 对话框标题
|
||
form: {
|
||
proId: undefined,
|
||
proName: undefined,
|
||
proType: undefined,
|
||
unit: undefined,
|
||
chargePerson: undefined,
|
||
location: undefined,
|
||
remark: undefined,
|
||
level: undefined,
|
||
},
|
||
rules: {
|
||
proName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
|
||
level: [{ required: true, message: '请选择层级配置', trigger: 'change' }],
|
||
},
|
||
levelList: [], // 层级配置列表
|
||
|
||
viewDialogVisible: false,
|
||
dxfPreviewUrl: [],
|
||
total: 0, // 总条数
|
||
loading: false, // 加载中
|
||
addOrEditFormTitle: '新增', // 新增或编辑对话框标题 (旧的AddOrEditForm组件使用)
|
||
addOrEditFormVisible: false, // 新增或编辑对话框是否显示 (旧的AddOrEditForm组件使用)
|
||
useOrReturnFormTitle: '模型上传', // 领用或归还对话框标题
|
||
useOrReturnFormVisible: false, // 领用或归还对话框是否显示
|
||
useRecordFormVisible: false, // 使用记录对话框是否显示
|
||
// 设备列表
|
||
modelList: [],
|
||
currentProjectId: null,
|
||
showSearch: true, // 控制搜索栏显示隐藏
|
||
// 查询参数
|
||
queryParams: {
|
||
// pageNum: 1, // 如果您的API支持分页,可以保留
|
||
// pageSize: 10, // 如果您的API支持分页,可以保留
|
||
projectId: undefined,
|
||
unit: undefined,
|
||
chargePerson: undefined,
|
||
},
|
||
projectSelectList: [],
|
||
list: [
|
||
{
|
||
label: '项目1',
|
||
id: 1,
|
||
level: 1,
|
||
count: 2,
|
||
children: [
|
||
// {
|
||
// label: '节点1',
|
||
// id: 1,
|
||
// level: 2,
|
||
// },
|
||
],
|
||
},
|
||
{
|
||
label: '项目2',
|
||
id: 2,
|
||
level: 1,
|
||
count: 3,
|
||
children: [
|
||
{
|
||
label: '节点1',
|
||
id: 1,
|
||
level: 2,
|
||
},
|
||
],
|
||
},
|
||
],
|
||
|
||
map: null,
|
||
modelPreviewVisible: false,
|
||
modelPreviewInfoList: [],
|
||
}
|
||
},
|
||
created() {
|
||
this.getLevelList() // 获取层级配置数据
|
||
this.getProjectSelectList()
|
||
},
|
||
methods: {
|
||
/** 获取层级配置列表 */
|
||
async getLevelList() {
|
||
try {
|
||
const res = await getLevelListApi({
|
||
pageNum: 1,
|
||
pageSize: 1000,
|
||
})
|
||
this.levelList = res.rows
|
||
} catch (error) {
|
||
console.error('获取层级配置失败:', error)
|
||
}
|
||
},
|
||
// 打开新增项目对话框
|
||
handleAdd() {
|
||
this.reset() // 重置表单
|
||
this.open = true // 显示对话框
|
||
this.title = '新增项目' // 设置对话框标题
|
||
},
|
||
// 查看模型按钮
|
||
handleViewModel(row) {
|
||
/* const modelUrl = row.modelUrl
|
||
if (!modelUrl || !modelUrl.endsWith('.dxf')) {
|
||
this.$modal.msgWarning('模型地址无效或不是DXF文件')
|
||
return
|
||
} */
|
||
/* 新需求 重新做 上面建议不要删除 有需要直接可以查看 */
|
||
// openView({ id: row.id })
|
||
// .then((response) => {
|
||
// this.dxfPreviewUrl = response.data
|
||
// this.viewDialogVisible = true
|
||
// })
|
||
// .catch((error) => {
|
||
// // console.error('获取项目详情失败:', error)
|
||
// })
|
||
|
||
openView({ id: row.id })
|
||
.then((res) => {
|
||
this.modelPreviewInfoList = res.data
|
||
this.modelPreviewVisible = true
|
||
|
||
console.log('res', res.data)
|
||
this.initMap()
|
||
})
|
||
.catch((err) => {
|
||
console.error('获取模型详情失败:', err)
|
||
})
|
||
},
|
||
|
||
// 查询按钮
|
||
handleQuery() {
|
||
this.queryParams.pageNum = 1 // 假设有分页,查询时重置页码
|
||
this.getModelList()
|
||
},
|
||
|
||
// 重置按钮
|
||
resetQuery() {
|
||
this.resetForm('queryForm')
|
||
this.handleQuery()
|
||
},
|
||
|
||
// 新增设备 (如果您的旧组件AddOrEditForm.vue仍然需要,可以保留此方法)
|
||
handleAddDevice() {
|
||
this.addOrEditFormTitle = '新增'
|
||
this.editForm = null
|
||
this.addOrEditFormVisible = true
|
||
},
|
||
|
||
// 新增子节点
|
||
handleAddChild(row) {
|
||
const { id, nodeName, nodelevel, nodeCount, projectId } = row
|
||
|
||
this.addOrEditFormTitle = '新增'
|
||
this.editForm = null
|
||
this.addOrEditFormVisible = true
|
||
|
||
const editForm = { deviceName: nodeName, id, level: nodelevel * 1 + 1, nodeCount, projectId }
|
||
this.$nextTick(() => {
|
||
this.$refs.addOrEditComponentRef.setFormData(editForm)
|
||
})
|
||
},
|
||
|
||
// 获取模型列表
|
||
// 生成 UUID,备用当 nodeId 缺失时
|
||
generateUUID() {
|
||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||
const r = (Math.random() * 16) | 0
|
||
const v = c === 'x' ? r : (r & 0x3) | 0x8
|
||
return v.toString(16)
|
||
})
|
||
},
|
||
|
||
// 获取模型列表
|
||
// 获取模型列表 - 最终优化版
|
||
// 获取模型列表 - 最终优化版
|
||
async getModelList() {
|
||
this.loading = true
|
||
|
||
const { rows: res } = await getModelListApi(this.queryParams)
|
||
console.log(res, '模型列表')
|
||
this.loading = false
|
||
this.modelList = this.onBuildTree(res)
|
||
// getModelListApi(this.queryParams)
|
||
// .then((response) => {
|
||
// const rawList = response.rows || []
|
||
|
||
// // 增强型递归处理
|
||
// const formatNodes = (nodes, level = 2) => {
|
||
// // 从第二级开始
|
||
// if (!Array.isArray(nodes)) return []
|
||
|
||
// return nodes
|
||
// .filter((item) => item && item.nodeId) // 过滤无效节点
|
||
// .map((item) => ({
|
||
// ...item,
|
||
// id: item.nodeId,
|
||
// name: item.nodeName || '未命名节点',
|
||
// level: level, // 添加层级标记
|
||
// // 递归处理子节点,层级+1
|
||
// nodes: formatNodes(item.nodes || item.children || [], level + 1),
|
||
// }))
|
||
// }
|
||
|
||
// // 处理项目层数据
|
||
// const formatted = rawList.map((project) => {
|
||
// const children = project.children || []
|
||
|
||
// // 获取层级名称(项目级才需要)
|
||
// let levelName = ''
|
||
// let nodeCount = ''
|
||
// if (this.levelList && this.levelList.length > 0) {
|
||
// const levelConfig = this.levelList.find(
|
||
// (item) => item.configId == project.level, // 注意类型匹配
|
||
// )
|
||
// levelName = levelConfig ? levelConfig.configName : ''
|
||
// }
|
||
|
||
// return {
|
||
// ...project,
|
||
// id: project.projectId,
|
||
// name: project.proName || '未命名项目',
|
||
// level: 1, // 项目级标记为1
|
||
// levelName: project.levelName, // 添加层级名称
|
||
// nodes: formatNodes(children),
|
||
// rawData: project,
|
||
// nodeCount: project.nodeCount,
|
||
// }
|
||
// })
|
||
|
||
// this.modelList = formatted
|
||
// this.total = response.total
|
||
// this.loading = false
|
||
// })
|
||
// .catch((error) => {
|
||
// console.error('获取模型列表失败:', error)
|
||
// this.loading = false
|
||
// this.$message.error('模型数据加载失败,请重试')
|
||
// })
|
||
},
|
||
// 上传按钮
|
||
handleUploadModel(row) {
|
||
this.$nextTick(() => {
|
||
this.currentProjectId = row.id || null
|
||
this.useOrReturnFormVisible = true
|
||
// 找到子组件并调用它的 resetForm 方法 (如果存在)
|
||
// 确保 useOrReturnFormComponentRef 存在且 resetForm 是一个函数
|
||
if (
|
||
this.$refs.useOrReturnFormComponentRef &&
|
||
typeof this.$refs.useOrReturnFormComponentRef.resetForm === 'function'
|
||
) {
|
||
this.$refs.useOrReturnFormComponentRef.resetForm()
|
||
}
|
||
})
|
||
},
|
||
|
||
// 查看记录按钮
|
||
handleViewRecord(row) {
|
||
console.log(row)
|
||
this.useRecordFormVisible = true
|
||
},
|
||
|
||
// 编辑按钮 (旧的AddOrEditForm组件使用)
|
||
handleEdit(row) {
|
||
const { id, proName, level, nodeCount } = row
|
||
this.addOrEditFormTitle = '编辑'
|
||
this.editForm = null
|
||
this.addOrEditFormVisible = true
|
||
|
||
const editForm = { deviceName: proName, id, level, nodeCount }
|
||
this.$nextTick(() => {
|
||
// 确保 addOrEditComponentRef 存在
|
||
if (this.$refs.addOrEditComponentRef) {
|
||
this.$refs.addOrEditComponentRef.setFormData(editForm)
|
||
}
|
||
})
|
||
},
|
||
|
||
/** 修改按钮操作 (使用新的弹窗) */
|
||
handleUpdate(row) {
|
||
this.reset()
|
||
const proId = row.id // 对于模型列表,id 对应 projectId
|
||
getProject(proId)
|
||
.then((response) => {
|
||
this.form = response.data
|
||
this.open = true
|
||
this.title = '修改项目'
|
||
// 注意:这里 form.level 的赋值可能需要根据您的实际数据结构调整
|
||
// 比如,如果 response.data.level 是 configId,则可以直接赋值
|
||
// this.form.level = response.data.level;
|
||
})
|
||
.catch((error) => {
|
||
console.error('获取项目详情失败:', error)
|
||
})
|
||
},
|
||
|
||
// 删除按钮
|
||
handleDelete(id) {
|
||
this.$modal
|
||
.confirm('是否确认删除当前节点数据吗?')
|
||
.then(async () => {
|
||
// 根据 row.level 判断是删除项目还是删除模型节点
|
||
// let res
|
||
// if (row.level === 1) {
|
||
// // 假设 level 1 是项目
|
||
// res = await delProject(row.id) // 使用 delProject API 删除项目
|
||
// } else {
|
||
// res = await delModelApi({ id: row.id }) // 使用 delModelApi 删除模型节点
|
||
// }
|
||
|
||
const res = await delModelApi({ id })
|
||
if (res.code === 200) {
|
||
this.$modal.msgSuccess('删除成功')
|
||
this.getModelList()
|
||
} else {
|
||
this.$modal.msgError(res.msg || '删除失败')
|
||
}
|
||
})
|
||
.catch(() => {
|
||
// 用户取消删除
|
||
})
|
||
},
|
||
|
||
// 关闭新增或编辑对话框 (针对旧的AddOrEditForm组件)
|
||
closeAddOrEditFormDialog(isSuccess) {
|
||
if (this.$refs.addOrEditComponentRef) {
|
||
this.$refs.addOrEditComponentRef.resetForm()
|
||
}
|
||
this.addOrEditFormVisible = false
|
||
if (isSuccess) {
|
||
this.getModelList()
|
||
}
|
||
},
|
||
|
||
// 关闭领用或归还对话框 (UseOrReturnForm)
|
||
closeUseOrReturnFormDialog() {
|
||
// 这个方法现在由 @closeUseOrReturnFormDialog="useOrReturnFormVisible = false" 直接处理,
|
||
// 如果还需要 resetForm,可以取消注释下面两行并确保子组件有此方法
|
||
// if (this.$refs.useOrReturnFormComponentRef) {
|
||
// this.$refs.useOrReturnFormComponentRef.resetForm();
|
||
// }
|
||
this.useOrReturnFormVisible = false
|
||
},
|
||
|
||
// 新增/修改项目对话框的取消按钮
|
||
cancel() {
|
||
this.open = false
|
||
this.reset()
|
||
},
|
||
// 新增/修改项目对话框的表单重置
|
||
reset() {
|
||
this.form = {
|
||
proId: undefined,
|
||
proName: undefined,
|
||
proType: undefined,
|
||
unit: undefined,
|
||
chargePerson: undefined,
|
||
location: undefined,
|
||
remark: undefined,
|
||
level: undefined,
|
||
}
|
||
this.resetForm('form') // Element UI 的 resetForm 方法
|
||
},
|
||
/** 提交按钮 (新增/修改项目) */
|
||
submitForm: function () {
|
||
this.$refs['form'].validate((valid) => {
|
||
if (valid) {
|
||
if (this.form.proId != undefined) {
|
||
updateProject(this.form)
|
||
.then((response) => {
|
||
this.$modal.msgSuccess('修改成功')
|
||
this.open = false
|
||
this.getModelList() // 刷新列表
|
||
})
|
||
.catch((error) => {
|
||
console.error('修改项目失败:', error)
|
||
})
|
||
} else {
|
||
addProject(this.form)
|
||
.then((response) => {
|
||
this.$modal.msgSuccess('新增成功')
|
||
this.open = false
|
||
this.getModelList() // 刷新列表
|
||
})
|
||
.catch((error) => {
|
||
console.error('新增项目失败:', error)
|
||
})
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// 获取所有的项目下拉选
|
||
async getProjectSelectList() {
|
||
const { data: res } = await getProjectSelectListApi()
|
||
|
||
console.log(res, '下拉选的所有数据')
|
||
this.projectSelectList = res
|
||
|
||
if (res.length > 0) {
|
||
this.queryParams.projectId = res[0].id
|
||
this.getModelList()
|
||
}
|
||
},
|
||
|
||
// 新增二级节点
|
||
|
||
handleAddLevelTwo() {
|
||
this.addOrEditFormTitle = '新增'
|
||
this.editForm = null
|
||
this.addOrEditFormVisible = true
|
||
const deviceName = this.projectSelectList.find((item) => item.id === this.queryParams.projectId).name
|
||
const nodeCount = this.projectSelectList.find((item) => item.id === this.queryParams.projectId).nodeCount
|
||
const editForm = {
|
||
deviceName,
|
||
level: 2,
|
||
nodeCount,
|
||
projectId: this.queryParams.projectId,
|
||
id: this.queryParams.projectId,
|
||
}
|
||
this.$nextTick(() => {
|
||
this.$refs.addOrEditComponentRef.setFormData(editForm)
|
||
})
|
||
},
|
||
|
||
// 项目切换
|
||
handleProjectChange() {
|
||
this.getModelList()
|
||
},
|
||
|
||
// 递归构建树形结构
|
||
onBuildTree(data) {
|
||
// 1. 筛选顶级节点(nodelevel为"2")
|
||
const topNodes = data.filter((item) => item.nodelevel == '2')
|
||
return topNodes.map((node) => ({
|
||
...node,
|
||
children: this.findChildren(node, data), // 顶级节点的子节点
|
||
}))
|
||
},
|
||
|
||
// 查找子节点
|
||
findChildren(node, data) {
|
||
// 寻找parentId等于当前节点id的所有项,作为子节点
|
||
const children = data.filter((item) => item.parentId == node.id)
|
||
// 如果有子节点,递归处理每个子节点的后代
|
||
if (children.length > 0) {
|
||
// 为每个子节点添加nodes属性,并递归查找其后代
|
||
return children.map((child) => ({
|
||
...child,
|
||
children: this.findChildren(child, data), // 递归关键:子节点的子节点
|
||
}))
|
||
}
|
||
// 没有子节点时返回空数组(或保留null,根据需求调整)
|
||
return []
|
||
},
|
||
|
||
// 模型预览
|
||
handleModelPreview() {
|
||
this.modelPreviewVisible = true
|
||
|
||
// 百度地图初始化
|
||
this.initMap()
|
||
},
|
||
|
||
// 预览弹框关闭
|
||
onCloseMapView() {
|
||
// 销毁地图实例
|
||
if (this.map) {
|
||
this.map = null
|
||
}
|
||
},
|
||
|
||
// 百度地图初始化
|
||
initMap() {
|
||
// 初始化开启地球模式
|
||
const lon = this.projectSelectList.find((item) => item.id == this.queryParams.projectId).lon * 1
|
||
const lat = this.projectSelectList.find((item) => item.id == this.queryParams.projectId).lat * 1
|
||
this.$nextTick(() => {
|
||
this.map = new BMapGL.Map('map-container') // 创建地图实例
|
||
this.map.setMapType(BMAP_EARTH_MAP) // 地球模式
|
||
this.map.setDisplayOptions({
|
||
poiText: false, // 隐藏POI文字
|
||
poiIcon: false, // 隐藏POI图标
|
||
building: false, // 隐藏建筑物
|
||
})
|
||
let point = new BMapGL.Point(lon, lat) // 创建点坐标
|
||
this.map.centerAndZoom(point, 16) // 初始化地图,设置中心点坐标和地图级别
|
||
this.map.enableScrollWheelZoom(true) // 启用滚轮放大缩小
|
||
|
||
this.drawModel(this.modelPreviewInfoList)
|
||
})
|
||
},
|
||
|
||
// drawModel方法
|
||
async drawModel(modelInfoList) {
|
||
// 清除现有覆盖物
|
||
this.map.clearOverlays()
|
||
|
||
for (const item of modelInfoList) {
|
||
console.log(item, 'item')
|
||
try {
|
||
// 处理新的LWPOLYLINE数据结构
|
||
if (item.entityType === 'LWPOLYLINE') {
|
||
console.log('处理LWPOLYLINE:', item)
|
||
const pointList = JSON.parse(item.geometry)
|
||
console.log(pointList, 'pointList')
|
||
const newPointList = pointList?.segments
|
||
console.log(newPointList, 'newPointList')
|
||
|
||
// 根据newPointList 中的type绘制线和半圆
|
||
newPointList.forEach((item) => {
|
||
if (item.type === 'line') {
|
||
console.log(item, 'item画直线, ')
|
||
const line = new BMapGL.Polyline(
|
||
[
|
||
new BMapGL.Point(item.start[0], item.start[1]),
|
||
new BMapGL.Point(item.end[0], item.end[1]),
|
||
],
|
||
{
|
||
strokeColor: 'red',
|
||
strokeWeight: 2,
|
||
strokeOpacity: 0.8,
|
||
},
|
||
)
|
||
this.map.addOverlay(line)
|
||
} else if (item.type === 'arc') {
|
||
console.log('画半圆', item)
|
||
// 绘制半圆弧线
|
||
if (item.start_point && item.end_point && item.center && item.radius) {
|
||
const line = new BMapGL.Polyline(
|
||
[
|
||
new BMapGL.Point(item.start_point[0], item.start_point[1]),
|
||
new BMapGL.Point(item.end_point[0], item.end_point[1]),
|
||
],
|
||
{
|
||
strokeColor: 'red',
|
||
strokeWeight: 2,
|
||
strokeOpacity: 0.8,
|
||
},
|
||
)
|
||
this.map.addOverlay(line)
|
||
// const arcPoints = this.generateArcPoints(
|
||
// item.center,
|
||
// item.radius,
|
||
// item.start_point,
|
||
// item.end_point,
|
||
// )
|
||
// const arcPolyline = new BMapGL.Polyline(arcPoints, {
|
||
// strokeColor: 'red',
|
||
// strokeWeight: 2,
|
||
// strokeOpacity: 0.8,
|
||
// })
|
||
// this.map.addOverlay(arcPolyline)
|
||
} else {
|
||
// 如果没有起点和终点,则绘制完整圆(备用方案)
|
||
const circle = new BMapGL.Circle(
|
||
new BMapGL.Point(item.center[0], item.center[1]),
|
||
item.radius,
|
||
{
|
||
strokeColor: 'red',
|
||
strokeWeight: 2,
|
||
fillColor: 'rgba(0,0,255,0.3)',
|
||
},
|
||
)
|
||
this.map.addOverlay(circle)
|
||
}
|
||
}
|
||
})
|
||
|
||
// if (pointList.points && pointList.points.length > 0) {
|
||
// // 提取坐标点和角度信息
|
||
// const pointsWithAngles = pointList.points.map((point) => {
|
||
// const [lng, lat, , , angle] = point // 提取经度、纬度和角度
|
||
// return {
|
||
// point: new BMapGL.Point(lng, lat),
|
||
// angle: angle || 40, // 角度信息
|
||
// }
|
||
// })
|
||
|
||
// console.log(pointsWithAngles, 'pointsWithAngles')
|
||
|
||
// // 根据颜色设置线条样式
|
||
// const getColorByIndex = (colorIndex) => {
|
||
// const colors = ['red', 'yellow', 'green', 'cyan', 'blue', 'magenta', 'white', 'gray']
|
||
// return colors[colorIndex] || 'red'
|
||
// }
|
||
|
||
// // 生成一个0-7的随机整数作为颜色索引
|
||
// const colorIndex = Math.floor(Math.random() * 7)
|
||
|
||
// // 创建带弧度的多段线
|
||
// const curvedLinePoints = this.createCurvedLine(pointsWithAngles)
|
||
|
||
// // 创建多段线(带弧度)
|
||
// const polyline = new BMapGL.Polyline(curvedLinePoints, {
|
||
// strokeColor: getColorByIndex(colorIndex),
|
||
// strokeWeight: 2,
|
||
// strokeOpacity: 0.8,
|
||
// strokeStyle: 'solid',
|
||
// })
|
||
|
||
// this.map.addOverlay(polyline)
|
||
// }
|
||
} else if (item.entityType === 'LINE') {
|
||
const geometry = JSON.parse(item.geometry)
|
||
const start = geometry.start
|
||
const end = geometry.end
|
||
|
||
// 转换坐标
|
||
|
||
const polyline = new BMapGL.Polyline(
|
||
[new BMapGL.Point(start[0], start[1]), new BMapGL.Point(end[0], end[1])],
|
||
{ strokeColor: 'red', strokeWeight: 2, strokeOpacity: 0.8 },
|
||
)
|
||
this.map.addOverlay(polyline)
|
||
} else if (item.entityType === 'CIRCLE') {
|
||
const geometry = JSON.parse(item.geometry)
|
||
const center = geometry.center
|
||
const radius = geometry.radius
|
||
|
||
// 转换中心点坐标
|
||
|
||
if (center.length > 0) {
|
||
const circle = new BMapGL.Circle(new BMapGL.Point(center[0], center[1]), radius, {
|
||
strokeColor: 'blue',
|
||
strokeWeight: 2,
|
||
fillColor: 'rgba(0,0,255,0.3)',
|
||
})
|
||
this.map.addOverlay(circle)
|
||
}
|
||
} else {
|
||
// 处理多边形或其他图形
|
||
const pointList = JSON.parse(item.geometry)
|
||
|
||
console.log(pointList, 'pointList')
|
||
|
||
if (pointList.points.length > 0) {
|
||
// 解析点坐标和角度(假设格式为 [x, y, ..., angle])
|
||
const path = pointList.points.map((p) => {
|
||
const [lng, lat, , , angle] = p // 提取角度(最后一个参数)
|
||
return {
|
||
point: new BMapGL.Point(lng, lat),
|
||
angle: angle || 0, // 角度默认为0
|
||
}
|
||
})
|
||
|
||
let overlay = null
|
||
if (path.length === 1) {
|
||
// 1. 单点:绘制带方向的标记(箭头)
|
||
const { point, angle } = path[0]
|
||
|
||
// 创建自定义箭头图标(根据角度旋转)
|
||
const arrowIcon = new BMapGL.Icon(
|
||
'//api.map.baidu.com/img/markers.png', // 可替换为箭头图片
|
||
new BMapGL.Size(20, 34),
|
||
{
|
||
anchor: new BMapGL.Size(10, 34), // 图标锚点
|
||
imageOffset: new BMapGL.Size(0, 0), // 图片偏移
|
||
},
|
||
)
|
||
|
||
overlay = new BMapGL.Marker(point, {
|
||
icon: arrowIcon,
|
||
rotation: (angle * 180) / Math.PI, // 转为角度制
|
||
})
|
||
} else if (path.length === 2) {
|
||
// 2. 线:绘制带箭头的线(终点显示角度方向)
|
||
const linePoints = path.map((p) => p.point)
|
||
const endAngle = (path[1].angle * 180) / Math.PI // 终点角度
|
||
|
||
// 绘制基础线条
|
||
overlay = new BMapGL.Polyline(linePoints, {
|
||
strokeColor: 'green',
|
||
strokeWeight: 2,
|
||
})
|
||
|
||
// 在终点添加带角度的箭头标记
|
||
const arrowMarker = new BMapGL.Marker(linePoints[1], {
|
||
icon: new BMapGL.Icon(
|
||
'//api.map.baidu.com/img/markers.png',
|
||
new BMapGL.Size(20, 34),
|
||
{ anchor: new BMapGL.Size(10, 34) },
|
||
),
|
||
rotation: endAngle,
|
||
})
|
||
this.map.addOverlay(arrowMarker) // 额外添加箭头标记
|
||
} else {
|
||
// 3. 多边形:保持原有逻辑,可在顶点添加角度标记
|
||
const polygonPoints = path.map((p) => p.point)
|
||
overlay = new BMapGL.Polygon(polygonPoints, {
|
||
strokeColor: '#3388ff',
|
||
strokeWeight: 2,
|
||
fillColor: 'rgba(51,136,255,0.2)',
|
||
})
|
||
}
|
||
this.map.addOverlay(overlay)
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('绘制图元失败:', item, error)
|
||
}
|
||
}
|
||
|
||
const points = this.map
|
||
.getOverlays()
|
||
.map((overlay) => {
|
||
if (overlay instanceof BMapGL.Marker) {
|
||
return overlay.getPosition()
|
||
} else if (overlay instanceof BMapGL.Polyline || overlay instanceof BMapGL.Polygon) {
|
||
return overlay.getPath()
|
||
} else if (overlay instanceof BMapGL.Circle) {
|
||
return overlay.getCenter()
|
||
}
|
||
})
|
||
.flat()
|
||
.filter(Boolean) // 过滤空值
|
||
|
||
if (points.length > 0) {
|
||
// 计算当前视图的最佳缩放级别
|
||
const viewport = this.map.getViewport(points)
|
||
|
||
// 调整缩放级别:在最佳级别基础上增加n级(值越大视角越近)
|
||
const zoomOffset = 2 // 视角拉近的幅度(建议1-3)
|
||
// const adjustedZoom = Math.min(
|
||
// viewport.zoom + zoomOffset,
|
||
// this.map.getMaxZoom(), // 不超过地图最大缩放级别
|
||
// )
|
||
|
||
const adjustedZoom = Math.min(
|
||
16, // 直接设置一个较大的固定值(如18)
|
||
this.map.getMaxZoom(),
|
||
)
|
||
|
||
// 应用调整后的视图
|
||
this.map.setViewport(points, {
|
||
zoom: adjustedZoom, // 使用调整后的缩放级别
|
||
padding: [50, 50, 50, 50], // 增加边距,避免图形贴边
|
||
})
|
||
}
|
||
},
|
||
|
||
// 创建带弧度的线条
|
||
createCurvedLine(pointsWithAngles) {
|
||
if (pointsWithAngles.length < 2) {
|
||
return pointsWithAngles.map((p) => p.point)
|
||
}
|
||
|
||
const curvedPoints = []
|
||
|
||
for (let i = 0; i < pointsWithAngles.length - 1; i++) {
|
||
const currentPoint = pointsWithAngles[i]
|
||
const nextPoint = pointsWithAngles[i + 1]
|
||
|
||
// 添加当前点
|
||
curvedPoints.push(currentPoint.point)
|
||
|
||
// 如果当前点有角度信息,创建弧度
|
||
if (currentPoint.angle !== 0) {
|
||
const controlPoints = this.generateCurveControlPoints(
|
||
currentPoint.point,
|
||
nextPoint.point,
|
||
currentPoint.angle,
|
||
)
|
||
curvedPoints.push(...controlPoints)
|
||
}
|
||
}
|
||
|
||
// 添加最后一个点
|
||
curvedPoints.push(pointsWithAngles[pointsWithAngles.length - 1].point)
|
||
|
||
return curvedPoints
|
||
},
|
||
|
||
// 生成曲线控制点(基于角度)
|
||
generateCurveControlPoints(startPoint, endPoint, angle) {
|
||
const controlPoints = []
|
||
const segments = 5 // 在两点之间插入5个控制点来创建平滑曲线
|
||
|
||
// 计算起点和终点之间的距离
|
||
const distance = this.calculateDistance(startPoint, endPoint)
|
||
|
||
// 根据角度计算弧度半径
|
||
const radius = Math.abs(distance * 0.3) // 弧度半径为基础距离的30%
|
||
|
||
for (let i = 1; i < segments; i++) {
|
||
const t = i / segments // 插值参数 (0-1)
|
||
|
||
// 线性插值得到基础点
|
||
const baseLng = startPoint.lng + (endPoint.lng - startPoint.lng) * t
|
||
const baseLat = startPoint.lat + (endPoint.lat - startPoint.lat) * t
|
||
|
||
// 根据角度计算偏移
|
||
const offsetDistance = radius * Math.sin(t * Math.PI) // 使用正弦函数创建平滑弧度
|
||
const offsetLng = offsetDistance * Math.cos(angle) * 0.0001 // 经度偏移
|
||
const offsetLat = offsetDistance * Math.sin(angle) * 0.0001 // 纬度偏移
|
||
|
||
// 创建控制点
|
||
const controlPoint = new BMapGL.Point(baseLng + offsetLng, baseLat + offsetLat)
|
||
|
||
controlPoints.push(controlPoint)
|
||
}
|
||
|
||
return controlPoints
|
||
},
|
||
|
||
// 计算两点之间的距离(简化版本)
|
||
calculateDistance(point1, point2) {
|
||
const dx = point2.lng - point1.lng
|
||
const dy = point2.lat - point1.lat
|
||
return Math.sqrt(dx * dx + dy * dy)
|
||
},
|
||
|
||
// 生成圆弧点序列(用于绘制半圆弧线)
|
||
generateArcPoints(center, radius, startPoint, endPoint) {
|
||
// 将坐标转换为 BMapGL.Point 格式(如果还不是)
|
||
const centerPoint = Array.isArray(center) ? new BMapGL.Point(center[0], center[1]) : center
|
||
const start = Array.isArray(startPoint) ? new BMapGL.Point(startPoint[0], startPoint[1]) : startPoint
|
||
const end = Array.isArray(endPoint) ? new BMapGL.Point(endPoint[0], endPoint[1]) : endPoint
|
||
|
||
// 计算起点和终点相对于圆心的角度(弧度)
|
||
const startAngle = Math.atan2(start.lat - centerPoint.lat, start.lng - centerPoint.lng)
|
||
const endAngle = Math.atan2(end.lat - centerPoint.lat, end.lng - centerPoint.lng)
|
||
|
||
// 计算圆心角(弧度)
|
||
let angleDiff = endAngle - startAngle
|
||
|
||
// 标准化角度差到 [-π, π] 范围
|
||
if (angleDiff > Math.PI) {
|
||
angleDiff -= 2 * Math.PI
|
||
} else if (angleDiff < -Math.PI) {
|
||
angleDiff += 2 * Math.PI
|
||
}
|
||
|
||
// 确定绘制方向:默认按较小角度差的方向绘制
|
||
// 如果角度差大于 π(180度),则反向绘制
|
||
if (Math.abs(angleDiff) > Math.PI) {
|
||
angleDiff = angleDiff > 0 ? angleDiff - 2 * Math.PI : angleDiff + 2 * Math.PI
|
||
}
|
||
|
||
// 计算实际半径(度单位)
|
||
// 如果半径单位是米,需要转换为度
|
||
// 1度纬度约等于111公里,1度经度在当前位置约等于111*cos(lat)公里
|
||
const latRad = (centerPoint.lat * Math.PI) / 180
|
||
const metersPerDegreeLat = 111000 // 每度纬度约111公里
|
||
|
||
// 判断半径单位:如果半径值很大(>1),可能是米;如果很小(<0.1),可能是度
|
||
// 这里假设如果半径大于1,则是米单位,需要转换
|
||
let radiusInDegrees = radius
|
||
if (radius > 1) {
|
||
// 假设是米单位,转换为度
|
||
radiusInDegrees = radius / metersPerDegreeLat
|
||
}
|
||
|
||
// 生成圆弧上的点
|
||
const arcPoints = []
|
||
// 根据角度动态调整点的数量,确保圆弧平滑
|
||
const segments = Math.max(30, Math.abs(angleDiff) * 30)
|
||
|
||
// 添加起点
|
||
arcPoints.push(start)
|
||
|
||
// 生成圆弧中间的点
|
||
for (let i = 1; i < segments; i++) {
|
||
const t = i / segments // 插值参数 (0-1)
|
||
const angle = startAngle + angleDiff * t
|
||
|
||
// 计算圆弧上的点坐标
|
||
// 考虑经纬度的差异,使用正确的转换
|
||
const deltaLng = (radiusInDegrees * Math.cos(angle)) / Math.cos(latRad)
|
||
const deltaLat = radiusInDegrees * Math.sin(angle)
|
||
const x = centerPoint.lng + deltaLng
|
||
const y = centerPoint.lat + deltaLat
|
||
|
||
arcPoints.push(new BMapGL.Point(x, y))
|
||
}
|
||
|
||
// 添加终点
|
||
arcPoints.push(end)
|
||
|
||
return arcPoints
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
/* 修改树形表格的展开图标为 +/- */
|
||
::v-deep .el-table .el-table__expand-icon {
|
||
transform: rotate(0deg); /* 取消默认的旋转效果 */
|
||
}
|
||
|
||
::v-deep .el-table .el-table__expand-icon .el-icon-arrow-right:before {
|
||
content: '+'; /* 更大的加号 */
|
||
font-size: 16px; /* 调整大小 */
|
||
font-weight: bold;
|
||
border: 1px solid #409eff;
|
||
color: #409eff;
|
||
background-color: #f0f7ff;
|
||
}
|
||
|
||
::v-deep .el-table .el-table__expand-icon--expanded .el-icon-arrow-right:before {
|
||
content: '-'; /* 更大的减号 */
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
border: 1px solid #409eff;
|
||
color: #409eff;
|
||
background-color: #f0f7ff;
|
||
}
|
||
|
||
#map-container {
|
||
min-height: 76vh;
|
||
}
|
||
</style>
|