填报页面修改,沿程配置修改,添加预警和推送页面

This commit is contained in:
扈兆增 2026-07-07 13:48:20 +08:00
parent 46100d8318
commit 549849b479
25 changed files with 4006 additions and 110 deletions

View File

@ -7,7 +7,7 @@ VITE_APP_TITLE = '水电水利建设项目全过程环境管理信息平台'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = '/dev-api'
# 本地环境
VITE_APP_BASE_URL = 'http://10.84.121.127:8093'
VITE_APP_BASE_URL = 'http://10.84.111.235:8093'
# 测试环境
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'

View File

@ -12,12 +12,9 @@ VITE_APP_BASE_API = '/dev-api'
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'
# 汤伟
VITE_APP_BASE_URL = 'http://10.84.121.127:8093'
# VITE_APP_BASE_URL = 'http://10.84.111.235:8093'
# 李林
# VITE_APP_BASE_URL = 'http://10.84.121.43:8093'
# VITE_APP_BASE_URL = 'http://192.168.1.162:8093'
# 李林
# VITE_APP_BASE_URL = 'http://10.84.121.43:8093'
VITE_APP_BASE_URL = 'http://10.84.111.25:8093'
# 测试环境线上10.84.121.122:
VITE_APP_TEST_ONLINE_URL = 'https://211.99.26.225:12122'

View File

@ -106,3 +106,11 @@ export function uploadFile(data: any) {
headers: { 'Content-Type': 'multipart/form-data' }
});
}
// 所在位置下拉列表
export function getAddvcdDropdownByRvcd(params: any) {
return request({
url: '/base/msalongb/noAuth/GetKendoList',
method: 'post',
params
});
}

View File

@ -3,7 +3,7 @@ import request from '@/utils/request';
// 获取所有沿程配置数据
export function getAllConfigTree(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/msalongb/GetKendoList',
url: '/base/msalongb/GetKendoList',
method: 'post',
data: data
});
@ -11,7 +11,7 @@ export function getAllConfigTree(data: any) {
// 保存沿程配置
export function saveBaseWbsb(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/wbsb/addOrUpdate',
url: '/base/msalongb/save',
method: 'post',
data: data
});
@ -19,8 +19,24 @@ export function saveBaseWbsb(data: any) {
// 删除沿程配置
export function deleteBaseWbsb(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/wbsb/delete',
method: 'get',
params: data
url: '/base/msalongb/delete',
method: 'post',
data
});
}
// 保存沿程配置子节点
export function saveBaseWbsbChild(data: any) {
return request({
url: '/base/msalongb/save',
method: 'post',
data: data
});
}
export function saveBaseWbsbChildDetail(data: any) {
return request({
url: '/base/msalongdetb/save',
method: 'post',
data: data
});
}

View File

@ -26,7 +26,7 @@ export function overviewGetOverviewSecond(data: any) {
data
});
}
//增殖站介绍 /api/dec-lygk-base-server/base/msstbprpt/GetKendoList
//增殖站介绍
export function msstbprptGetKendoList(data: any) {
return request({
url: '/fb/msstbprpt/GetKendoList',

View File

@ -56,6 +56,9 @@ service.interceptors.response.use(
if (status === 200) {
if (response.data.code == 401) {
message.error(response.data.msg || '请求失败');
setTimeout(() => {
router.push('/login');
}, 1000);
return;
} else if (response.data.code == 1) {
message.error(response.data.msg);

View File

@ -0,0 +1,784 @@
<template>
<a-modal
:title="isEdit ? '编辑沿程配置' : '新增沿程配置'"
v-model:open="modalVisible"
:confirm-loading="localLoading"
width="1200px"
:destroy-on-close="true"
ok-text="保存"
@cancel="handleCancel"
@ok="handleOk"
>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
name="edit_form"
:labelCol="{ span: 5 }"
>
<a-row>
<a-col :span="12">
<a-form-item label="沿程配置名称" name="name">
<a-input
v-model:value="formData.name"
style="width: 100%"
placeholder="请输入沿程配置名称"
/> </a-form-item
></a-col>
<a-col :span="12">
<a-form-item label="配置编码" name="code">
<a-input
v-model:value="formData.code"
style="width: 100%"
placeholder="请输入配置编码"
/> </a-form-item
></a-col>
</a-row>
<a-row>
<a-col :span="12">
<a-form-item label="所在河段" name="rvnm">
<a-input
v-model:value="formData.rvnm"
style="width: 100%"
placeholder="请输入所在河段"
/> </a-form-item
></a-col>
<a-col :span="12">
<a-form-item label="备注" name="description">
<a-textarea
:rows="4"
v-model:value="formData.description"
style="width: 100%"
placeholder="请输入备注"
/> </a-form-item
></a-col>
</a-row>
</a-form>
<div class="config-tree-container">
<!-- 新增一级节点区域 -->
<div v-if="addingNewNode" class="add-node-row">
<a-select
v-model:value="newNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleNodeSelect"
>
<a-select-option
v-for="item in stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click="handleSaveNewNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button @click="handleCancelAdd" style="margin-left: 8px">
<MinusOutlined />
</a-button>
</div>
<a-button v-else class="mb-4" type="primary" @click="handleAdd">
<PlusOutlined /> 新增一级节点
</a-button>
<!-- 树形结构 -->
<a-tree
v-if="treeData.length > 0"
:tree-data="treeData"
:default-expand-all="false"
:selectable="false"
block-node
style="width: 300px"
>
<template #title="{ dataRef }">
<!-- 编辑状态只显示编辑区域 -->
<div
v-if="editingNode && editingNodeKey === dataRef.key"
class="add-child-node-row w-[300px]"
>
<a-select
v-model:value="editNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleEditNodeSelect"
>
<a-select-option
v-for="item in stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click.stop="handleSaveEditNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button
@click.stop="handleCancelEditNode"
style="margin-left: 8px"
>
<MinusOutlined />
</a-button>
</div>
<!-- 非编辑状态显示正常节点内容 -->
<template v-else>
<div class="tree-node-wrapper">
<span class="tree-node-title"> {{ getNodeTitle(dataRef) }} </span>
<div class="tree-node-actions">
<!-- 一级节点支持新增子节点编辑排序删除 -->
<template v-if="!dataRef.parentId">
<a-button
type="link"
size="small"
@click.stop="handleAddChild(dataRef)"
title="添加子节点"
>
<PlusOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleEditNode(dataRef)"
title="编辑"
>
<EditOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveUp(dataRef)"
title="上移"
:disabled="isFirstNode(dataRef)"
>
<ArrowUpOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveDown(dataRef)"
title="下移"
:disabled="isLastNode(dataRef)"
>
<ArrowDownOutlined />
</a-button>
<a-button
type="link"
size="small"
danger
@click.stop="handleDeleteNode(dataRef)"
title="删除"
>
<DeleteOutlined />
</a-button>
</template>
<!-- 二级节点只支持编辑排序删除 -->
<template v-else>
<a-button
type="link"
size="small"
@click.stop="handleEditNode(dataRef)"
title="编辑"
>
<EditOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveUp(dataRef)"
title="上移"
:disabled="isFirstNode(dataRef)"
>
<ArrowUpOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveDown(dataRef)"
title="下移"
:disabled="isLastNode(dataRef)"
>
<ArrowDownOutlined />
</a-button>
<a-button
type="link"
size="small"
danger
@click.stop="handleDeleteNode(dataRef)"
title="删除"
>
<DeleteOutlined />
</a-button>
</template>
</div>
</div>
<!-- 子节点添加区域 -->
<div
v-if="addingChildNode && currentParentKey === dataRef.key"
class="add-child-node-row"
>
<a-select
v-model:value="childNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleChildNodeSelect"
>
<a-select-option
v-for="item in stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click.stop="handleSaveChildNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button
@click.stop="handleCancelAddChild"
style="margin-left: 8px"
>
<MinusOutlined />
</a-button>
</div>
</template>
</template>
</a-tree>
<a-empty v-else description="暂无数据" style="margin-top: 20px" />
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue';
import { message, Modal } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/es/form';
import dayjs from 'dayjs';
import {
PlusOutlined,
MinusOutlined,
EditOutlined,
DeleteOutlined,
ArrowUpOutlined,
ArrowDownOutlined
} from '@ant-design/icons-vue';
interface Props {
visible: boolean;
initialValues?: any | null;
loading?: boolean;
}
const localLoading = ref(false);
const props = withDefaults(defineProps<Props>(), {
visible: false,
initialValues: null,
loading: false
});
const modalVisible = computed({
get: () => props.visible,
set: val => emit('update:visible', val)
});
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
(e: 'ok', values: any): void;
}>();
const formRef = ref();
const defaultFormData = reactive({
id: undefined,
name: undefined,
code: undefined,
rvnm: undefined,
description: undefined
});
const formData: any = reactive({ ...defaultFormData });
const rules: Record<string, Rule[]> = {
name: [{ required: true, message: '请输入沿程配置名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入配置编码', trigger: 'blur' }],
rvnm: [{ required: true, message: '请输入所在河段', trigger: 'blur' }]
};
const isEdit = computed(() => !!props.initialValues);
//
const treeData = ref<any[]>([]);
//
const addingNewNode = ref(false);
const newNodeData = ref({
stcd: undefined,
stnm: '',
sttpName: ''
});
//
const addingChildNode = ref(false);
const currentParentKey = ref<string | null>(null);
const childNodeData = ref({
stcd: undefined,
stnm: '',
sttpName: ''
});
//
const editingNode = ref(false);
const editingNodeKey = ref<string | null>(null);
const editNodeData = ref({
stcd: undefined,
stnm: '',
sttpName: '',
originalData: null as any
});
//
const stationList = ref([
{ stcd: '1', stnm: '站点A', sttpName: '类型1' },
{ stcd: '2', stnm: '站点B', sttpName: '类型2' },
{ stcd: '3', stnm: '站点C', sttpName: '类型1' },
{ stcd: '4', stnm: '站点D', sttpName: '类型3' }
]);
// ID
let nodeIdCounter = 0;
const generateNodeId = () => `node_${Date.now()}_${++nodeIdCounter}`;
//
const getNodeTitle = (dataRef: any) => {
if (!dataRef.parentId) {
// stnm
return dataRef.stnm || '';
} else {
// stnm + sttpName
return `${dataRef.stnm || ''} ${dataRef.sttpName || ''}`;
}
};
//
const filterOption = (input: string, option: any) => {
return (
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
);
};
//
const isFirstNode = (dataRef: any) => {
const siblings = getSiblings(dataRef);
return siblings.length > 0 && siblings[0].key === dataRef.key;
};
//
const isLastNode = (dataRef: any) => {
const siblings = getSiblings(dataRef);
return (
siblings.length > 0 && siblings[siblings.length - 1].key === dataRef.key
);
};
//
const getSiblings = (dataRef: any) => {
if (!dataRef.parentId) {
//
return treeData.value;
} else {
// children
const parent = findNodeByKey(treeData.value, dataRef.parentId);
return parent?.children || [];
}
};
// key
const findNodeByKey = (nodes: any[], key: string): any => {
for (const node of nodes) {
if (node.key === key) {
return node;
}
if (node.children && node.children.length > 0) {
const found = findNodeByKey(node.children, key);
if (found) return found;
}
}
return null;
};
//
const handleAdd = () => {
addingNewNode.value = true;
newNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: ''
};
};
//
const handleNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
if (station) {
newNodeData.value.stnm = station.stnm;
newNodeData.value.sttpName = station.sttpName;
}
};
//
const handleSaveNewNode = () => {
if (!newNodeData.value.stcd) {
message.error('请选择站点');
return;
}
const newNode = {
key: generateNodeId(),
stcd: newNodeData.value.stcd,
stnm: newNodeData.value.stnm,
sttpName: newNodeData.value.sttpName,
parentId: null,
children: []
};
treeData.value.push(newNode);
addingNewNode.value = false;
message.success('添加成功');
};
//
const handleCancelAdd = () => {
addingNewNode.value = false;
newNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: ''
};
};
//
const handleAddChild = (parentData: any) => {
addingChildNode.value = true;
currentParentKey.value = parentData.key;
childNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: ''
};
};
//
const handleChildNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
if (station) {
childNodeData.value.stnm = station.stnm;
childNodeData.value.sttpName = station.sttpName;
}
};
//
const handleSaveChildNode = () => {
if (!childNodeData.value.stcd) {
message.error('请选择站点');
return;
}
const parent = findNodeByKey(treeData.value, currentParentKey.value!);
if (!parent) {
message.error('父节点不存在');
return;
}
const newChild = {
key: generateNodeId(),
stcd: childNodeData.value.stcd,
stnm: childNodeData.value.stnm,
sttpName: childNodeData.value.sttpName,
parentId: parent.key,
children: []
};
if (!parent.children) {
parent.children = [];
}
//
parent.children.unshift(newChild);
addingChildNode.value = false;
currentParentKey.value = null;
message.success('添加子节点成功');
};
//
const handleCancelAddChild = () => {
addingChildNode.value = false;
currentParentKey.value = null;
childNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: ''
};
};
//
const handleEditNode = (dataRef: any) => {
editingNode.value = true;
editingNodeKey.value = dataRef.key;
//
editNodeData.value = {
stcd: dataRef.stcd,
stnm: dataRef.stnm,
sttpName: dataRef.sttpName,
originalData: { ...dataRef }
};
};
//
const handleEditNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
if (station) {
editNodeData.value.stnm = station.stnm;
editNodeData.value.sttpName = station.sttpName;
editNodeData.value.stcd = station.stcd;
}
};
//
const handleSaveEditNode = () => {
if (!editNodeData.value.stcd) {
message.error('请选择站点');
return;
}
//
const node = findNodeByKey(treeData.value, editingNodeKey.value!);
if (!node) {
message.error('节点不存在');
return;
}
//
node.stcd = editNodeData.value.stcd;
node.stnm = editNodeData.value.stnm;
node.sttpName = editNodeData.value.sttpName;
editingNode.value = false;
editingNodeKey.value = null;
message.success('编辑成功');
};
//
const handleCancelEditNode = () => {
editingNode.value = false;
editingNodeKey.value = null;
editNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: '',
originalData: null
};
};
//
const handleMoveUp = (dataRef: any) => {
const siblings = getSiblings(dataRef);
const index = siblings.findIndex(item => item.key === dataRef.key);
if (index > 0) {
//
[siblings[index - 1], siblings[index]] = [
siblings[index],
siblings[index - 1]
];
message.success('上移成功');
}
};
//
const handleMoveDown = (dataRef: any) => {
const siblings = getSiblings(dataRef);
const index = siblings.findIndex(item => item.key === dataRef.key);
if (index < siblings.length - 1) {
//
[siblings[index], siblings[index + 1]] = [
siblings[index + 1],
siblings[index]
];
message.success('下移成功');
}
};
//
const handleDeleteNode = (dataRef: any) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除节点"${dataRef.stnm}"吗?`,
okText: '确定',
cancelText: '取消',
onOk: () => {
if (!dataRef.parentId) {
//
const index = treeData.value.findIndex(
item => item.key === dataRef.key
);
if (index > -1) {
treeData.value.splice(index, 1);
}
} else {
//
const parent = findNodeByKey(treeData.value, dataRef.parentId);
if (parent && parent.children) {
const index = parent.children.findIndex(
item => item.key === dataRef.key
);
if (index > -1) {
parent.children.splice(index, 1);
}
}
}
message.success('删除成功');
}
});
};
const handleOk = async () => {
try {
await formRef.value.validate();
const submitValues = {
...formData,
captureDate: formData.captureDate
? dayjs(formData.captureDate).format('YYYY-MM-DD')
: undefined,
configNodes: treeData.value //
};
emit('ok', submitValues);
} catch (error) {
console.error('Validate Failed:', error);
message.error('请检查表单填写是否正确');
}
};
const initForm = () => {
if (props.initialValues) {
const values = props.initialValues;
Object.keys(formData).forEach(key => {
if (values.hasOwnProperty(key)) {
if (key === 'captureDate' && values[key]) {
formData[key] = dayjs(values[key]);
} else {
formData[key] = values[key];
}
}
});
//
if (values.configNodes && Array.isArray(values.configNodes)) {
treeData.value = values.configNodes.map((node: any) => ({
...node,
children:
node.children?.map((child: any) => ({
...child
})) || []
}));
} else {
treeData.value = [];
}
} else {
resetForm();
}
};
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
treeData.value = [];
addingNewNode.value = false;
};
const handleCancel = () => {
emit('update:visible', false);
emit('cancel');
resetForm();
};
watch(
() => props.visible,
newVisible => {
if (newVisible) {
initForm();
}
},
{ immediate: false }
);
defineExpose({
localLoading
});
</script>
<style scoped lang="scss">
.config-tree-container {
margin-top: 16px;
.add-node-row {
display: flex;
align-items: center;
margin-bottom: 16px;
}
}
.tree-node-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding-right: 8px;
.tree-node-title {
flex: 1;
}
.tree-node-actions {
opacity: 0;
transition: opacity 0.2s;
}
}
//
.tree-node-wrapper:hover {
.tree-node-actions {
opacity: 1;
}
}
//
.add-child-node-row {
display: flex;
align-items: center;
padding: 8px 0 8px 24px;
margin-top: 4px;
background-color: #f5f5f5;
border-radius: 4px;
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<div class="content-search">
<BasicSearch
ref="basicSearchRef"
:searchList="searchList"
:initial-values="initSearchData"
@reset="handleReset"
@finish="onSearchFinish"
@values-change="onValuesChange"
>
<template #actions>
<a-tooltip title="新增配置">
<a-button @click="props.handleAdd" type="primary"> 新增沿程配置 </a-button>
</a-tooltip>
</template>
</BasicSearch>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import BasicSearch from "@/components/BasicSearch/index.vue";
interface Props {
handleAdd: () => void;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "reset", values: any): void;
(e: "searchFinish", values: any): void;
}>();
const initSearchData = {
configName: "",
};
const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: "Input",
name: "name",
label: "沿程配置名称",
fieldProps: {
allowClear: true,
},
},
{
type: "Input",
name: "rvnm",
label: "所在河段",
fieldProps: {
allowClear: true,
},
},
]);
const onSearchFinish = (values: any) => {
emit("searchFinish", values);
};
const onValuesChange = (changedValues: any, allValues: any) => {
searchData.value = { ...searchData.value, ...allValues };
};
const handleReset = () => {
emit("reset", initSearchData);
};
onMounted(() => {
emit("searchFinish", initSearchData);
});
</script>
<style lang="scss"></style>

View File

@ -0,0 +1,228 @@
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\ConfigManagement\index.vue -->
<template>
<div class="content">
预警配置管理
<!-- <ConfigManagementSearch
ref="configManagementSearch"
:handle-add="handleAdd"
@reset="handleReset"
@search-finish="onSearchFinish"
/>
<BasicTable
ref="basicTable"
:columns="columns"
:list-url="getAllConfigTree"
:search-params="searchParams"
>
<template #action="{ column, record }">
<div class="flex gap-[6px]">
<a-button
class="!p-0"
type="link"
size="small"
@click="handleEdit(record)"
>编辑</a-button
>
<a-button
class="!p-0"
type="link"
size="small"
danger
@click="handleDelete(record)"
>删除</a-button
>
</div>
</template>
</BasicTable>
<ConfigManagementForm
ref="configManagementForm"
v-model:visible="editModalVisible"
:initial-values="currentRecord"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/> -->
</div>
</template>
<script setup lang="ts">
import { ref, nextTick, onMounted } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import ConfigManagementSearch from './ConfigManagementSearch.vue';
import ConfigManagementForm from './ConfigManagementForm.vue';
import { message, Modal } from 'ant-design-vue';
import {
getAllConfigTree,
deleteBaseWbsb,
saveBaseWbsb
} from '@/api/system/map/ConfigManagement';
//
const basicTable = ref<any>(null);
//
const columns = [
{
title: '序号',
dataIndex: 'index',
key: 'index',
width: 60,
align: 'center',
customRender: ({ text, record, index }) => index + 1
},
{
title: '沿程配置名称',
dataIndex: 'name',
key: 'name',
width: 150
},
{
title: '配置编码',
key: 'code',
dataIndex: 'code',
width: 120
},
{
title: '所在河段',
dataIndex: 'rvnm',
key: 'rvnm',
ellipsis: true
},
{
title: '创建人',
key: 'recordUser',
dataIndex: 'recordUser'
},
{
title: '创建时间',
key: 'recordTime',
dataIndex: 'recordTime',
width: 150
},
{
title: '更新时间',
key: 'modifyTime',
dataIndex: 'modifyTime',
width: 150
},
{
title: '备注',
dataIndex: 'remark',
key: 'remark',
width: 360
},
{
title: '操作',
key: 'action',
width: 120,
fixed: 'right',
slots: { customRender: 'action' }
}
];
//
const searchParams = ref({});
//
const currentRecord = ref<any | null>(null);
const editModalVisible = ref(false);
//
const handleAdd = () => {
currentRecord.value = null;
editModalVisible.value = true;
};
//
const handleEdit = (record: any) => {
currentRecord.value = { ...record };
editModalVisible.value = true;
};
//
const handleDelete = (record: any) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除选中的记录吗?',
zIndex: 2002,
onOk: async () => {
try {
let res = await deleteBaseWbsb([record.id]);
message.success('删除成功');
basicTable.value.refresh();
} catch (error) {
message.error('删除失败');
return;
}
}
});
};
//
const onSearchFinish = (values: any) => {
const params = {
logic: 'and',
filters: [
values.parentId
? {
field: 'parentId',
operator: 'eq',
dataType: 'string',
value: values.parentId
}
: null,
values.name
? {
field: 'name',
operator: 'contains',
dataType: 'string',
value: values.name
}
: null
].filter(Boolean)
};
basicTable.value.getList(params);
};
//
const handleReset = () => {
basicTable.value.getList({
logic: 'and',
filters: []
});
};
//
const editModalCancel = () => {
currentRecord.value = null;
editModalVisible.value = false;
};
//
const handleEditSubmit = async (values: any) => {
try {
let res = await saveBaseWbsb({
...currentRecord.value,
...values
});
message.success(`保存成功`);
editModalVisible.value = false;
basicTable.value.refresh();
} catch (error) {
message.error(`保存失败`);
editModalVisible.value = false;
return;
}
};
const initOption = () => {};
onMounted(() => {
initOption();
});
</script>
<style scoped lang="scss">
.config-management {
height: 100%;
padding: 16px;
}
</style>

View File

@ -0,0 +1,260 @@
<template>
<a-modal
:title="isEdit ? '编辑图层' : '新增图层'"
v-model:open="modalVisible"
:confirm-loading="props.loading"
width="600px"
:destroy-on-close="true"
@cancel="handleCancel"
@ok="handleOk"
>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
name="edit_form"
:labelCol="{ span: 5 }"
>
<a-form-item label="图层名称" name="title">
<a-input
v-model:value="formData.title"
style="width: 100%"
placeholder="请输入图层名称"
/>
</a-form-item>
<a-form-item label="图层编码" name="code">
<a-input
v-model:value="formData.code"
style="width: 100%"
placeholder="请输入图层编码"
/>
</a-form-item>
<a-form-item label="图层类型" name="type" v-if="isInterface">
<a-select
:options="typeOpiton"
v-model:value="formData.type"
:fieldNames="{
label: 'dictName',
value: 'itemCode'
}"
style="width: 100%"
placeholder="请输入图层类型"
/>
</a-form-item>
<a-form-item label="标签显示级别" name="treeLevel" v-if="!isInterface">
<a-input-number
v-model:value="formData.treeLevel"
style="width: 100%"
placeholder="请输入标签显示级别"
:min="0"
:max="24"
:step="1"
/>
</a-form-item>
<a-form-item label="锚点接口" name="url" v-if="isInterface">
<a-input
v-model:value="formData.url"
style="width: 100%"
placeholder="请输入锚点接口"
/>
</a-form-item>
<a-form-item label="接口参数" name="anchorParamJson" v-if="isInterface">
<a-textarea
:rows="6"
v-model:value="formData.anchorParamJson"
style="width: 100%"
placeholder="请输入接口参数"
/>
</a-form-item>
<a-form-item
label="三维接口"
name="urlThd"
v-if="isInterface && formData.type === 'geojson'"
>
<a-input
v-model:value="formData.urlThd"
style="width: 100%"
placeholder="请输入三维接口"
/>
</a-form-item>
<a-form-item
label="gisJson"
name="paramJson"
v-if="isInterface && formData.type === 'geojson'"
>
<a-textarea
:rows="4"
v-model:value="formData.paramJson"
style="width: 100%"
placeholder="请输入gisJson"
/>
</a-form-item>
<a-form-item
label="备注"
name="description"
v-if="!isInterface || formData.type === 'geojson'"
>
<a-textarea
:rows="4"
v-model:value="formData.description"
style="width: 100%"
placeholder="请输入备注"
/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue';
import { message } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/es/form';
import { getDictItemsByCode } from '@/api/dict';
// Props
interface Props {
visible: boolean;
initialValues?: any | null;
loading?: boolean;
isInterface?: boolean;
}
//
const localLoading = ref(false);
const props = withDefaults(defineProps<Props>(), {
visible: false,
initialValues: null,
loading: false,
isInterface: false
});
const modalVisible = computed({
get: () => props.visible,
set: val => emit('update:visible', val)
});
// Emits
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
(e: 'ok', values: any, type: string): void;
}>();
//
const formRef = ref();
// 1.
const bodyLengthError = ref<string>('');
const weightError = ref<string>('');
//
const defaultFormData = reactive({
id: undefined,
title: undefined,
code: undefined,
type: undefined,
treeLevel: undefined,
description: undefined,
parentId: undefined,
url: undefined,
urlThd: undefined,
paramJson: undefined,
anchorParamJson: undefined
});
const typeOpiton = ref<any>([]);
const formData: any = reactive({ ...defaultFormData });
//
const rules: Record<string, Rule[]> = {
title: [{ required: true, message: '请输入图层名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入图层编码', trigger: 'blur' }],
type: [{ required: true, message: '请输入图层类型', trigger: 'change' }],
treeLevel: [
{ required: true, message: '请输入标签显示级别', trigger: 'blur' }
]
};
//
const isEdit = computed(() => !!props.initialValues);
//
const handleOk = async () => {
try {
//
await formRef.value.validate();
//
const submitValues = {
...formData
};
emit('ok', submitValues, 'form');
} catch (error) {
console.error('Validate Failed:', error);
message.error('请检查表单填写是否正确');
}
};
const initForm = () => {
if (props.initialValues) {
// --- ---
const values = props.initialValues;
if (props.isInterface) {
initType();
}
//
Object.keys(formData).forEach(key => {
// initialValues
if (values.hasOwnProperty(key)) {
formData[key] = values[key];
}
});
} else {
resetForm();
}
};
const initType = () => {
getDictItemsByCode({ dictCode: 'LayerTypeCode' }).then(res => {
typeOpiton.value = res.data;
});
};
//
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
//
bodyLengthError.value = '';
weightError.value = '';
};
//
const handleCancel = () => {
emit('update:visible', false);
emit('cancel');
resetForm();
};
watch(
() => props.visible,
newVisible => {
if (newVisible) {
initForm();
}
},
{ immediate: false }
);
//
watch(
() => formData.type,
(newType, oldType) => {
if (oldType && newType !== oldType) {
//
formData.urlThd = undefined;
formData.paramJson = undefined;
if (oldType === 'geojson') {
formData.description = undefined;
}
}
}
);
//
defineExpose({
localLoading
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,69 @@
<template>
<div class="content-search">
<BasicSearch
ref="basicSearchRef"
:searchList="searchList"
:initial-values="initSearchData"
@reset="handleReset"
@finish="onSearchFinish"
@values-change="onValuesChange"
>
<template #actions>
<a-tooltip title="新增图层">
<a-button @click="props.handleAdd" type="primary"> 新增图层 </a-button>
</a-tooltip>
</template>
</BasicSearch>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, watch } from "vue";
import BasicSearch from "@/components/BasicSearch/index.vue"; //
interface Props {
handleAdd: (record: any) => void;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "reset", values: any): void;
(e: "searchFinish", values: any): void;
}>();
const initSearchData = {
title: "",
};
const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: "Input",
name: "title",
label: "图层名称",
fieldProps: {
allowClear: true,
},
},
]);
// --- Methods ---
// 2.
const onSearchFinish = (values: any) => {
emit("searchFinish", values);
};
//
const onValuesChange = (changedValues: any, allValues: any) => {
searchData.value = { ...searchData.value, ...allValues };
};
//
const handleReset = () => {
emit("reset", initSearchData);
};
onMounted(() => {
emit("searchFinish", initSearchData);
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,358 @@
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\LayerManagement\index.vue -->
<template>
<div class="content">
预警规则
<!-- <LayerManagementSearch
ref="layerManagementSearch"
:handle-add="handleAdd"
@reset="handleReset"
@search-finish="onSearchFinish"
/>
<BasicTable
ref="basicTable"
:columns="columns"
:list-url="getAllMapLayerTree"
:search-params="searchParams"
>
<template #action="{ record }">
<div class="flex gap-[6px]">
<a-button
class="!p-0"
type="link"
size="small"
@click="handleEdit(record)"
>编辑</a-button
>
<a-button
v-if="record.children && record.children.length == 0"
class="!p-0"
type="link"
size="small"
@click="handleEditInterface(record)"
>编辑接口</a-button
>
<a-button
class="!p-0"
type="link"
size="small"
@click="handleAdd(record)"
>新增子图层</a-button
>
<a-button
class="!p-0"
type="link"
size="small"
danger
@click="handleDelete(record)"
>删除</a-button
>
</div>
</template>
<template #enable="{ record }">
<a-switch
:checked="record.enable === 1"
@change="(checked: boolean) => handleEnableChange(record, checked)"
/>
</template>
<template #checkable="{ record }">
<a-switch
:checked="record.checkable === 1"
@change="(checked: boolean) => handleCheckableChange(record, checked)"
/>
</template>
</BasicTable>
<LayerManagementForm
ref="layerManagementForm"
v-model:visible="editModalVisible"
:initial-values="currentRecord"
:is-interface="isInterface"
:loading="editLoading"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/> -->
</div>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import LayerManagementSearch from './LayerManagementSearch.vue';
import LayerManagementForm from './LayerManagementForm.vue';
import { message, Modal } from 'ant-design-vue';
import {
getAllMapLayerTree,
saveMapLayer,
deleteMapLayer
} from '@/api/system/map/LayerManagement';
//
const columns = [
{
title: '',
dataIndex: '',
key: '',
width: 50,
customRender: () => {
return '';
}
},
{
title: '序号',
key: 'index',
width: 60,
align: 'center',
customRender: ({ record, index }: any) => {
// children
// parentId
if (!record.parentId) {
// dataSource
let rootIndex = 0;
for (let i = 0; i < basicTable.value.tableData.length; i++) {
const item = basicTable.value.tableData[i];
// parentId
if (!item.parentId) {
rootIndex++;
//
if (
item.id === record.id ||
(item.key === record.key && item.id === record.id)
) {
return rootIndex;
}
}
}
return rootIndex;
} else {
// children
const findParentAndIndex = (data: any[], parentId: string): number => {
for (const item of data) {
if (item.key === parentId || item.id === parentId) {
// children
if (item.children && item.children.length > 0) {
const childIndex = item.children.findIndex(
(child: any) =>
child.key === record.key || child.id === record.id
);
return childIndex + 1;
}
}
//
if (item.children && item.children.length > 0) {
const result = findParentAndIndex(item.children, parentId);
if (result > 0) {
return result;
}
}
}
return 0;
};
return findParentAndIndex(basicTable.value.tableData, record.parentId);
}
}
},
{
title: '图层名称',
dataIndex: 'title',
key: 'title',
width: 150
},
{
title: '图层编码',
dataIndex: 'code',
key: 'code',
width: 120
},
{
title: '标签显示级别',
dataIndex: 'treeLevel',
key: 'treeLevel',
width: 100
},
{
title: '是否启用',
dataIndex: 'enable',
key: 'enable',
width: 80,
slots: { customRender: 'enable' }
},
{
title: '是否可选',
dataIndex: 'checkable',
key: 'checkable',
width: 80,
slots: { customRender: 'checkable' }
},
{
title: '备注',
dataIndex: 'description',
key: 'description'
},
{
title: '创建人',
key: 'displayRecordUser',
dataIndex: 'displayRecordUser'
},
{
title: '创建时间',
key: 'recordTime',
dataIndex: 'recordTime',
width: 150
},
{
title: '更新时间',
key: 'modifyTime',
dataIndex: 'modifyTime',
width: 150
},
{
title: '操作',
key: 'action',
width: 220,
fixed: 'right',
slots: { customRender: 'action' }
}
];
//
const basicTable = ref<any>(null);
//
const searchParams = ref({});
//
const isInterface = ref(false);
const editModalVisible = ref(false);
const editLoading = ref(false);
const parentId = ref<string | null>(null);
const currentRecord = ref<any | null>(null);
//
const handleAdd = (record: any) => {
currentRecord.value = null;
parentId.value = record.id;
editModalVisible.value = true;
};
// '-'
const filterRecord = (record: any) => {
const filtered: any = {};
const keepKeys = ['code']; //
Object.keys(record).forEach(key => {
if (keepKeys.includes(key) || record[key] !== '-') {
filtered[key] = record[key];
}
});
return filtered;
};
//
const handleEdit = (record: any) => {
console.log('handleEdit', record);
parentId.value = null;
isInterface.value = false;
currentRecord.value = filterRecord(record);
editModalVisible.value = true;
};
//
const handleEditInterface = (record: any) => {
console.log('handleEdit', record);
currentRecord.value = filterRecord(record);
console.log('handleEdit', currentRecord.value);
isInterface.value = true;
parentId.value = null;
editModalVisible.value = true;
};
//
const handleDelete = async (record: any) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除选中的记录吗?',
zIndex: 2002,
onOk: async () => {
try {
let res = await deleteMapLayer([record.id]);
message.success('删除成功');
basicTable.value.refresh();
} catch (error) {
message.error('删除失败');
return;
}
}
});
};
//
const onSearchFinish = (values: any) => {
searchParams.value = values;
nextTick(() => {
basicTable.value.getList();
});
};
//
const handleReset = () => {
searchParams.value = {};
nextTick(() => {
basicTable.value.refresh();
});
};
//
const handleEnableChange = (record: any, checked: boolean) => {
currentRecord.value = filterRecord(record);
console.log(currentRecord.value);
record.enable = checked ? 1 : 0;
handleEditSubmit({ enable: checked ? 1 : 0 }, 'switch');
};
//
const handleCheckableChange = (record: any, checked: boolean) => {
currentRecord.value = filterRecord(record);
record.checkable = checked ? 1 : 0;
handleEditSubmit({ checkable: checked ? 1 : 0 }, 'switch');
};
//
const editModalCancel = () => {
isInterface.value = false;
parentId.value = null;
currentRecord.value = null;
editModalVisible.value = false;
};
const handleEditSubmit = async (values: any, type: string) => {
editLoading.value = true;
try {
let res = await saveMapLayer({
...currentRecord.value,
...values,
parentId: parentId.value
});
if (res.code == 0) {
if (type === 'switch') {
message.success('修改状态成功');
} else {
message.success(`保存成功`);
}
editModalVisible.value = false;
basicTable.value.refresh();
} else {
if (type === 'switch') {
message.success('修改状态成功');
} else {
message.success(`保存成功`);
}
}
} catch (error) {
if (type === 'switch') {
message.error('修改状态失败');
} else {
message.error(`保存失败`);
}
} finally {
editLoading.value = false;
}
};
</script>
<style scoped lang="scss">
:deep(.ant-table-cell-with-append) {
display: flex;
width: 100%;
}
:deep(.ant-table-row-expand-icon) {
width: 17px !important;
margin: 0 !important;
}
</style>

View File

@ -0,0 +1,89 @@
<template>
<div class="map-page">
<!-- Ant Design Tabs -->
<a-tabs v-model:activeKey="activeTab" class="map-tabs">
<a-tab-pane v-for="tab in tabs" :key="tab.value" :tab="tab.name">
<!-- 动态加载对应的组件 -->
<component :is="getComponent(tab.value)" />
</a-tab-pane>
</a-tabs>
</div>
</template>
<script setup lang="ts">
import { ref, defineAsyncComponent } from 'vue';
// Tab
const tabs = [
{
name: '预警规则',
value: 'rules'
},
{
name: '预警配置管理',
value: 'config'
}
];
// tab
const activeTab = ref('rules');
//
const getComponent = (value: string) => {
const componentMap: Record<string, any> = {
rules: defineAsyncComponent(
() => import('./components/RulesManagement/index.vue')
),
config: defineAsyncComponent(
() => import('./components/ConfigManagement/index.vue')
)
};
return componentMap[value] || null;
};
</script>
<style scoped lang="scss">
.map-page {
position: relative;
z-index: 900;
pointer-events: all;
width: 100%;
height: 100%;
padding-top: 2px;
background-color: #ffffff;
box-sizing: border-box;
overflow: hidden;
.content {
position: relative;
z-index: 900;
pointer-events: all;
width: 100%;
height: 100%;
background-color: #ffffff;
display: flex;
flex-direction: column;
gap: 10px;
box-sizing: border-box;
overflow: hidden;
}
:deep(.ant-form-item) {
margin-bottom: 0 !important;
}
}
:deep(.ant-tabs) {
height: 100%; // tabs header
padding: 0 13px;
}
.map-tabs {
:deep(.ant-tabs-content) {
height: 100%; // tabs header
overflow: auto;
}
:deep(.ant-tabs-tabpane) {
height: 100%;
}
}
</style>

View File

@ -26,32 +26,40 @@
/> </a-form-item
></a-col>
<a-col :span="12">
<a-form-item label="配置编码" name="code">
<a-form-item label="沿程配置编码" name="code">
<a-input
v-model:value="formData.code"
style="width: 100%"
placeholder="请输入配置编码"
/> </a-form-item
></a-col>
placeholder="请输入沿程配置编码"
/>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="12">
<a-form-item label="所在河段" name="rvnm">
<a-input
v-model:value="formData.rvnm"
<a-form-item label="所在河段" name="rvcd">
<a-select
v-model:value="formData.rvcd"
show-search
style="width: 100%"
placeholder="请输入所在河段"
/> </a-form-item
></a-col>
placeholder="请选择所在河段"
:options="riverOptions"
:filter-option="filterOption"
@change="handleRiverSelect"
:disabled="isEdit"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="备注" name="description">
<a-form-item label="备注" name="remark">
<a-textarea
:rows="4"
v-model:value="formData.description"
v-model:value="formData.remark"
style="width: 100%"
placeholder="请输入备注"
/> </a-form-item
></a-col>
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
<div class="config-tree-container">
@ -70,7 +78,7 @@
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
{{ item.ennm }}
</a-select-option>
</a-select>
<a-button
@ -109,14 +117,16 @@
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleEditNodeSelect"
@change="() => handleEditNodeSelect(dataRef)"
>
<a-select-option
v-for="item in stationList"
v-for="item in dataRef.parentId
? childStationList
: stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
{{ dataRef.parentId ? item.stnm : item.ennm }}
</a-select-option>
</a-select>
<a-button
@ -238,7 +248,7 @@
@change="handleChildNodeSelect"
>
<a-select-option
v-for="item in stationList"
v-for="item in childStationList"
:key="item.stcd"
:value="item.stcd"
>
@ -280,18 +290,21 @@ import {
ArrowUpOutlined,
ArrowDownOutlined
} from '@ant-design/icons-vue';
import { getEngInfoDropdown, getAddvcdDropdownByRvcd } from '@/api/select';
interface Props {
visible: boolean;
initialValues?: any | null;
loading?: boolean;
riverOptions?: any[];
}
const localLoading = ref(false);
const props = withDefaults(defineProps<Props>(), {
visible: false,
initialValues: null,
loading: false
loading: false,
riverOptions: () => []
});
const modalVisible = computed({
@ -311,15 +324,20 @@ const defaultFormData = reactive({
id: undefined,
name: undefined,
code: undefined,
rvnm: undefined,
description: undefined
rvcd: undefined,
remark: undefined
});
const formData: any = reactive({ ...defaultFormData });
const rules: Record<string, Rule[]> = {
name: [{ required: true, message: '请输入沿程配置名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入配置编码', trigger: 'blur' }],
rvnm: [{ required: true, message: '请输入所在河段', trigger: 'blur' }]
rvcd: [{ required: true, message: '请选择所在河段', trigger: 'change' }]
};
const handleRiverSelect = async (value: string) => {
const res = await getEngInfoDropdown({ rvcd: value });
stationList.value = res.data || [];
};
const isEdit = computed(() => !!props.initialValues);
@ -331,7 +349,7 @@ const treeData = ref<any[]>([]);
const addingNewNode = ref(false);
const newNodeData = ref({
stcd: undefined,
stnm: '',
ennm: '',
sttpName: ''
});
@ -340,7 +358,7 @@ const addingChildNode = ref(false);
const currentParentKey = ref<string | null>(null);
const childNodeData = ref({
stcd: undefined,
stnm: '',
ennm: '',
sttpName: ''
});
@ -349,18 +367,13 @@ const editingNode = ref(false);
const editingNodeKey = ref<string | null>(null);
const editNodeData = ref({
stcd: undefined,
stnm: '',
ennm: '',
sttpName: '',
originalData: null as any
});
//
const stationList = ref([
{ stcd: '1', stnm: '站点A', sttpName: '类型1' },
{ stcd: '2', stnm: '站点B', sttpName: '类型2' },
{ stcd: '3', stnm: '站点C', sttpName: '类型1' },
{ stcd: '4', stnm: '站点D', sttpName: '类型3' }
]);
const stationList = ref([]);
const childStationList = ref([]);
// ID
let nodeIdCounter = 0;
@ -369,19 +382,19 @@ const generateNodeId = () => `node_${Date.now()}_${++nodeIdCounter}`;
//
const getNodeTitle = (dataRef: any) => {
if (!dataRef.parentId) {
// stnm
return dataRef.stnm || '';
return dataRef.ennm || '';
} else {
// stnm + sttpName
return `${dataRef.stnm || ''} ${dataRef.sttpName || ''}`;
return `${dataRef.ennm || ''} ${dataRef.sttpName || ''}`;
}
};
//
const filterOption = (input: string, option: any) => {
return (
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
);
const filterOption = (inputValue: string, option: any) => {
const label = option.label || option.name || '';
const reachcdName = option.reachcdName || '';
const keyword = inputValue || '';
return label.indexOf(keyword) !== -1 || reachcdName.indexOf(keyword) !== -1;
};
//
@ -426,10 +439,14 @@ const findNodeByKey = (nodes: any[], key: string): any => {
//
const handleAdd = () => {
if (!formData.rvcd) {
message.warning('请先选择所在河段');
return;
}
addingNewNode.value = true;
newNodeData.value = {
stcd: undefined,
stnm: '',
ennm: '',
sttpName: ''
};
};
@ -438,7 +455,7 @@ const handleAdd = () => {
const handleNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
if (station) {
newNodeData.value.stnm = station.stnm;
newNodeData.value.ennm = station.ennm;
newNodeData.value.sttpName = station.sttpName;
}
};
@ -453,7 +470,7 @@ const handleSaveNewNode = () => {
const newNode = {
key: generateNodeId(),
stcd: newNodeData.value.stcd,
stnm: newNodeData.value.stnm,
ennm: newNodeData.value.ennm,
sttpName: newNodeData.value.sttpName,
parentId: null,
children: []
@ -469,27 +486,29 @@ const handleCancelAdd = () => {
addingNewNode.value = false;
newNodeData.value = {
stcd: undefined,
stnm: '',
ennm: '',
sttpName: ''
};
};
//
const handleAddChild = (parentData: any) => {
const handleAddChild = async (parentData: any) => {
addingChildNode.value = true;
currentParentKey.value = parentData.key;
childNodeData.value = {
stcd: undefined,
stnm: '',
ennm: '',
sttpName: ''
};
const res = await getAddvcdDropdownByRvcd({ stcd: parentData.stcd });
childStationList.value = res.data || [];
};
//
const handleChildNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
const station = childStationList.value.find(item => item.stcd === value);
if (station) {
childNodeData.value.stnm = station.stnm;
childNodeData.value.ennm = station.stnm;
childNodeData.value.sttpName = station.sttpName;
}
};
@ -510,7 +529,7 @@ const handleSaveChildNode = () => {
const newChild = {
key: generateNodeId(),
stcd: childNodeData.value.stcd,
stnm: childNodeData.value.stnm,
ennm: childNodeData.value.ennm,
sttpName: childNodeData.value.sttpName,
parentId: parent.key,
children: []
@ -533,29 +552,38 @@ const handleCancelAddChild = () => {
currentParentKey.value = null;
childNodeData.value = {
stcd: undefined,
stnm: '',
ennm: '',
sttpName: ''
};
};
//
const handleEditNode = (dataRef: any) => {
const handleEditNode = async (dataRef: any) => {
editingNode.value = true;
editingNodeKey.value = dataRef.key;
//
editNodeData.value = {
stcd: dataRef.stcd,
stnm: dataRef.stnm,
ennm: dataRef.ennm,
sttpName: dataRef.sttpName,
originalData: { ...dataRef }
};
if (dataRef.parentId) {
const parent = findNodeByKey(treeData.value, dataRef.parentId);
if (parent) {
const res = await getAddvcdDropdownByRvcd({ stcd: parent.stcd });
childStationList.value = res.data || [];
}
}
};
//
const handleEditNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
const handleEditNodeSelect = (dataRef: any) => {
const value = editNodeData.value.stcd;
const list = dataRef.parentId ? childStationList.value : stationList.value;
const station = list.find(item => item.stcd === value);
if (station) {
editNodeData.value.stnm = station.stnm;
editNodeData.value.ennm = dataRef.parentId ? station.stnm : station.ennm;
editNodeData.value.sttpName = station.sttpName;
editNodeData.value.stcd = station.stcd;
}
@ -577,7 +605,7 @@ const handleSaveEditNode = () => {
//
node.stcd = editNodeData.value.stcd;
node.stnm = editNodeData.value.stnm;
node.ennm = editNodeData.value.ennm;
node.sttpName = editNodeData.value.sttpName;
editingNode.value = false;
@ -591,7 +619,7 @@ const handleCancelEditNode = () => {
editingNodeKey.value = null;
editNodeData.value = {
stcd: undefined,
stnm: '',
ennm: '',
sttpName: '',
originalData: null
};
@ -631,7 +659,7 @@ const handleMoveDown = (dataRef: any) => {
const handleDeleteNode = (dataRef: any) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除节点"${dataRef.stnm}"吗?`,
content: `确定要删除节点"${dataRef.ennm}"吗?`,
okText: '确定',
cancelText: '取消',
onOk: () => {
@ -677,7 +705,7 @@ const handleOk = async () => {
}
};
const initForm = () => {
const initForm = async () => {
if (props.initialValues) {
const values = props.initialValues;
Object.keys(formData).forEach(key => {
@ -702,6 +730,12 @@ const initForm = () => {
} else {
treeData.value = [];
}
//
if (formData.rvcd) {
const res = await getEngInfoDropdown({ rvcd: formData.rvcd });
stationList.value = res.data || [];
}
} else {
resetForm();
}

View File

@ -10,7 +10,9 @@
>
<template #actions>
<a-tooltip title="新增配置">
<a-button @click="props.handleAdd" type="primary"> 新增沿程配置 </a-button>
<a-button @click="props.handleAdd" type="primary">
新增沿程配置
</a-button>
</a-tooltip>
</template>
</BasicSearch>
@ -18,46 +20,52 @@
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import BasicSearch from "@/components/BasicSearch/index.vue";
import { ref, computed, onMounted } from 'vue';
import BasicSearch from '@/components/BasicSearch/index.vue';
interface Props {
handleAdd: () => void;
riverOptions: any[];
riverOptionsLoad: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "reset", values: any): void;
(e: "searchFinish", values: any): void;
(e: 'reset', values: any): void;
(e: 'searchFinish', values: any): void;
}>();
const initSearchData = {
configName: "",
name: null,
rvcd: null
};
const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: "Input",
name: "name",
label: "沿程配置名称",
type: 'Input',
name: 'name',
label: '沿程配置名称',
fieldProps: {
allowClear: true,
},
allowClear: true
}
},
{
type: "Input",
name: "rvnm",
label: "所在河段",
type: 'Select',
name: 'rvcd',
label: '所在河段',
width: 300,
fieldProps: {
allowClear: true,
allowClear: true
},
},
options: props.riverOptions,
loading: props.riverOptionsLoad
}
]);
const onSearchFinish = (values: any) => {
emit("searchFinish", values);
emit('searchFinish', values);
};
const onValuesChange = (changedValues: any, allValues: any) => {
@ -65,11 +73,11 @@ const onValuesChange = (changedValues: any, allValues: any) => {
};
const handleReset = () => {
emit("reset", initSearchData);
emit('reset', initSearchData);
};
onMounted(() => {
emit("searchFinish", initSearchData);
emit('searchFinish', initSearchData);
});
</script>
<style lang="scss"></style>

View File

@ -4,6 +4,8 @@
<ConfigManagementSearch
ref="configManagementSearch"
:handle-add="handleAdd"
:river-options="riverOptions"
:river-options-load="riverOptionsLoad"
@reset="handleReset"
@search-finish="onSearchFinish"
/>
@ -37,6 +39,8 @@
ref="configManagementForm"
v-model:visible="editModalVisible"
:initial-values="currentRecord"
:river-options="riverOptions"
:river-options-load="riverOptionsLoad"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/>
@ -52,8 +56,10 @@ import { message, Modal } from 'ant-design-vue';
import {
getAllConfigTree,
deleteBaseWbsb,
saveBaseWbsb
saveBaseWbsbChild,
saveBaseWbsbChildDetail
} from '@/api/system/map/ConfigManagement';
import { getRvcdDropdown } from '@/api/select';
//
const basicTable = ref<any>(null);
@ -129,9 +135,20 @@ const handleAdd = () => {
editModalVisible.value = true;
};
// '-'
const filterRecord = (record: any) => {
const filtered: any = {};
Object.keys(record).forEach(key => {
if (record[key] !== '-') {
filtered[key] = record[key];
}
});
return filtered;
};
//
const handleEdit = (record: any) => {
currentRecord.value = { ...record };
currentRecord.value = filterRecord(record);
editModalVisible.value = true;
};
@ -144,8 +161,12 @@ const handleDelete = (record: any) => {
onOk: async () => {
try {
let res = await deleteBaseWbsb([record.id]);
message.success('删除成功');
basicTable.value.refresh();
if (res.code == 0) {
message.success('删除成功');
basicTable.value.refresh();
} else {
message.error('删除失败');
}
} catch (error) {
message.error('删除失败');
return;
@ -159,12 +180,12 @@ const onSearchFinish = (values: any) => {
const params = {
logic: 'and',
filters: [
values.parentId
values.rvcd
? {
field: 'parentId',
field: 'rvcd',
operator: 'eq',
dataType: 'string',
value: values.parentId
value: values.rvcd
}
: null,
values.name
@ -197,23 +218,79 @@ const editModalCancel = () => {
//
const handleEditSubmit = async (values: any) => {
try {
let res = await saveBaseWbsb({
const formData = {
...currentRecord.value,
...values
});
message.success(`保存成功`);
};
delete formData.configNodes;
editModalVisible.value = false;
basicTable.value.refresh();
Object.keys(formData).forEach(key => {
if (key.includes('-')) {
delete formData[key];
}
});
let res = await saveBaseWbsbChild(formData);
if (res.code == 0) {
const alongId = res.data;
const treeData = values.configNodes || [];
if (treeData.length > 0) {
const formatTreeData = (
nodes: any[],
parentId: string | null = null
) => {
return nodes.map(node => ({
alongId,
stcd: node.stcd,
stnm: node.ennm,
sttpName: node.sttpName,
parentId: parentId ? parentId : null,
children:
node.children && node.children.length > 0
? formatTreeData(node.children, node.key)
: []
}));
};
const detailData = formatTreeData(treeData);
const detailRes = await saveBaseWbsbChildDetail(detailData);
if (detailRes.code == 0) {
message.success(`保存成功`);
editModalVisible.value = false;
basicTable.value.refresh();
} else {
message.error(`保存子节点失败`);
}
} else {
message.success(`保存成功`);
editModalVisible.value = false;
basicTable.value.refresh();
}
} else {
message.error(`保存失败`);
}
} catch (error) {
message.error(`保存失败`);
editModalVisible.value = false;
return;
}
};
const initOption = () => {};
const riverOptions = ref<any>([]);
const riverOptionsLoad = ref(false);
const initOption = () => {
const params = {};
riverOptionsLoad.value = true;
getRvcdDropdown(params).then((res: any) => {
res.data.forEach((item: any) => {
item.value = item.rvcd;
item.label = item.rvnm;
});
riverOptions.value = res.data || [];
riverOptionsLoad.value = false;
});
};
onMounted(() => {
initOption();
});

View File

@ -2,7 +2,7 @@
<a-modal
:title="isEdit ? '编辑图例数据' : '新增图例数据'"
v-model:open="modalVisible"
:confirm-loading="localLoading"
:confirm-loading="confirmLoading"
width="600px"
:destroy-on-close="true"
@cancel="handleCancel"
@ -194,6 +194,10 @@ const modalVisible = computed({
set: val => emit('update:visible', val)
});
const confirmLoading = computed(() => {
return localLoading.value || props.loading;
});
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
@ -276,6 +280,7 @@ const handleLayerSelect = (value: any) => {
};
const handleOk = async () => {
localLoading.value = true;
try {
await formRef.value.validate();
// undefined
@ -329,6 +334,7 @@ const handleOk = async () => {
} catch (error) {
console.error('Validate Failed:', error);
message.error('请检查表单填写是否正确');
localLoading.value = false;
}
};
@ -350,6 +356,7 @@ const resetForm = () => {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
localLoading.value = false;
};
const handleCancel = () => {
@ -358,11 +365,22 @@ const handleCancel = () => {
resetForm();
};
watch(
() => props.loading,
newLoading => {
if (newLoading) {
localLoading.value = false;
}
}
);
watch(
() => props.visible,
newVisible => {
if (newVisible) {
initForm();
} else {
localLoading.value = false;
}
},
{ immediate: false }

View File

@ -66,6 +66,7 @@
:LayerOptions="LayerOptions"
v-model:visible="editModalVisible"
:initial-values="currentRecord"
:loading="editLoading"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/>
@ -249,6 +250,7 @@ const LegendOptions = ref<any[]>([]);
const LayerOptions = ref<any[]>([]);
const currentRecord = ref<any | null>(null);
const editModalVisible = ref(false);
const editLoading = ref(false);
const handleAdd = () => {
currentRecord.value = null;
ensureOptionsLoaded().then(() => {
@ -401,11 +403,16 @@ const editModalCancel = () => {
//
const handleEditSubmit = async (values: any, type: string) => {
editLoading.value = true;
try {
let res = await saveMapLegend({
...currentRecord.value,
...values
});
if (res.code == 1) {
message.error('保存失败');
return;
}
if (type === 'switch') {
message.success('修改状态成功');
} else {
@ -421,6 +428,8 @@ const handleEditSubmit = async (values: any, type: string) => {
}
editModalVisible.value = false;
return;
} finally {
editLoading.value = false;
}
};
const initOption = () => {

View File

@ -0,0 +1,784 @@
<template>
<a-modal
:title="isEdit ? '编辑沿程配置' : '新增沿程配置'"
v-model:open="modalVisible"
:confirm-loading="localLoading"
width="1200px"
:destroy-on-close="true"
ok-text="保存"
@cancel="handleCancel"
@ok="handleOk"
>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
name="edit_form"
:labelCol="{ span: 5 }"
>
<a-row>
<a-col :span="12">
<a-form-item label="沿程配置名称" name="name">
<a-input
v-model:value="formData.name"
style="width: 100%"
placeholder="请输入沿程配置名称"
/> </a-form-item
></a-col>
<a-col :span="12">
<a-form-item label="配置编码" name="code">
<a-input
v-model:value="formData.code"
style="width: 100%"
placeholder="请输入配置编码"
/> </a-form-item
></a-col>
</a-row>
<a-row>
<a-col :span="12">
<a-form-item label="所在河段" name="rvnm">
<a-input
v-model:value="formData.rvnm"
style="width: 100%"
placeholder="请输入所在河段"
/> </a-form-item
></a-col>
<a-col :span="12">
<a-form-item label="备注" name="description">
<a-textarea
:rows="4"
v-model:value="formData.description"
style="width: 100%"
placeholder="请输入备注"
/> </a-form-item
></a-col>
</a-row>
</a-form>
<div class="config-tree-container">
<!-- 新增一级节点区域 -->
<div v-if="addingNewNode" class="add-node-row">
<a-select
v-model:value="newNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleNodeSelect"
>
<a-select-option
v-for="item in stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click="handleSaveNewNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button @click="handleCancelAdd" style="margin-left: 8px">
<MinusOutlined />
</a-button>
</div>
<a-button v-else class="mb-4" type="primary" @click="handleAdd">
<PlusOutlined /> 新增一级节点
</a-button>
<!-- 树形结构 -->
<a-tree
v-if="treeData.length > 0"
:tree-data="treeData"
:default-expand-all="false"
:selectable="false"
block-node
style="width: 300px"
>
<template #title="{ dataRef }">
<!-- 编辑状态只显示编辑区域 -->
<div
v-if="editingNode && editingNodeKey === dataRef.key"
class="add-child-node-row w-[300px]"
>
<a-select
v-model:value="editNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleEditNodeSelect"
>
<a-select-option
v-for="item in stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click.stop="handleSaveEditNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button
@click.stop="handleCancelEditNode"
style="margin-left: 8px"
>
<MinusOutlined />
</a-button>
</div>
<!-- 非编辑状态显示正常节点内容 -->
<template v-else>
<div class="tree-node-wrapper">
<span class="tree-node-title"> {{ getNodeTitle(dataRef) }} </span>
<div class="tree-node-actions">
<!-- 一级节点支持新增子节点编辑排序删除 -->
<template v-if="!dataRef.parentId">
<a-button
type="link"
size="small"
@click.stop="handleAddChild(dataRef)"
title="添加子节点"
>
<PlusOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleEditNode(dataRef)"
title="编辑"
>
<EditOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveUp(dataRef)"
title="上移"
:disabled="isFirstNode(dataRef)"
>
<ArrowUpOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveDown(dataRef)"
title="下移"
:disabled="isLastNode(dataRef)"
>
<ArrowDownOutlined />
</a-button>
<a-button
type="link"
size="small"
danger
@click.stop="handleDeleteNode(dataRef)"
title="删除"
>
<DeleteOutlined />
</a-button>
</template>
<!-- 二级节点只支持编辑排序删除 -->
<template v-else>
<a-button
type="link"
size="small"
@click.stop="handleEditNode(dataRef)"
title="编辑"
>
<EditOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveUp(dataRef)"
title="上移"
:disabled="isFirstNode(dataRef)"
>
<ArrowUpOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveDown(dataRef)"
title="下移"
:disabled="isLastNode(dataRef)"
>
<ArrowDownOutlined />
</a-button>
<a-button
type="link"
size="small"
danger
@click.stop="handleDeleteNode(dataRef)"
title="删除"
>
<DeleteOutlined />
</a-button>
</template>
</div>
</div>
<!-- 子节点添加区域 -->
<div
v-if="addingChildNode && currentParentKey === dataRef.key"
class="add-child-node-row"
>
<a-select
v-model:value="childNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleChildNodeSelect"
>
<a-select-option
v-for="item in stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click.stop="handleSaveChildNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button
@click.stop="handleCancelAddChild"
style="margin-left: 8px"
>
<MinusOutlined />
</a-button>
</div>
</template>
</template>
</a-tree>
<a-empty v-else description="暂无数据" style="margin-top: 20px" />
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue';
import { message, Modal } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/es/form';
import dayjs from 'dayjs';
import {
PlusOutlined,
MinusOutlined,
EditOutlined,
DeleteOutlined,
ArrowUpOutlined,
ArrowDownOutlined
} from '@ant-design/icons-vue';
interface Props {
visible: boolean;
initialValues?: any | null;
loading?: boolean;
}
const localLoading = ref(false);
const props = withDefaults(defineProps<Props>(), {
visible: false,
initialValues: null,
loading: false
});
const modalVisible = computed({
get: () => props.visible,
set: val => emit('update:visible', val)
});
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
(e: 'ok', values: any): void;
}>();
const formRef = ref();
const defaultFormData = reactive({
id: undefined,
name: undefined,
code: undefined,
rvnm: undefined,
description: undefined
});
const formData: any = reactive({ ...defaultFormData });
const rules: Record<string, Rule[]> = {
name: [{ required: true, message: '请输入沿程配置名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入配置编码', trigger: 'blur' }],
rvnm: [{ required: true, message: '请输入所在河段', trigger: 'blur' }]
};
const isEdit = computed(() => !!props.initialValues);
//
const treeData = ref<any[]>([]);
//
const addingNewNode = ref(false);
const newNodeData = ref({
stcd: undefined,
stnm: '',
sttpName: ''
});
//
const addingChildNode = ref(false);
const currentParentKey = ref<string | null>(null);
const childNodeData = ref({
stcd: undefined,
stnm: '',
sttpName: ''
});
//
const editingNode = ref(false);
const editingNodeKey = ref<string | null>(null);
const editNodeData = ref({
stcd: undefined,
stnm: '',
sttpName: '',
originalData: null as any
});
//
const stationList = ref([
{ stcd: '1', stnm: '站点A', sttpName: '类型1' },
{ stcd: '2', stnm: '站点B', sttpName: '类型2' },
{ stcd: '3', stnm: '站点C', sttpName: '类型1' },
{ stcd: '4', stnm: '站点D', sttpName: '类型3' }
]);
// ID
let nodeIdCounter = 0;
const generateNodeId = () => `node_${Date.now()}_${++nodeIdCounter}`;
//
const getNodeTitle = (dataRef: any) => {
if (!dataRef.parentId) {
// stnm
return dataRef.stnm || '';
} else {
// stnm + sttpName
return `${dataRef.stnm || ''} ${dataRef.sttpName || ''}`;
}
};
//
const filterOption = (input: string, option: any) => {
return (
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
);
};
//
const isFirstNode = (dataRef: any) => {
const siblings = getSiblings(dataRef);
return siblings.length > 0 && siblings[0].key === dataRef.key;
};
//
const isLastNode = (dataRef: any) => {
const siblings = getSiblings(dataRef);
return (
siblings.length > 0 && siblings[siblings.length - 1].key === dataRef.key
);
};
//
const getSiblings = (dataRef: any) => {
if (!dataRef.parentId) {
//
return treeData.value;
} else {
// children
const parent = findNodeByKey(treeData.value, dataRef.parentId);
return parent?.children || [];
}
};
// key
const findNodeByKey = (nodes: any[], key: string): any => {
for (const node of nodes) {
if (node.key === key) {
return node;
}
if (node.children && node.children.length > 0) {
const found = findNodeByKey(node.children, key);
if (found) return found;
}
}
return null;
};
//
const handleAdd = () => {
addingNewNode.value = true;
newNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: ''
};
};
//
const handleNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
if (station) {
newNodeData.value.stnm = station.stnm;
newNodeData.value.sttpName = station.sttpName;
}
};
//
const handleSaveNewNode = () => {
if (!newNodeData.value.stcd) {
message.error('请选择站点');
return;
}
const newNode = {
key: generateNodeId(),
stcd: newNodeData.value.stcd,
stnm: newNodeData.value.stnm,
sttpName: newNodeData.value.sttpName,
parentId: null,
children: []
};
treeData.value.push(newNode);
addingNewNode.value = false;
message.success('添加成功');
};
//
const handleCancelAdd = () => {
addingNewNode.value = false;
newNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: ''
};
};
//
const handleAddChild = (parentData: any) => {
addingChildNode.value = true;
currentParentKey.value = parentData.key;
childNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: ''
};
};
//
const handleChildNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
if (station) {
childNodeData.value.stnm = station.stnm;
childNodeData.value.sttpName = station.sttpName;
}
};
//
const handleSaveChildNode = () => {
if (!childNodeData.value.stcd) {
message.error('请选择站点');
return;
}
const parent = findNodeByKey(treeData.value, currentParentKey.value!);
if (!parent) {
message.error('父节点不存在');
return;
}
const newChild = {
key: generateNodeId(),
stcd: childNodeData.value.stcd,
stnm: childNodeData.value.stnm,
sttpName: childNodeData.value.sttpName,
parentId: parent.key,
children: []
};
if (!parent.children) {
parent.children = [];
}
//
parent.children.unshift(newChild);
addingChildNode.value = false;
currentParentKey.value = null;
message.success('添加子节点成功');
};
//
const handleCancelAddChild = () => {
addingChildNode.value = false;
currentParentKey.value = null;
childNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: ''
};
};
//
const handleEditNode = (dataRef: any) => {
editingNode.value = true;
editingNodeKey.value = dataRef.key;
//
editNodeData.value = {
stcd: dataRef.stcd,
stnm: dataRef.stnm,
sttpName: dataRef.sttpName,
originalData: { ...dataRef }
};
};
//
const handleEditNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
if (station) {
editNodeData.value.stnm = station.stnm;
editNodeData.value.sttpName = station.sttpName;
editNodeData.value.stcd = station.stcd;
}
};
//
const handleSaveEditNode = () => {
if (!editNodeData.value.stcd) {
message.error('请选择站点');
return;
}
//
const node = findNodeByKey(treeData.value, editingNodeKey.value!);
if (!node) {
message.error('节点不存在');
return;
}
//
node.stcd = editNodeData.value.stcd;
node.stnm = editNodeData.value.stnm;
node.sttpName = editNodeData.value.sttpName;
editingNode.value = false;
editingNodeKey.value = null;
message.success('编辑成功');
};
//
const handleCancelEditNode = () => {
editingNode.value = false;
editingNodeKey.value = null;
editNodeData.value = {
stcd: undefined,
stnm: '',
sttpName: '',
originalData: null
};
};
//
const handleMoveUp = (dataRef: any) => {
const siblings = getSiblings(dataRef);
const index = siblings.findIndex(item => item.key === dataRef.key);
if (index > 0) {
//
[siblings[index - 1], siblings[index]] = [
siblings[index],
siblings[index - 1]
];
message.success('上移成功');
}
};
//
const handleMoveDown = (dataRef: any) => {
const siblings = getSiblings(dataRef);
const index = siblings.findIndex(item => item.key === dataRef.key);
if (index < siblings.length - 1) {
//
[siblings[index], siblings[index + 1]] = [
siblings[index + 1],
siblings[index]
];
message.success('下移成功');
}
};
//
const handleDeleteNode = (dataRef: any) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除节点"${dataRef.stnm}"吗?`,
okText: '确定',
cancelText: '取消',
onOk: () => {
if (!dataRef.parentId) {
//
const index = treeData.value.findIndex(
item => item.key === dataRef.key
);
if (index > -1) {
treeData.value.splice(index, 1);
}
} else {
//
const parent = findNodeByKey(treeData.value, dataRef.parentId);
if (parent && parent.children) {
const index = parent.children.findIndex(
item => item.key === dataRef.key
);
if (index > -1) {
parent.children.splice(index, 1);
}
}
}
message.success('删除成功');
}
});
};
const handleOk = async () => {
try {
await formRef.value.validate();
const submitValues = {
...formData,
captureDate: formData.captureDate
? dayjs(formData.captureDate).format('YYYY-MM-DD')
: undefined,
configNodes: treeData.value //
};
emit('ok', submitValues);
} catch (error) {
console.error('Validate Failed:', error);
message.error('请检查表单填写是否正确');
}
};
const initForm = () => {
if (props.initialValues) {
const values = props.initialValues;
Object.keys(formData).forEach(key => {
if (values.hasOwnProperty(key)) {
if (key === 'captureDate' && values[key]) {
formData[key] = dayjs(values[key]);
} else {
formData[key] = values[key];
}
}
});
//
if (values.configNodes && Array.isArray(values.configNodes)) {
treeData.value = values.configNodes.map((node: any) => ({
...node,
children:
node.children?.map((child: any) => ({
...child
})) || []
}));
} else {
treeData.value = [];
}
} else {
resetForm();
}
};
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
treeData.value = [];
addingNewNode.value = false;
};
const handleCancel = () => {
emit('update:visible', false);
emit('cancel');
resetForm();
};
watch(
() => props.visible,
newVisible => {
if (newVisible) {
initForm();
}
},
{ immediate: false }
);
defineExpose({
localLoading
});
</script>
<style scoped lang="scss">
.config-tree-container {
margin-top: 16px;
.add-node-row {
display: flex;
align-items: center;
margin-bottom: 16px;
}
}
.tree-node-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding-right: 8px;
.tree-node-title {
flex: 1;
}
.tree-node-actions {
opacity: 0;
transition: opacity 0.2s;
}
}
//
.tree-node-wrapper:hover {
.tree-node-actions {
opacity: 1;
}
}
//
.add-child-node-row {
display: flex;
align-items: center;
padding: 8px 0 8px 24px;
margin-top: 4px;
background-color: #f5f5f5;
border-radius: 4px;
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<div class="content-search">
<BasicSearch
ref="basicSearchRef"
:searchList="searchList"
:initial-values="initSearchData"
@reset="handleReset"
@finish="onSearchFinish"
@values-change="onValuesChange"
>
<template #actions>
<a-tooltip title="新增配置">
<a-button @click="props.handleAdd" type="primary"> 新增沿程配置 </a-button>
</a-tooltip>
</template>
</BasicSearch>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import BasicSearch from "@/components/BasicSearch/index.vue";
interface Props {
handleAdd: () => void;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "reset", values: any): void;
(e: "searchFinish", values: any): void;
}>();
const initSearchData = {
configName: "",
};
const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: "Input",
name: "name",
label: "沿程配置名称",
fieldProps: {
allowClear: true,
},
},
{
type: "Input",
name: "rvnm",
label: "所在河段",
fieldProps: {
allowClear: true,
},
},
]);
const onSearchFinish = (values: any) => {
emit("searchFinish", values);
};
const onValuesChange = (changedValues: any, allValues: any) => {
searchData.value = { ...searchData.value, ...allValues };
};
const handleReset = () => {
emit("reset", initSearchData);
};
onMounted(() => {
emit("searchFinish", initSearchData);
});
</script>
<style lang="scss"></style>

View File

@ -0,0 +1,228 @@
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\ConfigManagement\index.vue -->
<template>
<div class="content">
推送配置
<!-- <ConfigManagementSearch
ref="configManagementSearch"
:handle-add="handleAdd"
@reset="handleReset"
@search-finish="onSearchFinish"
/>
<BasicTable
ref="basicTable"
:columns="columns"
:list-url="getAllConfigTree"
:search-params="searchParams"
>
<template #action="{ column, record }">
<div class="flex gap-[6px]">
<a-button
class="!p-0"
type="link"
size="small"
@click="handleEdit(record)"
>编辑</a-button
>
<a-button
class="!p-0"
type="link"
size="small"
danger
@click="handleDelete(record)"
>删除</a-button
>
</div>
</template>
</BasicTable>
<ConfigManagementForm
ref="configManagementForm"
v-model:visible="editModalVisible"
:initial-values="currentRecord"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/> -->
</div>
</template>
<script setup lang="ts">
import { ref, nextTick, onMounted } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import ConfigManagementSearch from './ConfigManagementSearch.vue';
import ConfigManagementForm from './ConfigManagementForm.vue';
import { message, Modal } from 'ant-design-vue';
import {
getAllConfigTree,
deleteBaseWbsb,
saveBaseWbsb
} from '@/api/system/map/ConfigManagement';
//
const basicTable = ref<any>(null);
//
const columns = [
{
title: '序号',
dataIndex: 'index',
key: 'index',
width: 60,
align: 'center',
customRender: ({ text, record, index }) => index + 1
},
{
title: '沿程配置名称',
dataIndex: 'name',
key: 'name',
width: 150
},
{
title: '配置编码',
key: 'code',
dataIndex: 'code',
width: 120
},
{
title: '所在河段',
dataIndex: 'rvnm',
key: 'rvnm',
ellipsis: true
},
{
title: '创建人',
key: 'recordUser',
dataIndex: 'recordUser'
},
{
title: '创建时间',
key: 'recordTime',
dataIndex: 'recordTime',
width: 150
},
{
title: '更新时间',
key: 'modifyTime',
dataIndex: 'modifyTime',
width: 150
},
{
title: '备注',
dataIndex: 'remark',
key: 'remark',
width: 360
},
{
title: '操作',
key: 'action',
width: 120,
fixed: 'right',
slots: { customRender: 'action' }
}
];
//
const searchParams = ref({});
//
const currentRecord = ref<any | null>(null);
const editModalVisible = ref(false);
//
const handleAdd = () => {
currentRecord.value = null;
editModalVisible.value = true;
};
//
const handleEdit = (record: any) => {
currentRecord.value = { ...record };
editModalVisible.value = true;
};
//
const handleDelete = (record: any) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除选中的记录吗?',
zIndex: 2002,
onOk: async () => {
try {
let res = await deleteBaseWbsb([record.id]);
message.success('删除成功');
basicTable.value.refresh();
} catch (error) {
message.error('删除失败');
return;
}
}
});
};
//
const onSearchFinish = (values: any) => {
const params = {
logic: 'and',
filters: [
values.parentId
? {
field: 'parentId',
operator: 'eq',
dataType: 'string',
value: values.parentId
}
: null,
values.name
? {
field: 'name',
operator: 'contains',
dataType: 'string',
value: values.name
}
: null
].filter(Boolean)
};
basicTable.value.getList(params);
};
//
const handleReset = () => {
basicTable.value.getList({
logic: 'and',
filters: []
});
};
//
const editModalCancel = () => {
currentRecord.value = null;
editModalVisible.value = false;
};
//
const handleEditSubmit = async (values: any) => {
try {
let res = await saveBaseWbsb({
...currentRecord.value,
...values
});
message.success(`保存成功`);
editModalVisible.value = false;
basicTable.value.refresh();
} catch (error) {
message.error(`保存失败`);
editModalVisible.value = false;
return;
}
};
const initOption = () => {};
onMounted(() => {
initOption();
});
</script>
<style scoped lang="scss">
.config-management {
height: 100%;
padding: 16px;
}
</style>

View File

@ -0,0 +1,260 @@
<template>
<a-modal
:title="isEdit ? '编辑图层' : '新增图层'"
v-model:open="modalVisible"
:confirm-loading="props.loading"
width="600px"
:destroy-on-close="true"
@cancel="handleCancel"
@ok="handleOk"
>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
name="edit_form"
:labelCol="{ span: 5 }"
>
<a-form-item label="图层名称" name="title">
<a-input
v-model:value="formData.title"
style="width: 100%"
placeholder="请输入图层名称"
/>
</a-form-item>
<a-form-item label="图层编码" name="code">
<a-input
v-model:value="formData.code"
style="width: 100%"
placeholder="请输入图层编码"
/>
</a-form-item>
<a-form-item label="图层类型" name="type" v-if="isInterface">
<a-select
:options="typeOpiton"
v-model:value="formData.type"
:fieldNames="{
label: 'dictName',
value: 'itemCode'
}"
style="width: 100%"
placeholder="请输入图层类型"
/>
</a-form-item>
<a-form-item label="标签显示级别" name="treeLevel" v-if="!isInterface">
<a-input-number
v-model:value="formData.treeLevel"
style="width: 100%"
placeholder="请输入标签显示级别"
:min="0"
:max="24"
:step="1"
/>
</a-form-item>
<a-form-item label="锚点接口" name="url" v-if="isInterface">
<a-input
v-model:value="formData.url"
style="width: 100%"
placeholder="请输入锚点接口"
/>
</a-form-item>
<a-form-item label="接口参数" name="anchorParamJson" v-if="isInterface">
<a-textarea
:rows="6"
v-model:value="formData.anchorParamJson"
style="width: 100%"
placeholder="请输入接口参数"
/>
</a-form-item>
<a-form-item
label="三维接口"
name="urlThd"
v-if="isInterface && formData.type === 'geojson'"
>
<a-input
v-model:value="formData.urlThd"
style="width: 100%"
placeholder="请输入三维接口"
/>
</a-form-item>
<a-form-item
label="gisJson"
name="paramJson"
v-if="isInterface && formData.type === 'geojson'"
>
<a-textarea
:rows="4"
v-model:value="formData.paramJson"
style="width: 100%"
placeholder="请输入gisJson"
/>
</a-form-item>
<a-form-item
label="备注"
name="description"
v-if="!isInterface || formData.type === 'geojson'"
>
<a-textarea
:rows="4"
v-model:value="formData.description"
style="width: 100%"
placeholder="请输入备注"
/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue';
import { message } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/es/form';
import { getDictItemsByCode } from '@/api/dict';
// Props
interface Props {
visible: boolean;
initialValues?: any | null;
loading?: boolean;
isInterface?: boolean;
}
//
const localLoading = ref(false);
const props = withDefaults(defineProps<Props>(), {
visible: false,
initialValues: null,
loading: false,
isInterface: false
});
const modalVisible = computed({
get: () => props.visible,
set: val => emit('update:visible', val)
});
// Emits
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
(e: 'ok', values: any, type: string): void;
}>();
//
const formRef = ref();
// 1.
const bodyLengthError = ref<string>('');
const weightError = ref<string>('');
//
const defaultFormData = reactive({
id: undefined,
title: undefined,
code: undefined,
type: undefined,
treeLevel: undefined,
description: undefined,
parentId: undefined,
url: undefined,
urlThd: undefined,
paramJson: undefined,
anchorParamJson: undefined
});
const typeOpiton = ref<any>([]);
const formData: any = reactive({ ...defaultFormData });
//
const rules: Record<string, Rule[]> = {
title: [{ required: true, message: '请输入图层名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入图层编码', trigger: 'blur' }],
type: [{ required: true, message: '请输入图层类型', trigger: 'change' }],
treeLevel: [
{ required: true, message: '请输入标签显示级别', trigger: 'blur' }
]
};
//
const isEdit = computed(() => !!props.initialValues);
//
const handleOk = async () => {
try {
//
await formRef.value.validate();
//
const submitValues = {
...formData
};
emit('ok', submitValues, 'form');
} catch (error) {
console.error('Validate Failed:', error);
message.error('请检查表单填写是否正确');
}
};
const initForm = () => {
if (props.initialValues) {
// --- ---
const values = props.initialValues;
if (props.isInterface) {
initType();
}
//
Object.keys(formData).forEach(key => {
// initialValues
if (values.hasOwnProperty(key)) {
formData[key] = values[key];
}
});
} else {
resetForm();
}
};
const initType = () => {
getDictItemsByCode({ dictCode: 'LayerTypeCode' }).then(res => {
typeOpiton.value = res.data;
});
};
//
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
//
bodyLengthError.value = '';
weightError.value = '';
};
//
const handleCancel = () => {
emit('update:visible', false);
emit('cancel');
resetForm();
};
watch(
() => props.visible,
newVisible => {
if (newVisible) {
initForm();
}
},
{ immediate: false }
);
//
watch(
() => formData.type,
(newType, oldType) => {
if (oldType && newType !== oldType) {
//
formData.urlThd = undefined;
formData.paramJson = undefined;
if (oldType === 'geojson') {
formData.description = undefined;
}
}
}
);
//
defineExpose({
localLoading
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,69 @@
<template>
<div class="content-search">
<BasicSearch
ref="basicSearchRef"
:searchList="searchList"
:initial-values="initSearchData"
@reset="handleReset"
@finish="onSearchFinish"
@values-change="onValuesChange"
>
<template #actions>
<a-tooltip title="新增图层">
<a-button @click="props.handleAdd" type="primary"> 新增图层 </a-button>
</a-tooltip>
</template>
</BasicSearch>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, watch } from "vue";
import BasicSearch from "@/components/BasicSearch/index.vue"; //
interface Props {
handleAdd: (record: any) => void;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "reset", values: any): void;
(e: "searchFinish", values: any): void;
}>();
const initSearchData = {
title: "",
};
const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: "Input",
name: "title",
label: "图层名称",
fieldProps: {
allowClear: true,
},
},
]);
// --- Methods ---
// 2.
const onSearchFinish = (values: any) => {
emit("searchFinish", values);
};
//
const onValuesChange = (changedValues: any, allValues: any) => {
searchData.value = { ...searchData.value, ...allValues };
};
//
const handleReset = () => {
emit("reset", initSearchData);
};
onMounted(() => {
emit("searchFinish", initSearchData);
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,358 @@
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\LayerManagement\index.vue -->
<template>
<div class="content">
推送历史
<!-- <LayerManagementSearch
ref="layerManagementSearch"
:handle-add="handleAdd"
@reset="handleReset"
@search-finish="onSearchFinish"
/>
<BasicTable
ref="basicTable"
:columns="columns"
:list-url="getAllMapLayerTree"
:search-params="searchParams"
>
<template #action="{ record }">
<div class="flex gap-[6px]">
<a-button
class="!p-0"
type="link"
size="small"
@click="handleEdit(record)"
>编辑</a-button
>
<a-button
v-if="record.children && record.children.length == 0"
class="!p-0"
type="link"
size="small"
@click="handleEditInterface(record)"
>编辑接口</a-button
>
<a-button
class="!p-0"
type="link"
size="small"
@click="handleAdd(record)"
>新增子图层</a-button
>
<a-button
class="!p-0"
type="link"
size="small"
danger
@click="handleDelete(record)"
>删除</a-button
>
</div>
</template>
<template #enable="{ record }">
<a-switch
:checked="record.enable === 1"
@change="(checked: boolean) => handleEnableChange(record, checked)"
/>
</template>
<template #checkable="{ record }">
<a-switch
:checked="record.checkable === 1"
@change="(checked: boolean) => handleCheckableChange(record, checked)"
/>
</template>
</BasicTable>
<LayerManagementForm
ref="layerManagementForm"
v-model:visible="editModalVisible"
:initial-values="currentRecord"
:is-interface="isInterface"
:loading="editLoading"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/> -->
</div>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import LayerManagementSearch from './LayerManagementSearch.vue';
import LayerManagementForm from './LayerManagementForm.vue';
import { message, Modal } from 'ant-design-vue';
import {
getAllMapLayerTree,
saveMapLayer,
deleteMapLayer
} from '@/api/system/map/LayerManagement';
//
const columns = [
{
title: '',
dataIndex: '',
key: '',
width: 50,
customRender: () => {
return '';
}
},
{
title: '序号',
key: 'index',
width: 60,
align: 'center',
customRender: ({ record, index }: any) => {
// children
// parentId
if (!record.parentId) {
// dataSource
let rootIndex = 0;
for (let i = 0; i < basicTable.value.tableData.length; i++) {
const item = basicTable.value.tableData[i];
// parentId
if (!item.parentId) {
rootIndex++;
//
if (
item.id === record.id ||
(item.key === record.key && item.id === record.id)
) {
return rootIndex;
}
}
}
return rootIndex;
} else {
// children
const findParentAndIndex = (data: any[], parentId: string): number => {
for (const item of data) {
if (item.key === parentId || item.id === parentId) {
// children
if (item.children && item.children.length > 0) {
const childIndex = item.children.findIndex(
(child: any) =>
child.key === record.key || child.id === record.id
);
return childIndex + 1;
}
}
//
if (item.children && item.children.length > 0) {
const result = findParentAndIndex(item.children, parentId);
if (result > 0) {
return result;
}
}
}
return 0;
};
return findParentAndIndex(basicTable.value.tableData, record.parentId);
}
}
},
{
title: '图层名称',
dataIndex: 'title',
key: 'title',
width: 150
},
{
title: '图层编码',
dataIndex: 'code',
key: 'code',
width: 120
},
{
title: '标签显示级别',
dataIndex: 'treeLevel',
key: 'treeLevel',
width: 100
},
{
title: '是否启用',
dataIndex: 'enable',
key: 'enable',
width: 80,
slots: { customRender: 'enable' }
},
{
title: '是否可选',
dataIndex: 'checkable',
key: 'checkable',
width: 80,
slots: { customRender: 'checkable' }
},
{
title: '备注',
dataIndex: 'description',
key: 'description'
},
{
title: '创建人',
key: 'displayRecordUser',
dataIndex: 'displayRecordUser'
},
{
title: '创建时间',
key: 'recordTime',
dataIndex: 'recordTime',
width: 150
},
{
title: '更新时间',
key: 'modifyTime',
dataIndex: 'modifyTime',
width: 150
},
{
title: '操作',
key: 'action',
width: 220,
fixed: 'right',
slots: { customRender: 'action' }
}
];
//
const basicTable = ref<any>(null);
//
const searchParams = ref({});
//
const isInterface = ref(false);
const editModalVisible = ref(false);
const editLoading = ref(false);
const parentId = ref<string | null>(null);
const currentRecord = ref<any | null>(null);
//
const handleAdd = (record: any) => {
currentRecord.value = null;
parentId.value = record.id;
editModalVisible.value = true;
};
// '-'
const filterRecord = (record: any) => {
const filtered: any = {};
const keepKeys = ['code']; //
Object.keys(record).forEach(key => {
if (keepKeys.includes(key) || record[key] !== '-') {
filtered[key] = record[key];
}
});
return filtered;
};
//
const handleEdit = (record: any) => {
console.log('handleEdit', record);
parentId.value = null;
isInterface.value = false;
currentRecord.value = filterRecord(record);
editModalVisible.value = true;
};
//
const handleEditInterface = (record: any) => {
console.log('handleEdit', record);
currentRecord.value = filterRecord(record);
console.log('handleEdit', currentRecord.value);
isInterface.value = true;
parentId.value = null;
editModalVisible.value = true;
};
//
const handleDelete = async (record: any) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除选中的记录吗?',
zIndex: 2002,
onOk: async () => {
try {
let res = await deleteMapLayer([record.id]);
message.success('删除成功');
basicTable.value.refresh();
} catch (error) {
message.error('删除失败');
return;
}
}
});
};
//
const onSearchFinish = (values: any) => {
searchParams.value = values;
nextTick(() => {
basicTable.value.getList();
});
};
//
const handleReset = () => {
searchParams.value = {};
nextTick(() => {
basicTable.value.refresh();
});
};
//
const handleEnableChange = (record: any, checked: boolean) => {
currentRecord.value = filterRecord(record);
console.log(currentRecord.value);
record.enable = checked ? 1 : 0;
handleEditSubmit({ enable: checked ? 1 : 0 }, 'switch');
};
//
const handleCheckableChange = (record: any, checked: boolean) => {
currentRecord.value = filterRecord(record);
record.checkable = checked ? 1 : 0;
handleEditSubmit({ checkable: checked ? 1 : 0 }, 'switch');
};
//
const editModalCancel = () => {
isInterface.value = false;
parentId.value = null;
currentRecord.value = null;
editModalVisible.value = false;
};
const handleEditSubmit = async (values: any, type: string) => {
editLoading.value = true;
try {
let res = await saveMapLayer({
...currentRecord.value,
...values,
parentId: parentId.value
});
if (res.code == 0) {
if (type === 'switch') {
message.success('修改状态成功');
} else {
message.success(`保存成功`);
}
editModalVisible.value = false;
basicTable.value.refresh();
} else {
if (type === 'switch') {
message.success('修改状态成功');
} else {
message.success(`保存成功`);
}
}
} catch (error) {
if (type === 'switch') {
message.error('修改状态失败');
} else {
message.error(`保存失败`);
}
} finally {
editLoading.value = false;
}
};
</script>
<style scoped lang="scss">
:deep(.ant-table-cell-with-append) {
display: flex;
width: 100%;
}
:deep(.ant-table-row-expand-icon) {
width: 17px !important;
margin: 0 !important;
}
</style>

View File

@ -0,0 +1,89 @@
<template>
<div class="map-page">
<!-- Ant Design Tabs -->
<a-tabs v-model:activeKey="activeTab" class="map-tabs">
<a-tab-pane v-for="tab in tabs" :key="tab.value" :tab="tab.name">
<!-- 动态加载对应的组件 -->
<component :is="getComponent(tab.value)" />
</a-tab-pane>
</a-tabs>
</div>
</template>
<script setup lang="ts">
import { ref, defineAsyncComponent } from 'vue';
// Tab
const tabs = [
{
name: '推送配置',
value: 'config'
},
{
name: '推送历史',
value: 'history'
}
];
// tab
const activeTab = ref('config');
//
const getComponent = (value: string) => {
const componentMap: Record<string, any> = {
config: defineAsyncComponent(
() => import('./components/ConfigManagement/index.vue')
),
history: defineAsyncComponent(
() => import('./components/HistoryManagement/index.vue')
)
};
return componentMap[value] || null;
};
</script>
<style scoped lang="scss">
.map-page {
position: relative;
z-index: 900;
pointer-events: all;
width: 100%;
height: 100%;
padding-top: 2px;
background-color: #ffffff;
box-sizing: border-box;
overflow: hidden;
.content {
position: relative;
z-index: 900;
pointer-events: all;
width: 100%;
height: 100%;
background-color: #ffffff;
display: flex;
flex-direction: column;
gap: 10px;
box-sizing: border-box;
overflow: hidden;
}
:deep(.ant-form-item) {
margin-bottom: 0 !important;
}
}
:deep(.ant-tabs) {
height: 100%; // tabs header
padding: 0 13px;
}
.map-tabs {
:deep(.ant-tabs-content) {
height: 100%; // tabs header
overflow: auto;
}
:deep(.ant-tabs-tabpane) {
height: 100%;
}
}
</style>