You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

390 lines
13 KiB
Vue

This file contains ambiguous Unicode characters!

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

<template>
<div class="left-tree-container">
<j-input placeholder="名称" v-model:value="searchValue" @pressEnter="search" @change="searchChange">
<template #suffix>
<AIcon type="SearchOutlined" @click="search" />
</template>
</j-input>
<div v-if="admin" class="add-btn">
<j-row :style="{ marginTop: '10px' }">
<j-button type="primary" @click="visible = true">
新增站点
</j-button>
</j-row>
</div>
<div class="tree" :style="{ height: 'calc(100vh - 332px)' }">
<j-spin :spinning='loading'>
<j-tree :tree-data="treeData" v-if="treeData.length" draggable @dragstart="dragstart"
:fieldNames="{ title: 'nodeName', key: 'id', children: 'children' }" blockNode
:default-expanded-keys="defaultExpandedKeys" :showLine="{ showLeafIcon: false }" :showIcon="true">
<template #title="{ nodeName, data, positionX, parentId, nodeType }">
<div v-if="selectId === data.id">
<j-input v-model:value="addName" @blur="() => saveGroup(data)" ref="inputRef"
:maxlength="64"></j-input>
</div>
<span v-else class='department-tree-item-content'>
<span class='title' :style="{
width: '100px',
marginRight: nodeType == '0' ? '120px' : '120px',
}">
<j-ellipsis>
<AIcon :style="{ color: positionX?'#315efb':''}" v-if="nodeType == '1'" type="HeartFilled" />
&nbsp;{{ nodeName }}
</j-ellipsis>
</span>
<span class="func-btns" :style="{
width: nodeType == '0' ? '120px' : '120px',
}" @click="(e) => e.stopPropagation()">
<PermissionButton type="link" :tooltip="{ title: '编辑' }" @click="editGroup(data)">
<AIcon type="EditOutlined" />
</PermissionButton>
<PermissionButton v-if="nodeType === '0' || parentId === '0'" type="link"
:tooltip="{ title: '组内监测点绑定设备' }" @click="openBindModel(data)">
<AIcon type="ApiOutlined" />
</PermissionButton>
<PermissionButton v-if="nodeType === '0' || parentId === '0'" type="link"
:tooltip="{ title: '新增子站' }" @click="addGroup(data, '0')">
<AIcon type="PlusOutlined" />
</PermissionButton>
<PermissionButton v-if="nodeType === '0' || parentId === '0'" type="link"
:tooltip="{ title: '新增子监测点' }" @click="addGroup(data, '1')">
<AIcon type="PlusCircleOutlined" />
</PermissionButton>
<PermissionButton type="link" :tooltip="{ title: '删除' }" :popConfirm="{
title: `确定要删除吗`,
onConfirm: () => { deleteGroup(data.id) },
}">
<AIcon type="DeleteOutlined" />
</PermissionButton>
<PermissionButton v-if="nodeType === '1'" type="link" :tooltip="{ title: '移除' }">
<AIcon type="CloseOutlined" />
</PermissionButton>
</span>
</span>
</template>
</j-tree>
<j-empty v-else description="暂无数据" />
</j-spin>
</div>
<j-row :style="{ marginTop: '10px' }" :gutter="10">
<j-col :span="12">
<j-button type="primary" @click="visible = true">
导入站点
</j-button>
</j-col>
<j-col :span="12">
<j-button type="primary" @click="visible = true">
导出站点
</j-button>
</j-col>
</j-row>
<AddGroup v-if="visible" :logicId="logicId" @close="visible = false" @save="() => { emit('getMonitorList') }" />
<BindDevice v-if="bindVisible" :checkNodeData="checkNodeData" @close="bindVisible = false"
@save="() => { emit('getMonitorList') }" />
</div>
</template>
<script lang="ts" setup>
import { onlyMessage } from '@/utils/comm';
import { useUserInfo } from '@/store/userInfo';
import { storeToRefs } from 'pinia';
import { filterTreeItem } from '@/utils/comm'
import { addMonitor, editMonitor, deleteMonitor } from '@/api/monitor';
import { randomString } from '@/utils/utils';
import AddGroup from './AddGroup.vue';
import BindDevice from './BindDevice.vue';
const visible = ref<boolean>(false);
const bindVisible = ref<boolean>(false);
const loading = ref<boolean>(false); // 数据加载状态
const emit = defineEmits(['selectData', 'getMonitorList']);
const treeData = ref([]);
const checkNodeData = ref([]);
const props = defineProps({
listData: {
type: Array,
default: []
},
logicId: {
type: String,
}
})
const userInfoStore = useUserInfo();
const { userInfos } = storeToRefs(userInfoStore);
const admin = computed(() => {
return userInfos.value?.username === 'admin';
});
const searchValue = ref();
const inputRef = ref();
const addName = ref();
const selectId = ref();
const defaultExpandedKeys = ref<(number | string)[]>([]);
const isMonitorList = (node) => {
if (!node) {
return false;
}
const result = [];
const recurse = (arr) => {
arr.forEach(item => {
// 如果 nodeType 等于 1直接添加到结果中
if (item.nodeType == 1) {
result.push(item);
} else if (item.children && Array.isArray(item.children)) {
// 如果 nodeType 等于 0并且存在 children则递归处理
recurse(item.children);
}
});
};
recurse(node); // 开始递归处理
return result.length
};
const openBindModel = (node) => {
if (isMonitorList(node?.children)) {
checkNodeData.value = node;
bindVisible.value = true;
} else {
onlyMessage('该组下无监测点')
}
};
//获取全部id
const getTreeId = <T>(treeNode: T[]) => {
let result: any[] = [];
const traverse = (node: any) => {
result?.push(node.id)
if (node.children && Array.isArray(node.children)) {
node.children.forEach((child: any) => traverse(child))
}
}
treeNode?.forEach((child: any) => traverse(child))
return result
}
watch(() => props.listData, (newValue) => {
treeData.value = newValue;
defaultExpandedKeys.value = getTreeId(treeData.value)
}, { deep: true, immediate: true })
const openGroup = () => {
visible.value = false;
};
const dragstart = ({ event, node }: { event: DragEvent, node: any }) => {
//event.preventDefault()
event.dataTransfer?.setData('monitorData', JSON.stringify(node.dataRef)); // 使用 text/plain 类型存储节点信息
}
const queryGroup = async (select?: Boolean, searchName?: string) => {
// const params = searchName
// ? {
// sorts: [{ name: 'createTime', order: 'desc' }],
// terms: [
// {
// terms: [
// {
// value: '%' + searchName + '%',
// termType: 'like',
// column: 'name',
// },
// ],
// },
// ],
// }
// : { sorts: [{ name: 'createTime', order: 'desc' }] };
// const req: any = await queryRoleGroup(params);
// if (req.status === 200) {
// listData.value[0].children = req.result;
// if (req.result[req.result.length - 1].id === 'default_group') {
// req.result.unshift(req.result[req.result.length - 1]);
// req.result.pop();
// }
// // if(req.result.length && select){
// // selectGroup(req.result[0].id)
// // }
// }
};
const addGroup = async (data: any, nodeType: any) => {
addName.value = '';
const newId = randomString();
if (!data.children) {
data.children = []
};
data.children?.splice(1, 0, {
isNewChild: true,
nodeName: '',
logicId: props.logicId,
nodeType,
id: newId,
parentId: data.id,
});
treeData.value = [...treeData.value]
selectId.value = newId;
defaultExpandedKeys.value = getTreeId(treeData.value)
await nextTick(() => {
inputRef.value.focus();
});
};
const saveGroup = async (data: any) => {
if (data.nodeName !== addName.value) {
const saveData = {
...data,
nodeName: addName.value,
};
if (data.isNewChild) {
delete data.id;
}
const res = data.isNewChild ? await addMonitor(saveData) : await editMonitor(saveData);
if (res.status === 200) {
onlyMessage('操作成功!');
emit('getMonitorList');
} else {
onlyMessage('操作失败!');
}
} else if (data.isNewChild) {
const removeChild = (child) => {
child.forEach((item, index) => {
if (item.id == data.id) {
child.splice(index, 1);
} else if (item?.children?.length) {
removeChild(item.children)
}
})
}
removeChild(treeData.value)
}
setTimeout(() => {
selectId.value = '';
}, 300);
};
const deleteGroup = async (id: string) => {
const res = await deleteMonitor(id);
if (res.status === 200) {
onlyMessage('操作成功!');
emit('getMonitorList');
} else {
onlyMessage('操作失败!');
}
}
const search = () => {
queryGroup(true, searchValue.value);
};
const searchChange = () => {
if (searchValue.value === '') {
queryGroup();
}
};
const editGroup = (data: any) => {
if (!selectId.value) {
selectId.value = data.id;
addName.value = data.nodeName;
// listData.value[0].children?.forEach((item: any) => {
// if (item.id === data.id) {
// item.edit = true;
// }
// });
nextTick(() => {
inputRef.value.focus();
});
}
};
onMounted(() => {
});
</script>
<style lang="less" scoped>
.left-tree-container {
display: flex;
height: 100%;
flex-direction: column;
.add-btn {
margin: 24px 0;
:deep(.ant-btn-primary) {
width: 100%;
}
}
:deep(.ant-tree-treenode-disabled) {
.ant-tree-node-content-wrapper {
color: rgba(0, 0, 0, 0.55)
}
}
:deep(.ant-tree-treenode) {
width: 100%;
.ant-tree-node-content-wrapper {
flex: 1 1 auto;
.ant-tree-title {
&:hover {
.func-btns {
display: block;
}
}
}
}
}
.tree {
overflow-y: auto;
overflow-x: auto;
flex: 1 1 auto;
.department-tree-item-content {
display: inline-flex;
align-items: stretch;
.title {
flex: 1;
//margin-right: 80px;
}
.func-btns {
display: none;
font-size: 14px;
width: 100px;
margin-left: -80px;
:deep(.ant-btn-link) {
padding: 0 4px;
height: 24px;
}
}
}
.loading {
display: flex;
width: 100%;
justify-content: center;
margin-top: 20px;
}
}
}
:deep(.ant-btn-primary) {
width: 100%;
}
::-webkit-scrollbar {
height: 6px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
background: #f2f2f2;
border-radius: 4px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
background: #cecece;
border-radius: 4px;
}
</style>