进度填报单选优化,项目列表地图功能修正

This commit is contained in:
Teo
2025-06-03 19:57:53 +08:00
parent 6758437892
commit 80c9d517ab
4 changed files with 301 additions and 55 deletions

View File

@ -22,7 +22,7 @@
<p <p
v-for="(item, index) in selectLayer" v-for="(item, index) in selectLayer"
class="pl-xl border-rd pr p-3 w-111 mr-1 bg-#909399 flex items-center cursor-pointer justify-between" class="pl-xl border-rd pr p-3 w-111 mr-1 bg-#909399 flex items-center cursor-pointer justify-between"
@click="delLayer(index)" @click="delLayer(index, item.option)"
> >
{{ item.location.name + '被选中为' + item.option }} {{ item.location.name + '被选中为' + item.option }}
<el-icon> <el-icon>
@ -30,35 +30,27 @@
</el-icon> </el-icon>
</p> </p>
</div> </div>
<el-form-item label="类型" class="items-center"> <!-- <el-form-item label="类型" class="items-center">
<el-radio-group v-model="layerType"> <el-radio-group v-model="layerType">
<el-radio value="1" size="large">光伏板</el-radio> <el-radio :value="1" size="large">光伏板</el-radio>
<el-radio value="2" size="large">桩点/支架</el-radio> <el-radio :value="2" size="large">桩点/支架</el-radio>
<el-radio value="3" size="large">方阵</el-radio> <el-radio :value="3" size="large">方阵</el-radio>
<el-radio value="4" size="large">逆变器</el-radio> <el-radio :value="4" size="large">逆变器</el-radio>
<el-radio value="5" size="large">箱变</el-radio> <el-radio :value="5" size="large">箱变</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item> -->
</div> </div>
<div v-if="isMenuVisible" :style="{ left: menuX + 'px', top: menuY + 'px' }" class="fixed bg-white shadow-md rounded-md overflow-hidden"> <div v-if="isMenuVisible" :style="{ left: menuX + 'px', top: menuY + 'px' }" class="fixed bg-white shadow-md rounded-md overflow-hidden">
<ul class="py-1 pl-0"> <ul class="py-1 pl-0">
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('光伏板')"> <li
<i class="fa-solid fa-check mr-2"></i>光伏板 v-for="(item, index) in layerTypeList"
class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer"
@click="handleMenuItemClick(item, index + 1)"
>
<i class="fa-solid fa-check mr-2"></i>{{ item }}
</li> </li>
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('桩点/支架')"> <li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('名称', null)">
<i class="fa-solid fa-times mr-2"></i>桩点/支架
</li>
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('方阵')">
<i class="fa-solid fa-times mr-2"></i>方阵
</li>
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('逆变器')">
<i class="fa-solid fa-times mr-2"></i>逆变器
</li>
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('箱变')">
<i class="fa-solid fa-times mr-2"></i>箱变
</li>
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('名称')">
<i class="fa-solid fa-times mr-2"></i>名称 <i class="fa-solid fa-times mr-2"></i>名称
</li> </li>
</ul> </ul>
@ -98,6 +90,7 @@ const props = defineProps({
}); });
const treeData = ref<any>([]); const treeData = ref<any>([]);
const layerType = ref(null); const layerType = ref(null);
const layerTypeList = ref(['光伏板', '桩点/支架', '方阵', '逆变器', '箱变']);
const contextMenu = ref(null); const contextMenu = ref(null);
const selectLayer = ref([]); const selectLayer = ref([]);
const treeRef = ref<TreeInstance>(); const treeRef = ref<TreeInstance>();
@ -325,7 +318,7 @@ const showMenu = (event: MouseEvent, data) => {
}; };
// 处理菜单项点击事件的方法 // 处理菜单项点击事件的方法
const handleMenuItemClick = (option: string) => { const handleMenuItemClick = (option: string, index: number) => {
isMenuVisible.value = false; isMenuVisible.value = false;
if (selectLayer.value.some((item) => item.location.name === contextMenu.value.name)) { if (selectLayer.value.some((item) => item.location.name === contextMenu.value.name)) {
@ -335,14 +328,17 @@ const handleMenuItemClick = (option: string) => {
if (option !== '名称' && option !== '箱变') return proxy?.$modal.msgError('只能选择一个类型'); if (option !== '名称' && option !== '箱变') return proxy?.$modal.msgError('只能选择一个类型');
} }
selectLayer.value.push({ location: contextMenu.value, option }); selectLayer.value.push({ location: contextMenu.value, option });
console.log('selectLayer.value', selectLayer.value); layerType.value = index ? index : layerType.value; // 设置 layerType 为对应的索引值
emit('handleCheckChange', selectLayer.value); emit('handleCheckChange', selectLayer.value);
}; };
//删除菜单 //删除菜单
const delLayer = (index) => { const delLayer = (index, option) => {
selectLayer.value.splice(index, 1); selectLayer.value.splice(index, 1);
if (option != '名称') {
if (selectLayer.value.every((item) => item.option == '名称')) layerType.value = null;
}
emit('handleCheckChange', selectLayer.value); emit('handleCheckChange', selectLayer.value);
}; };
@ -478,9 +474,7 @@ const addFacilities = async () => {
const reset = () => { const reset = () => {
selectLayer.value = []; selectLayer.value = [];
treeRef.value?.setCheckedKeys([]); treeRef.value?.setCheckedKeys([]);
for (const key in layerData) { sharedSource.clear(); // 清空共享 source 中的所有要素
map.removeLayer(layerData[key]);
}
layerType.value = null; layerType.value = null;
}; };

View File

@ -1,12 +1,12 @@
<template> <template>
<div class="header flex justify-end"> <div class="header flex justify-end">
<el-form :model="queryParams" ref="form" label-width="80px" inline class="flex items-center"> <el-form :model="queryParams" ref="form" label-width="80px" inline class="flex items-center">
<el-form-item label="请选择项目:" prop="pid" label-width="100"> <el-form-item label="请选择项目:" prop="pid" label-width="100" style="margin-bottom: 0">
<el-select v-model="selectedProjectId" placeholder="请选择" @change="handleSelect" clearable> <el-select v-model="selectedProjectId" placeholder="请选择" @change="handleSelect" clearable>
<el-option v-for="item in ProjectList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in ProjectList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="请选择方阵:" prop="pid" label-width="100"> <el-form-item label="请选择方阵:" prop="pid" label-width="100" style="margin-bottom: 0">
<el-select v-model="matrixValue" placeholder="请选择" @change="handleChange" clearable> <el-select v-model="matrixValue" placeholder="请选择" @change="handleChange" clearable>
<el-option v-for="item in matrixOptions" :key="item.id" :label="item.matrixName" :value="item.id" /> <el-option v-for="item in matrixOptions" :key="item.id" :label="item.matrixName" :value="item.id" />
</el-select> </el-select>
@ -19,7 +19,6 @@
style="max-width: 600px" style="max-width: 600px"
:data="progressCategoryList" :data="progressCategoryList"
ref="treeRef" ref="treeRef"
show-checkbox
@check-change="handleCheckChange" @check-change="handleCheckChange"
:props="treeProps" :props="treeProps"
:load="loadNode" :load="loadNode"
@ -27,10 +26,12 @@
lazy lazy
@node-collapse="closeNode" @node-collapse="closeNode"
@node-expand="openNode" @node-expand="openNode"
:render-content="renderContent"
check-strictly
/> />
</div> </div>
<div class="submit"> <div class="submit">
<el-button type="primary" size="default" @click="submit">提交</el-button> <el-button type="primary" size="default" @click="submit" :loading="loading">提交</el-button>
</div> </div>
</template> </template>
@ -45,11 +46,13 @@ import { useUserStoreHook } from '@/store/modules/user';
import { getProjectSquare, listProgressCategory, addDaily, workScheduleListPosition } from '@/api/progress/plan'; import { getProjectSquare, listProgressCategory, addDaily, workScheduleListPosition } from '@/api/progress/plan';
import { ProgressCategoryVO, progressPlanDetailForm } from '@/api/progress/plan/types'; import { ProgressCategoryVO, progressPlanDetailForm } from '@/api/progress/plan/types';
import { Circle, Fill, Stroke, Style, Text } from 'ol/style'; import { Circle, Fill, Stroke, Style, Text } from 'ol/style';
import { defaults as defaultInteractions } from 'ol/interaction';
import Feature from 'ol/Feature'; import Feature from 'ol/Feature';
import { Point, Polygon } from 'ol/geom'; import { Point, Polygon } from 'ol/geom';
import VectorSource from 'ol/source/Vector'; import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector'; import VectorLayer from 'ol/layer/Vector';
import Node from 'element-plus/es/components/tree/src/model/node.mjs'; import Node from 'element-plus/es/components/tree/src/model/node.mjs';
import { ElCheckbox } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 获取用户 store // 获取用户 store
@ -96,20 +99,14 @@ const handleSelect = (projectId: string) => {
/** 进度类别树选中事件 */ /** 进度类别树选中事件 */
const handleCheckChange = (data: any, checked: boolean, indeterminate: boolean) => { const handleCheckChange = (data: any, checked: boolean, indeterminate: boolean) => {
const node: Node | undefined = treeRef.value?.getNode(data.id); const node: Node | undefined = treeRef.value?.getNode(data.id);
if (node && node.level === 3) { if (!checked) return (submitForm.value.id = ''); // 只处理第三级节点的选中事件
console.log('第三级节点被选中:', data, '选中状态:', checked);
}
if (!node || node.level !== 3 || !checked) return;
const parent = node.parent; const parent = node.parent;
if (!parent) return; if (!parent) return;
//消除所有节点的选中状态
// 遍历兄弟节点,取消选中除当前节点之外的其他第三级节点 treeRef.value.setCheckedKeys([], false);
parent.childNodes.forEach((sibling: Node) => { // 设置当前点击项为选中
if (sibling !== node) { treeRef.value.setChecked(data.id, true, false);
treeRef.value.setChecked(sibling.data.id, false, false);
}
});
submitForm.value.id = data.id; // 设置提交表单的id submitForm.value.id = data.id; // 设置提交表单的id
}; };
@ -172,9 +169,20 @@ const loadNode = async (node: any, resolve: (data: any[]) => void) => {
/** 提交按钮点击事件 */ /** 提交按钮点击事件 */
const submit = () => { const submit = () => {
console.log('sunbmitForm', submitForm.value); console.log('sunbmitForm', submitForm.value);
const { finishedDetailIdList, id } = submitForm.value;
if (!id || finishedDetailIdList.length === 0) return proxy?.$modal.msgWarning('请选择图层以及日期');
loading.value = true;
addDaily(submitForm.value) addDaily(submitForm.value)
.then(() => { .then(() => {
proxy?.$modal.msgSuccess('提交成功'); proxy?.$modal.msgSuccess('提交成功');
const scale = Math.max(map.getView().getZoom() / 10, 1); // 获取当前缩放比例
sharedSource.getFeatures().forEach((feature) => {
if (feature.get('highlighted')) {
feature.setStyle(successStyle(feature.get('name'), scale)); // 转为成功样式
feature.set('highlighted', false); // 重置高亮状态
feature.set('status', '2'); // 设置为完成状态
}
});
resetTreeAndMap(); resetTreeAndMap();
}) })
.catch((error) => { .catch((error) => {
@ -186,6 +194,8 @@ const submit = () => {
const resetTreeAndMap = () => { const resetTreeAndMap = () => {
// 重置树形结构选中状态 // 重置树形结构选中状态
treeRef.value?.setCheckedKeys([]); treeRef.value?.setCheckedKeys([]);
//取消加载状态
loading.value = false;
// 清除地图上的所有高亮 // 清除地图上的所有高亮
const scale = Math.max(map.getView().getZoom() / 10, 1); // 获取当前缩放比例 const scale = Math.max(map.getView().getZoom() / 10, 1); // 获取当前缩放比例
sharedSource.getFeatures().forEach((feature) => { sharedSource.getFeatures().forEach((feature) => {
@ -205,6 +215,22 @@ const handleChange = (value: number) => {
getList(); getList();
}; };
//限定部分节点能选择
const renderContent = (context, { node }) => {
if (node.level === 3) {
return h('span', { class: 'custom-tree-node' }, [
h(ElCheckbox, {
modelValue: node.checked,
'onUpdate:modelValue': (val) => node.setChecked(val),
style: 'margin-right: 8px;margin-left: -20px;'
}),
h('span', node.label)
]);
} else {
return h('span', node.label);
}
};
//切换项目重置方阵 //切换项目重置方阵
const resetMatrix = () => { const resetMatrix = () => {
matrixValue.value = undefined; matrixValue.value = undefined;
@ -271,26 +297,29 @@ const initOLMap = () => {
zoom: false, zoom: false,
rotate: false, rotate: false,
attribution: false attribution: false
}),
interactions: defaultInteractions({
doubleClickZoom: false // 禁用双击缩放
}) })
}); });
map.on('click', (e: any) => { map.on('click', (e: any) => {
const zoom = map.getView().getZoom(); const zoom = map.getView().getZoom();
const scale = Math.max(zoom / 10, 1); // 缩放比例,根据需要调整公式 const scale = Math.max(zoom / 10, 1); // 缩放比例,根据需要调整公式
map.forEachFeatureAtPixel(e.pixel, (feature: Feature) => { map.forEachFeatureAtPixel(e.pixel, (feature: Feature) => {
if (feature.get('status') === '2') return; // 如果是完成状态,直接返回
const isHighlighted = feature.get('highlighted') === true;
const geomType = feature.getGeometry().getType(); const geomType = feature.getGeometry().getType();
if (feature.get('status') === '2' || geomType != 'Polygon') return; // 如果是完成状态,直接返回
const isHighlighted = feature.get('highlighted') === true;
if (isHighlighted) { if (isHighlighted) {
feature.setStyle(defaultStyle(feature.get('name'), scale)); // 清除高亮样式 feature.setStyle(defaultStyle(feature.get('name'), scale)); // 清除高亮样式
feature.set('highlighted', false); feature.set('highlighted', false);
submitForm.value.finishedDetailIdList = submitForm.value.finishedDetailIdList.filter((id) => id !== feature.get('id')); // 从已完成列表中移除 submitForm.value.finishedDetailIdList = submitForm.value.finishedDetailIdList.filter((id) => id !== feature.get('id')); // 从已完成列表中移除
return; return;
} }
if (geomType === 'Polygon') { feature.setStyle(highlightStyle(feature.get('name'), scale));
feature.setStyle(highlightStyle(feature.get('name'), scale)); feature.set('highlighted', true);
feature.set('highlighted', true); submitForm.value.finishedDetailIdList.push(feature.get('id')); // 添加到已完成列表
submitForm.value.finishedDetailIdList.push(feature.get('id')); // 添加到已完成列表
}
}); });
}); });
map.getView().on('change:resolution', () => { map.getView().on('change:resolution', () => {
@ -455,10 +484,13 @@ onMounted(() => {
z-index: 1; z-index: 1;
} }
.header { .header {
height: 90px; height: 70px;
width: 100%; width: 100%;
position: absolute; position: absolute;
z-index: 2; z-index: 2;
background: rgba(255, 255, 255, 0.2); /* 半透明白色 */
backdrop-filter: blur(10px); /* 背景模糊 */
-webkit-backdrop-filter: blur(10px); /* 兼容 Safari */
} }
.aside { .aside {
position: absolute; position: absolute;
@ -476,4 +508,8 @@ onMounted(() => {
right: 70px; right: 70px;
z-index: 3; z-index: 3;
} }
.custom-tree-node {
display: flex;
align-items: center;
}
</style> </style>

View File

@ -45,7 +45,17 @@
<el-table v-loading="loading" :data="projectList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="projectList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="60" align="center" /> <el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="项目名称" align="center" prop="projectName" /> <el-table-column label="项目名称" align="center" prop="projectName">
<template #default="scope">
<el-link
:type="scope.row.designId ? 'primary' : 'default'"
:disabled="!scope.row.designId"
@click="handleOpenLayer(scope.row)"
v-loading.fullscreen.lock="fullscreenLoading"
>{{ scope.row.projectName }}</el-link
>
</template>
</el-table-column>
<el-table-column label="项目简称" align="center" prop="shortName" /> <el-table-column label="项目简称" align="center" prop="shortName" />
<el-table-column label="状态" align="center" prop="status"> <el-table-column label="状态" align="center" prop="status">
<template #default="scope"> <template #default="scope">
@ -69,7 +79,7 @@
<el-table-column label="开工时间" align="center" prop="onStreamTime" width="120" /> <el-table-column label="开工时间" align="center" prop="onStreamTime" width="120" />
<el-table-column label="打卡范围" align="center" prop="punchRange" /> <el-table-column label="打卡范围" align="center" prop="punchRange" />
<el-table-column label="设计总量" align="center" prop="designTotal" /> <el-table-column label="设计总量" align="center" prop="designTotal" />
<el-table-column label="是否上传DXF" align="center" prop="designId" width="140"> <!-- <el-table-column label="是否上传DXF" align="center" prop="designId" width="140">
<template #default="scope"> <template #default="scope">
<el-link <el-link
:type="scope.row.designId ? 'primary' : 'default'" :type="scope.row.designId ? 'primary' : 'default'"
@ -79,7 +89,7 @@
>{{ scope.row.designId ? '已上传' : '未上传' }}</el-link >{{ scope.row.designId ? '已上传' : '未上传' }}</el-link
> >
</template> </template>
</el-table-column> </el-table-column> -->
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" /> <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="400"> <el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="400">

File diff suppressed because one or more lines are too long