LinYeFangHuo/src/views/demo/layer/index.vue

784 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<LayerTree
ref="treeRef"
class="w-1/4 xl:w-1/5"
@select="handleSelect"
@flyToLayer="flyToLayer"
@edit="editLayer"
@add="addLayer"
@checked="checkedLayer"
@remove="tableVisible = false"
/>
<div class="map-container w-3/4 xl:w-4/5">
<Map @onLoad="onMapLoad" />
<!-- 数据列表 -->
<div class="table-constainer" v-show="tableVisible">
<div class="drawer-title-box">
<a-input
v-model:value="keyWord"
placeholder="字段"
style="width: 200px; margin-right: 10px"
/>
<a-input
v-model:value="keyValue"
placeholder="字段值"
style="width: 200px; margin-right: 10px"
/>
<a-button type="primary" @click="getList"> 查询 </a-button>
<a-dropdown>
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="addData('point')">
<UserOutlined />
点数据
</a-menu-item>
<a-menu-item key="2" @click="addData('polyline')">
<UserOutlined />
线数据
</a-menu-item>
<a-menu-item key="3" @click="addData('polygon')">
<UserOutlined />
面数据
</a-menu-item>
</a-menu>
</template>
<a-button>
添加
<DownOutlined />
</a-button>
</a-dropdown>
<a-button type="primary" @click="exportTemplate"> 导出模版 </a-button>
<a-button type="primary" @click="changeBatchProcessingModal(true)"> 批量操作 </a-button>
<a-button type="primary" @click="styleHandle"> 样式配置 </a-button>
</div>
<div class="close-icon" @click="tableVisible = false">
<CloseOutlined />
</div>
<div class="table-content">
<BasicTable @register="registerTable" @change="handleTableChange">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
label: '定位',
onClick: posData.bind(null, record),
},
{
label: '编辑',
onClick: editData.bind(null, record),
},
{
label: '查看',
onClick: viewData.bind(null, record),
},
{
label: '删除',
color: 'error',
onClick: delData.bind(null, record),
},
]"
/>
</template>
</template>
</BasicTable>
</div>
</div>
<!-- 详情 -->
<div class="data-detail" v-if="showTable == 'detail'">
<div class="detail-content">
<a-descriptions title="详情" :column="columnVal">
<a-descriptions-item
v-for="(item, index) in detailData"
:key="index"
:label="item.label"
>{{ item.value }}</a-descriptions-item
>
</a-descriptions>
</div>
<div class="detail-button">
<a-button type="primary" @click="detailToEdit"> 编辑 </a-button>
<a-button type="primary" @click="closeDetail" danger> 关闭 </a-button>
</div>
</div>
<!-- 编辑 -->
<div class="data-edit" v-if="showTable == 'edit' || showTable == 'add'">
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item
v-for="(item, index) in headData"
:ref="item"
:label="item.title"
:name="item"
:key="index"
>
<a-input v-model:value="formState[item.dataIndex]" />
</a-form-item>
</a-form>
<div class="data-button">
<a-button type="primary" @click="onSubmit">保存</a-button>
<a-button style="margin-left: 10px" @click="resetForm">取消</a-button>
</div>
</div>
<!-- 样式配置 -->
<div class="data-style" v-if="showTable == 'style'">
<div class="img-box">
<a-input v-model:value="styleName" placeholder="样式名称" style="margin-bottom: 10px" />
<p style="color: #ed6f6f" v-if="styleName == ''">请输入样式名称</p>
<a-upload-dragger
v-model:fileList="fileList"
name="file"
@change="handleChange"
:multiple="false"
:maxCount="1"
:customRequest="handleCustomRequest"
>
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text">上传SLD文件</p>
</a-upload-dragger>
<div v-if="fileUrl">
<p>{{ fileUrlView.name }}</p>
<a-button type="primary" @click="editorHandle">在编辑器中打开</a-button>
</div>
</div>
<div class="data-button">
<a-button type="primary" @click="showTable = ''" danger> 关闭 </a-button>
<a-button @click="styleCancel"> 取消 </a-button>
<a-button type="primary" @click="styleSubmit"> 提交 </a-button>
</div>
</div>
</div>
<AddModel @register="registerAddModal" :treeData="treeData" @success="handleSuccess" />
<EditorModel @register="registerEditorModal" @editorCancel="editorCancel" />
<a-modal
:keyboard="false"
:maskClosable="false"
wrapClassName="batch-modal-wrap"
class="BatchProcessingModal"
width="1660px"
v-model:open="batchProcessingModalOpen"
:footer="null"
:closable="false"
:destroyOnClose="true"
style="top: 50px"
>
<BatchProcessingModal
:applicationName="applicationName"
:tableName="tableName"
@changeBatchProcessingModal="changeBatchProcessingModal"
/>
</a-modal>
<TempeleteModel @register="registerTempeleteModel" :tableName="tableName" />
</PageWrapper>
</template>
<script setup lang="ts">
import { LayerTree, AddModel, Map, EditorModel, TempeleteModel } from './page';
import { ref, UnwrapRef, reactive, onMounted } from 'vue';
import { PageWrapper } from '@/components/Page';
import { useModal } from '@/components/Modal';
import { CloseOutlined, InboxOutlined } from '@ant-design/icons-vue';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { columns } from './data';
import {
tableDataByTableName,
updateTableData,
addTableData,
uploadSldStyle,
getSldFilePath,
} from '@/api/application/layer';
import { ValidateErrorEntity } from 'ant-design-vue/es/form/interface';
import { useMessage } from '@/hooks/web/useMessage';
import { uploadFile, fun_Delete } from '@/api/demo/files';
import { getAppEnvConfig } from '@/utils/env';
import BatchProcessingModal from './BatchProcessingModal/index.vue';
import { Upload, UploadShape, UploadExcelAll } from '@/api/demo/BatchProcessingModal';
import { message } from 'ant-design-vue';
import { EventBus } from '@/utils/eventBus';
import {
GetGeometryCenter,
EditDrawingGeometry,
AddDrawingGeometry,
PreviewDrawingGeometry,
DrawingEnd,
GeojsonToWkt,
} from './geometryHandler.ts';
import * as mars3d from 'mars3d';
const { VITE_GLOB_GEOSERVER_BASE_URL } = getAppEnvConfig();
let globalMap: mars3d.Map;
const onMapLoad = (map) => {
globalMap = map;
};
EventBus.on('editLayerEnd', function (e) {
formState.value.geom = e;
});
// 图层目录树控制逻辑
const handlerLoadLayer = (node) => {
if(node.node.tableName){ // 图层节点
handlerToggleLayer(node.node);
}else{ // 分类节点
node.node.child?.forEach((item,index)=>{
if(!node.node.checked){
item.checked = node.node.checked;
handlerToggleLayer(item);
}
})
}
};
const handlerToggleLayer = (node)=>{
if(!node.checked){
let layer = globalMap.getLayerById(node.id);
if(layer){
layer.show = true
}else{
let tileLayer = new mars3d.layer.WmsLayer({
name: node.applicationName,
id:node.id,
url: VITE_GLOB_GEOSERVER_BASE_URL+"/geoserver/my_workspace/wms",
layers: "my_workspace:"+node.tableName,
parameters: {
transparent: true,
format: 'image/png',
},
crs: "EPSG:4326",
getFeatureInfoParameters: {
feature_count: 10
},
// 单击高亮及其样式
highlight: {
type: "wallP",
diffHeight: 100,
materialType: mars3d.MaterialType.LineFlow,
materialOptions: {
image: "https://data.mars3d.cn/img/textures/fence.png",
color: "#ffff00",
speed: 10, // 速度建议取值范围1-100
axisY: true
}
},
popup: "all",
flyTo: false,
featureToGraphic:function(e){ // 获取点击查询到的数据
let wkt = GeojsonToWkt(e.data.geometry);
let editData = {...e.data.properties}
editData.geom = wkt;
viewData(editData)
}
})
globalMap.addLayer(tileLayer);
}
}else{
let layer = globalMap.getLayerById(node.id);
if(layer){
layer.show = false;
}
}
}
const { VITE_GLOB_API_URL } = getAppEnvConfig();
const { createConfirm, createMessage } = useMessage();
const showTable = ref('');
const tableVisible = ref(false);
const selectVal = ref();
const columnVal = ref(1);
const fileList = ref([]);
const batchProcessingModalOpen = ref(false);
const [registerAddModal, { openModal: openAddModal }] = useModal();
const [registerEditorModal, { openModal: openEditorModal }] = useModal();
const [registerTempeleteModel, { openModal: openTempeleteModel }] = useModal();
const labelCol = { span: 6 };
const wrapperCol = { span: 18 };
const keyWord = ref(null);
const keyValue = ref(null);
const headData = ref();
const [
registerTable,
{ reload, getSelectRows, clearSelectedRowKeys, setTableData, setColumns, setPagination, getColumns },
] = useTable({
// 表格名称
title: '数据列表',
// 获取数据的接口
dataSource: [],
// 表单列信息 BasicColumn[]
columns,
rowKey: 'id',
// 使用搜索表单
useSearchForm: false,
// 显示表格设置工具
showTableSetting: true,
// 是否显示表格边框
bordered: true,
// 序号列
showIndexColumn: false,
// 搜索
handleSearchInfoFn(info) {
return info;
},
actionColumn: {
width: 200,
title: '操作',
dataIndex: 'action',
// slots: { customRender: 'action' },
fixed: 'right',
},
});
const treeRef = ref();
const treeData = ref();
const formRef = ref();
const formState = ref({});
const tableName = ref();
const applicationName = ref();
const styleName = ref();
const rules = {
name: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
],
region: [{ required: true, message: 'Please select Activity zone', trigger: 'change' }],
date1: [{ required: true, message: 'Please pick a date', trigger: 'change', type: 'object' }],
type: [
{
type: 'array',
required: true,
message: 'Please select at least one activity type',
trigger: 'change',
},
],
resource: [{ required: true, message: 'Please select activity resource', trigger: 'change' }],
desc: [{ required: true, message: 'Please input activity form', trigger: 'blur' }],
};
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
const params = {
tableName: tableName.value,
list: [],
};
for (const key in formState.value) {
params.list.push({
name: key,
value: formState.value[key],
});
}
if (showTable.value == 'edit') {
updateTableData(params).then((res) => {
console.log('res', res);
if (res) {
createMessage.success('编辑成功!');
DrawingEnd();
showTable.value = '';
} else {
createMessage.error('编辑失败!');
}
});
} else if (showTable.value == 'add') {
params.list = [];
headData.value?.forEach((item, index) => {
params.list.push({
name: item.dataIndex,
value: formState.value[item] ? formState.value[item] : null,
});
});
console.log('params', params.list);
addTableData(params).then((res) => {
console.log('res', res);
if (res) {
createMessage.success('添加成功!');
DrawingEnd();
showTable.value = '';
} else {
createMessage.error('添加失败!');
}
});
}
//清空图层
DrawingEnd();
})
.catch((error: ValidateErrorEntity<FormState>) => {
console.log('error', error);
});
};
const resetForm = () => {
showTable.value = '';
DrawingEnd();
};
const handleSelect = (record) => {
tableName.value = record.tableName;
applicationName.value = record.applicationName;
tableVisible.value = true;
keyWord.value = null;
keyValue.value = null;
showTable.value = '';
getList();
};
const editLayer = (node) => {
treeData.value = treeRef.value.treeData;
openAddModal(true, {
node,
isUpdate: true,
});
};
const addLayer = () => {
treeData.value = treeRef.value.treeData;
openAddModal(true, {
isUpdate: false,
});
};
const checkedLayer = (node) => {
handlerLoadLayer(node);
}
const flyToLayer = (id) => {
let layer = globalMap.getLayerById(id);
if(layer){
layer.flyTo();
}
}
const hanlerLoadLayer = () => {};
const handleSuccess = () => {
treeRef.value.fetch();
};
const addData = (type) => {
AddDrawingGeometry(globalMap, type);
showTable.value = 'add';
};
const detailData = ref([]);
const editData = (record) => {
selectVal.value = record;
formState.value = record;
showTable.value = 'edit';
EditDrawingGeometry(globalMap, record.geom);
};
const viewData = (record) => {
PreviewDrawingGeometry(globalMap, record.geom);
selectVal.value = record;
formState.value = record;
showTable.value = 'detail';
detailData.value = [];
for (const key in record) {
for (var i = 0; i < headData.value.length; i++) {
if (headData.value[i].dataIndex == key) {
detailData.value.push({
label: headData.value[i].title,
value: record[key],
});
}
}
}
};
const detailToEdit = () => {
editData(formState.value);
showTable.value = 'edit';
};
const detailClose = ()=>{
showTable.value = ''
DrawingEnd();
}
const delData = (record) => {
createConfirm({
iconType: 'info',
title: '删除',
content: '确定要删除当前数据吗?',
onOk: async () => {
// let result = await functionDeleteForm(query);
// if (result) {
// createMessage.success('数据成功!');
// } else {
// createMessage.error('数据失败!');
// }
reload();
},
});
};
const fileUrl = ref('');
const fileUrlView: any = ref({});
const posData = (record) => {
let centerLngLat = GetGeometryCenter(record.geom);
globalMap.flyToPoint(centerLngLat, { radius: 500 });
};
const styleHandle = () => {
showTable.value = 'style';
getSldFilePath({ tablename: tableName.value }).then((res) => {
if (res) {
fileUrlView.value.name = res.filepath;
fileUrl.value = res.filepath;
fileUrlView.value.url = res.filepath;
fileUrlView.value.id = res.fileid;
styleName.value = res.stylename;
} else {
fileUrl.value = '';
fileUrlView.value = {};
styleName.value = '';
}
});
};
const handleChange = (info) => {
fileList.value = info.fileList;
};
// 上传文件接口
const handleCustomRequest = (options) => {
fileList.value = [];
const formData = new FormData();
formData.append('files', options.file);
fileUrlView.value.name = options.file.name;
uploadFile(formData)
.then((res: any) => {
fileUrl.value = res[0].filePath;
fileUrlView.value.url = res[0].filePath;
fileUrlView.value.id = res[0].id;
createMessage.success('上传成功!');
})
.catch((err) => {
options.onError(err);
});
};
const styleSubmit = () => {
if (!styleName.value) {
return createMessage.warning('样式名称不能为空!');
}
const params = {
filepath: fileUrl.value,
styleName: styleName.value,
tablename: tableName.value,
id: fileUrlView.value.id,
};
uploadSldStyle(params).then((res) => {
console.log(res);
if (res) {
showTable.value = '';
createMessage.success('提交成功!');
} else {
createMessage.error('提交失败!');
}
});
};
const styleCancel = () => {
if (fileUrlView.value.id) {
const params = [fileUrlView.value.id];
createConfirm({
iconType: 'info',
title: '取消',
content: '取消会删除当前文件',
onOk: async () => {
fun_Delete(params).then((res) => {
createMessage.success('删除成功!');
showTable.value = '';
});
},
});
} else {
showTable.value = '';
}
};
const editorHandle = () => {
if (!styleName.value) {
return createMessage.warning('样式名称不能为空!');
}
openEditorModal(true, {
urlData: fileUrlView.value,
styleName: styleName.value,
tableName: tableName.value,
});
};
const changeBatchProcessingModal = (type: boolean) => {
batchProcessingModalOpen.value = type;
};
const searchData = reactive({
page: 1,
limit: 10,
});
const handleTableChange = (values: any) => {
searchData.page = values.current;
searchData.limit = values.pageSize;
getList();
};
const getList = () => {
if (!tableName.value) {
tableVisible.value = false;
message.warning('当前图层没有关联空间数据表');
return;
}
let realUseKeyWord = getColumns().find(item => item.dataIndex == keyWord.value || item.title == keyWord.value)
const querys = {
...searchData,
tablename: tableName.value,
keyword: realUseKeyWord? realUseKeyWord.dataIndex: '',
keyvalue: keyValue.value,
};
tableDataByTableName(querys).then((res) => {
setTableData(res.data);
let arr: any = [];
res.headName.forEach((element, index) => {
arr.push({
title: element.comment ? element.comment : element.columnName,
dataIndex: element.columnName,
});
});
headData.value = arr;
setPagination({ total: res.total });
setColumns(arr);
});
};
const exportTemplate = () => {
openTempeleteModel(true);
};
onMounted(() => {});
const editorCancel = () => {
fileList.value = [];
fileUrlView.value = {};
fileUrl.value = '';
};
const closeDetail = () => {
showTable.value = ''
DrawingEnd()
}
</script>
<style lang="less" scoped>
.map-container {
position: relative;
}
.table-constainer {
padding: 20px;
width: 99%;
height: 40vh;
position: absolute;
bottom: 0;
left: 5px;
z-index: 2;
background-color: @component-background;
border-radius: 4px;
}
.drawer-title-box {
span {
margin-right: 100px;
}
button {
margin-right: 10px;
}
}
.close-icon {
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
}
.data-detail {
position: absolute;
top: 0;
right: 0;
background-color: @component-background;
z-index: 3;
width: 30%;
height: 100%;
padding: 20px;
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.2);
.detail-button {
position: absolute;
bottom: 30px;
right: 40px;
background-color: @component-background;
button {
margin-left: 10px;
}
}
.detail-content {
overflow-y: scroll;
height: 80vh;
}
}
.data-edit {
position: absolute;
top: 0;
right: 0;
background-color: @component-background;
z-index: 3;
width: 40%;
height: 100%;
padding: 20px;
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.2);
.data-button {
position: absolute;
width: 90%;
bottom: 30px;
display: flex;
justify-content: flex-end;
button {
margin-left: 10px;
}
}
}
.data-style {
position: absolute;
top: 0;
right: 0;
background-color: @component-background;
z-index: 3;
width: 30%;
height: 100%;
padding: 20px;
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.2);
.img-box {
height: 200px;
}
.data-button {
position: absolute;
width: 100%;
bottom: 30px;
button {
margin-left: 10px;
}
}
}
</style>
<style>
.batch-modal-wrap .ant-modal {
transform: scale(0.75) !important;
transform-origin: center !important;
}
</style>