Browse Source

feat: tree组件

master_tdsql
ruancuihong 1 year ago
parent
commit
2b504213f5
  1. 428
      src/views/aiSupervision/fourPredictions/warnInfo/components/Tree.vue
  2. 70
      src/views/aiSupervision/fourPredictions/warnInfo/components/forecastDetail.vue
  3. 452
      src/views/aiSupervision/fourPredictions/warnInfo/detail/PlanDetail.vue

428
src/views/aiSupervision/fourPredictions/warnInfo/components/Tree.vue

@ -0,0 +1,428 @@
<template>
<div class="tree-wrapper">
<div class="tree-filter" v-if="showInput">
<el-input placeholder="请输入搜索关键字" v-model="filterText" maxlength="30" show-word-limit clearable></el-input>
<slot name="filter-btns"></slot>
</div>
<el-tree ref="tree" class="tree-main" :class="{ 'no-checkbox': noCheckbox }" :node-key="nodeKey" :data="data"
:props="defaultProps" :filter-node-method="filterNode" :default-expand-all="defaultExpandAll"
:emptyText="emptyText" :renderAfterExpand="renderAfterExpand" :checkStrictly="checkStrictly"
:expandOnClickNode="expandOnClickNode" :checkOnClickNode="checkOnClickNode"
:checkDescendants="checkDescendants" :autoExpandParent="autoExpandParent"
:defaultCheckedKeys="defaultCheckedKeys" :defaultExpandedKeys="defaultExpandedKeys"
:showCheckbox="showCheckbox" :lazy="lazy" :load="load" :draggable="draggable" :allowDrag="allowDrag"
:allowDrop="allowDrop" :highlightCurrent="highlightCurrent" :accordion="accordion" :indent="indent"
:iconClass="iconClass" :renderContent="renderContent" @node-click="handleNodeClick"
@node-expand="handleExpand" @node-collapse="handleCollapse">
<template slot-scope="{ node, data }">
<div class="custom-tree-node" :class="node.checked || node.indeterminate ? '' : 'opacity'">
<div class="left">
<!-- <iconpark-icon v-if="data && data[defaultProps.children]" class="icon" name="organization"
size="16"></iconpark-icon> -->
<slot v-bind="{ node, data }"></slot>
</div>
<div class="right">
<slot name="right" v-bind="{ node, data }"></slot>
<span class="more" @mouseleave="moreMenuLeave">
<slot name="more" v-bind="{ node, data, onMoreHandle }"></slot>
</span>
</div>
</div>
</template>
</el-tree>
<div class="more-menu-box" ref="moreMenuBox" v-show="isMoreMenuShow" @mouseleave="hideMoreMenu">
<slot name="more-menu" v-bind="{ moreMenuData, hideMoreMenu }"></slot>
</div>
</div>
</template>
<script>
let timer;
export default {
name: "tree",
props: {
data: {
type: Array,
default: () => [],
},
emptyText: {
type: String,
default () {
return "暂无数据";
},
},
renderAfterExpand: {
type: Boolean,
default: true,
},
nodeKey: {
type: String,
default: "key",
},
checkStrictly: {
type: Boolean,
default: false,
},
defaultExpandAll: {
type: Boolean,
default: true,
},
expandOnClickNode: {
type: Boolean,
default: true,
},
checkOnClickNode: {
type: Boolean,
default: false,
},
checkDescendants: {
type: Boolean,
default: false,
},
autoExpandParent: {
type: Boolean,
default: true,
},
defaultCheckedKeys: {
type: Array,
default: () => [],
},
defaultExpandedKeys: {
type: Array,
default: () => [],
},
currentNodeKey: [String, Number],
renderContent: Function,
showCheckbox: {
type: Boolean,
default: false,
},
draggable: {
type: Boolean,
default: false,
},
allowDrag: Function,
allowDrop: Function,
defaultProps: {
default () {
return {
children: "children",
label: "label",
isLeaf: "isLeaf",
};
},
},
lazy: {
type: Boolean,
default: false,
},
highlightCurrent: Boolean,
load: Function,
filterNodeMethod: Function,
accordion: Boolean,
indent: {
type: Number,
default: 18,
},
iconClass: String,
noCheckbox: {
type: Boolean,
default: true,
},
showInput: {
type: Boolean,
default: true
}
},
data () {
return {
filterText: "",
isMoreMenuShow: false,
moreMenuData: null,
moreMenuTriggered: false,
};
},
watch: {
filterText (val) {
this.$refs.tree.filter(val);
},
},
mounted () { },
methods: {
handleNodeClick (data, node, self) {
this.$emit("node-click", data, node, self);
},
handleExpand (data) {
this.$emit("node-expand", data);
},
handleCollapse (data) {
this.$emit("node-collapse", data);
},
filterNode (value, data) {
if (!value || !data || !data[this.defaultProps.label]) return true;
if (this.filterNodeMethod) {
return this.filterNodeMethod.call(null, value, data);
}
return data[this.defaultProps.label].indexOf(value) !== -1;
},
onMoreHandle (event, data) {
this.$set(this, "moreMenuData", data);
this.isMoreMenuShow = true;
this.moreMenuTriggered = true;
this.$nextTick(() => {
this.$refs.moreMenuBox.style.top = event.clientY + 20 + "px";
this.$refs.moreMenuBox.style.left =
event.clientX - this.$refs.moreMenuBox.offsetWidth + 15 + "px";
});
},
hideMoreMenu () {
clearTimeout(timer);
timer = setTimeout(() => {
this.isMoreMenuShow = false;
}, 600);
},
moreMenuLeave () {
if (this.moreMenuTriggered) {
clearTimeout(timer);
timer = setTimeout(() => {
this.isMoreMenuShow = false;
this.moreMenuTriggered = false;
}, 300);
}
},
toggleShow (node, data, keyName) {
const isShow = !data[keyName];
node.data[keyName] = isShow;
if (node.data.children) {
const traverse = function (arr) {
for (let i = 0; i < arr.length; i++) {
arr[i][keyName] = isShow;
if (arr[i].children) {
traverse(arr[i].children);
}
}
};
traverse(node.data.children);
}
let tempNode = node.parent;
let tempTreeData = [];
while (tempNode) {
tempTreeData = tempNode.data;
if (
tempTreeData.children &&
tempTreeData.children.every((f) => f[keyName] === isShow)
) {
tempTreeData[keyName] = isShow;
}
tempNode = tempNode.parent;
}
tempTreeData = JSON.parse(JSON.stringify(tempTreeData));
return tempTreeData;
},
getCheckedKeys (leafOnly) {
return this.$refs?.tree?.getCheckedKeys(leafOnly);
},
setCheckedKeys (keys, leafOnly) {
this.$refs?.tree?.setCheckedKeys(keys, leafOnly);
},
getCheckedNode (leafOnly) {
return this.$refs?.tree?.getCheckedNodes(leafOnly);
},
getNode (data) {
return this.$refs?.tree?.getNode(data);
},
setChecked (key, checked, deep) {
this.$refs?.tree?.setChecked(key, checked, deep);
},
updateKeyChildren (key, data) {
this.$refs?.tree?.updateKeyChildren(key, data);
},
},
beforeDestroy () { },
};
</script>
<style lang="scss" scoped>
.tree-wrapper {
padding: 12px 0;
min-height: 350px;
::v-deep.tree-filter {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 16px;
margin-bottom: 8px;
.el-input__inner,
.el-textarea__inner {
// color: #fff;
&::placeholder {
color: rgba(255, 255, 255, 0.35);
font-size: 12px;
}
}
.el-input__count .el-input__count-inner {
background: transparent;
}
.el-input__inner {
//background-color: transparent;
border-color: transparent;
}
.is-disabled {
background-color: transparent;
}
}
::v-deep .el-input {
.el-input__inner {
height: 32px;
line-height: 32px;
background-color: #444550;
color: rgb(38, 38, 38);
border: none;
padding-left: 8px;
}
.el-input__count-inner {
background: #444550;
color: rgba(255, 255, 255, 0.35);
}
.el-input__icon {
line-height: 28px;
}
}
::v-deep.tree-main {
background: none;
padding: 0 16px;
.el-tree-node__content {
height: 34px;
line-height: 34px;
margin-bottom: 4px;
display: flex;
align-items: center;
justify-content: flex-start;
}
.el-tree-node:focus>.el-tree-node__content,
.el-tree-node__content:hover {
background: #ebf7f5;
color: rgb(54, 178, 158) !important;
// border-right: 3px solid #36b29e;
}
//.el-tree-node__expand-icon {
// color: rgba(255, 255, 255, 0.55);
// }
.el-tree-node__expand-icon.is-leaf {
color: transparent;
cursor: default;
}
.custom-tree-node {
display: flex;
justify-content: space-between;
align-items: center;
// color: rgb(38, 38, 38);
// width: 100%;
// padding-right: 8px;
font-family: "思源黑体";
font-size: 14px;
font-weight: normal;
letter-spacing: 0em;
flex: 1;
.left,
.right {
display: flex;
justify-content: space-between;
align-items: center;
}
.icon {
margin-right: 8px;
}
.name {
// flex: 1;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.btn-item {
margin-right: 9px;
display: flex;
justify-content: center;
align-items: center;
&:hover {
background: rgba(255, 255, 255, 0.16);
}
}
}
.custom-tree-node.opacity {
// opacity: 0.5;
}
&.no-checkbox {
.el-tree-node__content {
.el-checkbox {
display: none;
}
}
}
}
.more-menu-box {
position: fixed;
background-color: #4d4f5b;
border-radius: 4px;
z-index: 1000;
padding: 8px;
border: 0.5px solid rgba(255, 255, 255, 0.16);
box-shadow: 0px 3px 14px 2px rgba(0, 0, 0, 0.05),
0px 8px 10px 1px rgba(0, 0, 0, 0.06), 0px 5px 5px -3px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: 4px;
flex-grow: 1;
.menu-item {
min-width: 104px;
height: 28px;
display: flex;
flex-direction: row;
align-items: center;
padding: 3px 8px;
gap: 8px;
align-self: stretch;
border-radius: 4px;
font-family: "思源黑体";
font-size: 14px;
font-weight: normal;
line-height: 22px;
letter-spacing: 0em;
color: rgb(38, 38, 38);
cursor: pointer;
&:hover {
background: rgba(0, 0, 0, 0.1216);
}
&.red {
color: #d54941;
}
}
}
}
</style>

70
src/views/aiSupervision/fourPredictions/warnInfo/components/forecastDetail.vue

@ -0,0 +1,70 @@
<template>
<div class="forecast-detail-wrapper">
<div class="detail-title">预报详情</div>
<div class="select-content">
<span>预报类型</span>
<el-select v-model="selectValue" placeholder="请选择">
<el-option v-for="item in selectOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
<el-radio-group v-model="radioValue">
<el-radio v-for="item in radioOptions" :key="item.label" :label="item.label">{{ item.name }}</el-radio>
</el-radio-group>
</div>
<div class="analysis-content">
<div class="echarts-content" id="chart"></div>
<div class="warn-analysis">
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
selectOptions: [{
value: '1',
label: '工程水位预报'
}, {
value: '2',
label: '河道水位预报'
}, {
value: '3',
label: '工程降雨量预报'
}, {
value: '4',
label: '水库出库流量预报'
}, {
value: '5',
label: '流域来水量预报'
}, {
value: '6',
label: '河道流量预报'
}],
selectValue: '1',
radioOptions: [{
label: 1,
name: '飞来峡水库'
}, {
label: 2,
name: '龙潭水库'
}],
radioValue: 1
};
},
methods: {
handleNodeClick (data) {
console.log(data);
}
}
};
</script>
<style lang="scss" scoped>
.forecast-detail-wrapper {
width: 100%;
height: 100%;
}
</style>

452
src/views/aiSupervision/fourPredictions/warnInfo/detail/PlanDetail.vue

@ -1,321 +1,163 @@
<template>
<div class="warn-wrapper">
<!-- 预警信息管理 -->
<Tree :data="treeData" :defaultProps="defaultProps" :showInput="false" :default-checked-keys="defaultCheckedKeys"
show-checkbox no-checkbox node-key="id" ref="warnTreeRef" class="warn-tree">
<template v-slot:default="{ node, data }">
<span class="name">{{ data.name }}</span>
</template>
<template v-slot:right="{ node, data }">
<el-dropdown @command="handleCommand($event, data)" v-if="data && !data.children">
<span class="btn-item">
<i class="el-icon-more"></i>
</span>
<template #dropdown>
<el-dropdown-menu class="dropdown-menu">
<el-dropdown-item class="right-menu" command="infoSetting">
预演信息配置
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</Tree>
<forecastDetail></forecastDetail>
</div>
</template>
<script>
import { getAreasData } from "@/api/areas/index";
import Edit from "./edit.vue";
import { ENGINTYPE, PLANLIST } from "../../const";
let that;
import forecastDetail from '../components/forecastDetail.vue'
import Tree from '../components/Tree.vue'
export default {
name: "PlanDetail",
components: {
Edit
},
data() {
components: { Tree, forecastDetail },
data () {
return {
searchForm: {
enginType: "1",
adcd: "",
value: ""
},
dialog: {
title: "新增预案",
dom: "",
visible: false,
},
mdl: null,
enginTypeList: ENGINTYPE, //
areasOptions: [],
areasOptionProps: {
emitPath: false,
checkStrictly: true, //
},
tableData: [],
pageData: {
pageNum: 1, //
pageSize: 10, //
pageSizes: [10, 20, 50, 100],
total: 0, //
treeData: [
{
"id": "1752932766908338178",
"parentId": "root",
"name": "全部",
"userId": 1,
"children": [
{
"id": "1759857493740810240",
"parentId": "1752932766908338178",
"name": "2024年预报方案",
"userId": 1,
"children": [
{
"id": "1759858444149125122",
"name": "4月12号",
"level": 2,
"children": [
{
"id": "1759858444149125142",
"name": "预报方案1",
"level": 3
},
{
"id": "1759458444149125142",
"name": "预报方案2",
"level": 3
}
]
},
{
"id": "1776817687412244481",
"name": "4月13号",
"level": 2,
"children": [
{
"id": "1759858444142125142",
"name": "预报方案1",
"level": 3
},
{
"id": "1759468444149125142",
"name": "预报方案2",
"level": 3
}
]
}
],
"level": 1
},
{
"id": "1759857493700810240",
"parentId": "1752932766908338178",
"name": "2023年预报方案",
"userId": 1,
"children": [
{
"id": "1759858444109125122",
"name": "4月15号",
"level": 2,
"children": [
{
"id": "1759858484149125142",
"name": "预报方案1",
"level": 3
},
{
"id": "1759458444142125142",
"name": "预报方案2",
"level": 3
}
]
},
{
"id": "1776817687412244481",
"name": "4月17号",
"level": 2,
"children": [
{
"id": "1759858544142125142",
"name": "预报方案1",
"level": 3
},
{
"id": "1759468444749125142",
"name": "预报方案2",
"level": 3
}
]
}
],
"level": 1
}
],
"level": 0
}
],
defaultProps: {
children: "children",
label: "name",
// isLeaf: "isLeaf",
},
defaultCheckedKeys: [],
draw: null,
_poiTreeData: {},
checkedKeys: [],
entityFeatures: [],
childArr: [],
};
},
created() {
that = this;
},
filters: {
//
filterembankment(price) {
const data = that.enginTypeList.filter((res) => res.dictValue == price);
return data[0] ? data[0].dictLabel : "/";
},
},
methods: {
handleGoManage(row) {
this.$router.push({
path: "planInfoDetail",
query: { id: row.id },
});
},
handleCurrentPageChange(page) {
this.pageData.pageNum = page;
this.getTableData();
},
handlePageSizeChange(pageSize) {
this.pageData.pageSize = pageSize;
this.getTableData();
},
search() {
this.pageData.pageNum = 1;
this.getTableData();
},
//
resetSearch() {
this.pageData.pageNum = 1;
if (!this.$refs["searchForm"]) return;
this.$refs["searchForm"].resetFields();
this.getTableData();
},
handleAdd() {
this.dialog.dom = "Edit";
this.dialog.visible = true;
},
handleCheck(row) {
this.dialog.dom = "Edit";
this.mdl = { ...row };
this.dialog.visible = true;
},
handleEdit(row) {
this.dialog.dom = "Edit";
this.mdl = {
eventType: "edit",
...row,
};
this.dialog.visible = true;
},
handleDelete() {
this.$message.success("删除成功");
},
submitForm() {
console.log(123)
this.$refs.component.submitForm(async (from) => {
if (this.mdl) {
this.$message.success("修改成功");
this.closeDialog();
} else {
this.$message.success("新增成功");
this.closeDialog();
}
})
},
// dialog
closeDialog() {
this.dialog.visible = false;
this.mdl = null;
},
//
getTreeData() {
getAreasData().then((items) => {
this.adcdOptions = items.data;
if (items?.data) {
let res = [];
let getChildren = (res, pid) => {
for (const i of items.data) {
if (i.parentid === pid) {
const newItem = {
label: i.name,
value: i.id,
};
if (i.layer != 3) newItem.children = [];
res.push(newItem);
getChildren(newItem.children, newItem.value);
}
}
};
getChildren(res, items.data[0].parentid);
this.areasOptions = res;
}
});
},
//
getTableData() {
console.log(this.searchForm);
const list = PLANLIST.filter(res => {
let filter = true
//
if (this.searchForm.value) {
if (!res.planName.includes(this.searchForm.value)) {
filter = false
}
}
if (filter) return res
})
this.tableData = list
handleCommand (node, data) {
console.log(node, data, 'node, data');
}
},
async mounted() {
this.getTreeData()
this.getTableData()
},
};
</script>
<template>
<div class="slider-right">
<div class="table-box">
<el-form
inline
:model="searchForm"
ref="searchForm"
class="demo-ruleForm"
>
<el-form-item prop="value">
<el-input
v-model="searchForm.value"
class="search-input"
placeholder="请输入名称"
></el-input>
</el-form-item>
<el-form-item>
<el-button class="search-btn" type="success" @click="search"
>查询</el-button
>
</el-form-item>
</el-form>
<el-button
class="search-btn"
style="margin-right: 16px; margin-bottom: 8px; float: right"
type="success"
@click="handleAdd()"
>新增
</el-button>
<el-table height="625" :data="tableData" border>
<el-table-column type="index" align="center" label="序号" width="100">
</el-table-column>
<el-table-column prop="planName" align="center" label="预案名称">
</el-table-column>
<el-table-column prop="planType" align="center" label="预案类型">
</el-table-column>
<el-table-column prop="director" align="center" label="负责人">
</el-table-column>
<el-table-column prop="correlation" align="center" label="关联对象">
</el-table-column>
<el-table-column prop="updateTime" align="center" label="最近开展时间">
</el-table-column>
<el-table-column
prop="address"
align="center"
label="操作"
min-width="200"
>
<template slot-scope="scope">
<el-button
style="margin-right: 16px"
@click="handleCheck(scope.row)"
type="text"
size="small"
>
查看
</el-button>
<el-button
style="margin-right: 16px"
@click="handleEdit(scope.row)"
type="text"
size="small"
>
编辑
</el-button>
<el-popconfirm
confirm-button-text="确定"
cancel-button-text="取消"
icon="el-icon-info"
icon-color="red"
title="确定删除吗?"
@confirm="handleDelete(scope.row)"
>
<el-button
style="color: red"
type="text"
size="small"
slot="reference"
>删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-pagination
background
class="pagination"
style="margin-top: 16px; margin-right: 16px; float: right"
:current-page="pageData.pageNum"
:page-sizes="pageData.pageSizes"
layout="total, prev, pager, next, sizes, jumper"
:total="pageData.total"
@current-change="(e) => handleCurrentPageChange(e)"
@size-change="(e) => handlePageSizeChange(e)"
>
</el-pagination>
</div>
<el-dialog
:title="dialog.title"
@close="closeDialog"
:visible.sync="dialog.visible"
width="50%"
>
<component
v-if="dialog.visible"
:is="dialog.dom"
ref="component"
:model="mdl"
></component>
<div slot="footer" class="dialog-footer">
<el-button size="mini" @click="closeDialog"> </el-button>
<el-button size="mini" type="primary" @click="submitForm"
>保存
</el-button>
</div>
</el-dialog>
</div>
</template>
<style scoped lang="less">
.table-box {
<style lang="scss" scoped>
.warn-wrapper {
width: 100%;
height: calc(100% - 50px - 24px);
margin-top: 24px;
padding: 16px;
background-color: white;
.search-input {
width: 300px;
margin-right: 10px;
}
.search-btn {
margin-left: 10px;
background-color: #37b29e;
border: none;
&:hover {
background-color: #5ac6b9;
}
&:active {
background-color: #2b8070;
}
}
height: 100%;
display: flex;
justify-content: space-between;
}
.search-btn {
margin-left: 10px;
background-color: #37b29e;
border: none;
&:hover {
background-color: #5ac6b9;
}
&:active {
background-color: #2b8070;
}
.warn-tree {
width: 350px !important;
}
</style>

Loading…
Cancel
Save