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

729 lines
21 KiB

<template>
<div class="app-container">
<div class="el-header">
<el-form :inline="true" ref="mainForm"
:model="formData"
:rules="formRules"
class="demo-form-inline">
<!-- 服务器配置部分 -->
<el-form-item label="数据服务器IP:" prop="sqlseverIp">
<el-input v-model="formData.sqlseverIp"></el-input>
</el-form-item>
<el-form-item label="登录名" prop="userName">
<el-input v-model="formData.userName"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="formData.password" type="password"></el-input>
</el-form-item>
<el-form-item label="数据库名称:" prop="dataBase">
<el-input v-model="formData.dataBase"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="testConnection">测试连接</el-button>
</el-form-item>
</el-form>
</div>
<div class="el-left">
<div class="table-title">南方片数据交互</div>
<el-form>
<el-form-item style="margin-left: 15%" label="交互类型:" prop="softTypeValue">
<el-select style="margin-left: 5%" v-model="formData.softTypeValue" clearable placeholder="请选择交互类型">
<el-option
v-for="item in softType"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item style="margin-left: 15%" label="开始时间:" prop="startTime">
<el-date-picker
style="margin-left: 5%"
v-model="formData.startTime"
type="month"
placeholder="选择日期">
</el-date-picker>
</el-form-item>
<el-form-item style="margin-left: 15%" label="结束时间:" prop="endTime">
<el-date-picker
style="margin-left: 5%"
v-model="formData.endTime"
type="month"
placeholder="选择日期">
</el-date-picker>
</el-form-item>
<el-form-item label="类型:" style="margin-left: 15%" prop="stationTypeValue">
<el-select style="margin-left: 12%" v-model="formData.stationTypeValue" clearable placeholder="请选择类型">
<el-option
v-for="item in stationType"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button style="float: right" type="primary" @click="submitForm">提交</el-button>
</el-form-item>
</el-form>
</div>
<div class="el-middle">
<div class="table-title">可选测站</div>
<div style="width: 60%; margin-bottom: 5px; margin-left: 22%">
<el-input placeholder="请输入站点名称" v-model="filterText" clearable />
</div>
<!-- 新增滚动容器 -->
<div style="margin-left: 20%" class="tree-scroll-container">
<el-tree
ref="tree"
:data="options"
:props="props"
node-key="value"
:filter-node-method="filterNode"
@check-change="handleCheckChange"
:highlight-current="true"
default-expand-all
show-checkbox
/>
</div>
</div>
<div class="el-main">
<el-table
:data="tableData"
ref="treeTable"
:row-class-name="getRowClassName"
row-key="id"
:tree-props="{children: 'children'}"
@select="handleSelect"
@select-all="handleSelectAll"
default-expand-all
border
>
<!-- 多选列 -->
<el-table-column
type="selection"
width="55"
:reserve-selection="true">
</el-table-column>
<!-- 树形内容列 -->
<el-table-column label="所有表项">
<template slot-scope="{ row }">
<span :style="{ paddingLeft: (row.level * 18) + 'px' }">
{{ row.label }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import {treeselect} from "@/api/system/dept";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import stationApi from "@/api/basic/station";
import dayRainApi from "@/api/day/dayRain";
export default {
data() {
return {
formData: {
sqlseverIp: '',
userName: '',
password: '',
dataBase: '',
softTypeValue: '',
startTime: '',
endTime: '',
stationTypeValue: '',
selectedValues: []
},
// 表单验证规则
formRules: {
sqlseverIp: [
{required: true, message: '请输入数据服务器IP', trigger: 'blur'}
],
userName: [
{required: true, message: '请输入登录名', trigger: 'blur'}
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'}
],
dataBase: [
{required: true, message: '请输入数据库名称', trigger: 'blur'}
],
softTypeValue: [
{required: true, message: '请选择交互类型', trigger: 'change'}
],
startTime: [
{required: true, message: '请选择开始时间', trigger: 'change'}
],
endTime: [
{required: true, message: '请选择结束时间', trigger: 'change'}
],
stationTypeValue: [
{required: true, message: '请选择类型', trigger: 'change'}
],
selectedValues: [
{required: true, message: '请选择站点', trigger: 'change'}
]
},
isAllSelected: false,
tableData: [
{
id: 1,
label: '基本信息',
parentId: null,
level: 0,
checked: false,
indeterminate: false, // 添加半选状态标识
children: [
{id: 11, parentId: 1, label: '测站信息一览表', level: 1, checked: false},
]
},
{
id: 2,
label: '雨量资料',
level: 0,
parentId: null,
checked: false,
indeterminate: false, // 添加半选状态标识
children: [
{id: 21, parentId: 2, label: '逐日降水表', level: 1, checked: false},
{id: 22, parentId: 2, label: '各时段最大降水表(1)', level: 1, checked: false},
{id: 23, parentId: 2, label: '各时段最大降水表(2)', level: 1, checked: false},
{id: 24, parentId: 2, label: '降水量摘录表', level: 1, checked: false},
]
},
{
id: 3,
label: '水位资料',
level: 0,
parentId: null,
checked: false,
indeterminate: false, // 添加半选状态标识
children: [
{id: 31, parentId: 3, label: '逐日平均水位表', level: 1, checked: false},
{id: 32, parentId: 3, label: '水闸洪水水文要素摘录表', level: 1, checked: false},
{id: 33, parentId: 4, label: '逐潮高低潮位表', level: 1, checked: false},
{id: 34, parentId: 4, label: '潮位月年统计表', level: 1, checked: false},
]
},
{
id: 5,
label: '流量资料',
level: 0,
parentId: null,
checked: false,
indeterminate: false, // 添加半选状态标识
children: [
{id: 51, parentId: 5, label: '逐日平均流量表', level: 1, checked: false},
]
},
],
softType: [{
value: '0',
label: '导出数据到云服务数据库'
}, {
value: '1',
label: '导入数据到南方片数据库'
}],
softTypeValue: '',
stationType: [
{
value: 'P',
label: '雨量站'
},
{
value: 'H',
label: '水文站'
},
{
value: 'Q',
label: '水位站'
}
],
stationTypeValue: '',
props: {
multiple: true,
checkStrictly: true,// 允许独立选择节点
value: 'value', // 确保与数据源字段名一致
label: 'label',
disabled: 'disabled'
},
componentKey: 0,
options: [],
stcds: '',
filterText:"",
selectedValues: [], // 绑定选中值
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 弹出层标题
title: "",
// 表单参数
form: {},
// 表单校验
rules: {},
dateYear: [],
deptOptions: undefined,
isHandlingCheckChange: false
};
},
created() {
},
watch: {
filterText(val) {
this.$refs['tree'].filter(val);
},
'formData.startTime': {
handler(newVal) {
this.checkAndFetchData(newVal, this.formData.endTime, this.formData.stationTypeValue);
},
immediate: true // 初始立即执行
},
'formData.endTime'(newVal) {
this.checkAndFetchData(this.formData.startTime, newVal, this.formData.stationTypeValue);
},
'formData.stationTypeValue'(newVal) {
this.checkAndFetchData(this.formData.startTime, this.formData.endTime, newVal);
}
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
handleCheckChange(data, checked, indeterminate) {
// 防止递归调用
if (this.isHandlingCheckChange) return;
this.isHandlingCheckChange = true;
try {
// 获取当前所有选中的节点(包括非叶子节点)
const checkedKeys = this.$refs.tree.getCheckedKeys();
// 情况1:当前选中了"all"节点
if (data.value === 'all' && checked) {
// 只保留"all"节点
this.$refs.tree.setCheckedKeys(['all']);
this.stcds = 'all';
}
// 情况2:当前选中了其他节点(非all)
else if (checked && data.value !== 'all') {
// 如果已存在"all"节点,先移除
if (checkedKeys.includes('all')) {
const newCheckedKeys = checkedKeys.filter(key => key !== 'all');
this.$refs.tree.setCheckedKeys(newCheckedKeys);
this.stcds = newCheckedKeys.join(",");
} else {
// 直接更新为当前选中的节点
this.stcds = this.$refs.tree.getCheckedKeys(true).join(",");
}
}
// 情况3:取消选中操作
else {
this.stcds = this.$refs.tree.getCheckedKeys(true).join(",");
}
} finally {
this.isHandlingCheckChange = false;
}
},
getRowClassName({row}) {
if (row.indeterminate) {
return 'indeterminate-row'; // 应用半选样式
}
return '';
},
// 查找父节点
findParent(parentId) {
if (!parentId) return null;
// 广度优先搜索父节点
const queue = [...this.tableData];
while (queue.length) {
const node = queue.shift();
if (node.id === parentId) return node;
if (node.children) queue.push(...node.children);
}
return null;
},
// 1. 单行选择处理(父子联动)
handleSelect(selection, row) {
if (row.indeterminate && !selection.includes(row)) {
this.clearIndeterminateState(row); // 新增方法
} else {
row.checked = selection.includes(row);
// 处理子节点
if (row.children) {
this.toggleChildren(row, row.checked);
}
}
// 更新父节点状态
this.updateParentStatus(row);
},
// 新增:清除半选节点及子节点状态
clearIndeterminateState(node) {
// 重置当前节点状态
node.checked = false;
node.indeterminate = false;
this.$refs.treeTable.toggleRowSelection(node, false);
// 递归清除子节点
if (node.children) {
node.children.forEach(child => {
this.clearIndeterminateState(child);
});
}
},
// 2. 递归设置子节点状态
toggleChildren(node, isSelected) {
if (node.children) {
node.children.forEach(child => {
// 设置子节点选中状态
this.$refs.treeTable.toggleRowSelection(child, isSelected);
child.checked = isSelected;
// 递归处理孙子节点
if (child.children) this.toggleChildren(child, isSelected);
});
}
},
// 3. 更新父节点状态(关键修复)
// 在 updateParentStatus 中强化半选处理
updateParentStatus(childNode) {
const parent = this.findParent(childNode.parentId);
if (!parent) return;
const checkedCount = parent.children.filter(c => c.checked).length;
const indeterminateCount = parent.children.filter(c => c.indeterminate).length; // 新增
// 强化半选判断逻辑
parent.checked = checkedCount === parent.children.length;
parent.indeterminate =
(checkedCount > 0 && checkedCount < parent.children.length) ||
indeterminateCount > 0; // 关键修复[7](@ref)
// 更新UI
this.$refs.treeTable.toggleRowSelection(parent, parent.checked);
this.$set(parent, 'indeterminate', parent.indeterminate);
// 半选状态特殊处理
if (parent.indeterminate && !parent.checked) {
// 强制清除可能残留的半选状态
parent.children.forEach(child => {
if (child.indeterminate) {
this.clearIndeterminateState(child);
}
});
}
},
// 4. 全选/全不选处理(修复版)
handleSelectAll(selection) {
const isAllSelected = selection.length > 0;
// 深度递归设置所有节点状态
const setAllSelection = (nodes, status) => {
nodes.forEach(node => {
// 设置当前节点状态
node.checked = status;
node.indeterminate = false; // 清除半选状态
this.$refs.treeTable.toggleRowSelection(node, status);
// 递归处理子节点
if (node.children && node.children.length) {
setAllSelection(node.children, status);
}
});
};
setAllSelection(this.tableData, isAllSelected);
// 特殊处理:取消全选时强制清空选中项
if (!isAllSelected) {
this.$nextTick(() => {
this.$refs.treeTable.clearSelection(); // 关键修复[1,3](@ref)
});
}
},
formatDateToYearMonth(date) {
if (date == null) {
return '';
}
const d = new Date(date);
if (isNaN(d.getTime())) {
return '';
}
const year = d.getFullYear();
const month = (d.getMonth() + 1).toString().padStart(2, '0');
return `${year}-${month}`;
},
// 检查是否满足查询条件
checkAndFetchData(start, end, stationTypeValue) {
if (start && end && stationTypeValue) {
this.componentKey++;
this.options = []
this.formData.selectedValues = []
this.getStaionList(start, end, stationTypeValue);
}
},
async testConnection() {
const res = await stationApi.testSqlConnection(this.formData)
if (res.code === 0) {
this.$modal.msgSuccess(res.data)
} else {
this.$modal.msgError("链接失败")
}
},
async getStaionList(start, end, type) {
const res = await stationApi.getStnmAndStcdByType(this.formatDateToYearMonth(start),
this.formatDateToYearMonth(end), type);
// 新增数据处理逻辑
const processTree = (nodes) => {
return nodes.map(node => {
const hasChildren = node.children && node.children.length > 0;
return {
...node,
disabled: hasChildren, // 有子节点的父类自动禁用
children: hasChildren ? processTree(node.children) : null
};
});
};
this.options = processTree([res.data]); // 注意保持数组结构
},
submitForm() {
this.$refs.mainForm.validate(valid => {
if (valid) {
// 所有验证通过,执行提交操作
this.executeSubmit(this.formData);
} else {
// 验证失败,显示错误提示
this.$message.error('请填写所有必填项');
return false;
}
});
// this.executeSubmit(this.formData);
},
getFilteredSelection() {
const allSelected = this.$refs.treeTable.selection; // 获取所有选中节点[1](@ref)
const result = [];
// 遍历选中节点,按规则过滤
allSelected.forEach(node => {
const isParent = node.children && node.children.length > 0;
// 规则1:父节点被选中时,忽略其所有子节点
if (isParent && allSelected.some(n => n.parentId === node.id)) {
return; // 跳过子节点
}
// 规则2:子节点被选中时保留
result.push(node);
});
return result;
},
executeSubmit(formData) {
const filteredData = this.getFilteredSelection();
const ids = filteredData.length
? filteredData.map(item => item.id).join(',')
: '';
const params = {
sqlseverIp: this.formData.sqlseverIp,
userName: this.formData.userName,
password: this.formData.password,
dataBase: this.formData.dataBase,
startTime: this.formatDateToYearMonth(this.formData.startTime),
endTime: this.formatDateToYearMonth(this.formData.endTime),
softTypeValue: this.formData.softTypeValue,
stationTypeValue: this.formData.stationTypeValue,
stcds: this.stcds,
ids: ids
}
stationApi.importDateToSoftOrExportSoft(params).then(res => {
if (res.code === 0) {
this.$modal.msgSuccess(res.msg);
} else {
this.$modal.msgError(res.msg);
}
});
},
// 取消按钮
cancel() {
this.open = false;
this.softOpen = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
startTime: '',
endTime: ''
}
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.page = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
},
mounted() {
}
};
</script>
<style scoped lang="scss">
.app-container {
background: #F2F2F2;
.el-header {
border-radius: 4px;
background: #FFFFFF;
color: #333;
padding: 10px;
height: 55px;
box-shadow: 0 0 10px 1px rgba(123, 123, 123, 0.4);
}
//.el-header {
// border-radius: 4px;
// background: #FFFFFF;
// color: #333;
// padding: 10px;
// height: 100px;
// box-shadow: 0 0 10px 1px rgba(123, 123, 123, 0.4);
//}
.el-middle {
.table-title{
font-size: 25px;
font-weight: bold;
width: 100%;
height: 60px;
// line-height: 30px;
text-align: center;
}
border-radius: 4px;
color: #333;
overflow: hidden; /* 改为允许滚动条 */
background: #FFFFFF;
text-align: center;
height: calc(95vh - 120px);
padding: 5px !important;
float: left;
width: 30%;
margin-left: 20px;
margin-top: 20px;
padding-bottom: 15px !important;
box-shadow: 0 0 10px 1px rgba(123, 123, 123, 0.4);
/* 专用滚动容器样式 */
.tree-scroll-container {
height: calc(100% - 40px); /* 减去输入框高度 */
overflow: auto; /* 双向滚动 */
}
/* 修复横向滚动 */
.tree-scroll-container .el-tree {
min-width: 100%;
}
.el-table .el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #409EFF;
border-color: #409EFF;
}
}
.el-left {
.table-title{
font-size: 25px;
font-weight: bold;
width: 100%;
height: 60px;
// line-height: 30px;
text-align: center;
}
border-radius: 4px;
color: #333;
background: #FFFFFF;
text-align: center;
height: calc(95vh - 120px);
padding: 5px !important;
float: left;
width: 25%;
margin-top: 20px;
padding-bottom: 15px !important;
box-shadow: 0 0 10px 1px rgba(123, 123, 123, 0.4);
.el-table .el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #409EFF;
border-color: #409EFF;
}
}
.el-main {
border-radius: 4px;
color: #333;
background: #FFFFFF;
text-align: center;
height: calc(95vh - 120px);
padding: 5px !important;
float: right;
width: 43%;
margin-top: 20px;
padding-bottom: 15px !important;
box-shadow: 0 0 10px 1px rgba(123, 123, 123, 0.4);
.el-table .el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #409EFF;
border-color: #409EFF;
}
}
}
</style>
<!-- 在组件中添加CSS样式 -->
<style>
/* 半选状态样式 */
.el-table .indeterminate-row .el-checkbox__inner {
background-color: #409EFF;
border-color: #409EFF;
}
.el-table .indeterminate-row .el-checkbox__inner::after {
content: "";
position: absolute;
display: block;
background-color: white;
height: 2px;
width: 55%;
top: 50%;
left: 20%;
transform: translateY(-50%);
}
</style>