右侧抽屉调整,表头组件,基本情况介绍,大中型水电开发及环境监测数据接入情况模块添加

This commit is contained in:
王兴凯 2026-03-31 10:14:20 +08:00
parent 4531d76f21
commit a2b9de5038
9 changed files with 919 additions and 123 deletions

View File

@ -1,37 +1,44 @@
<!-- SidePanelItem.vue -->
<template>
<div class="rightContentDrawer">
<div @click="drawerOpen = !drawerOpen" class="drawerController1"
>
<div @click="handleToggle" class="drawerController1" v-if="!drawerOpen">
<img src="../../assets/components/arrow-left.png" alt="">
</div>
<a-drawer :get-container="false"
:style="{ position: 'absolute' }" v-model:open="drawerOpen" :mask="false" placement="right" width="450" :closable="false"
@after-open-change="afterOpenChange" :headerStyle="{ color: '#FAFCFE' }">
<div @click="drawerOpen = !drawerOpen" class="drawerController">
<!-- 使用 Vue Transition 组件控制动画 -->
<transition name="drawer-slide">
<a-drawer
:get-container="false"
:style="{ position: 'relative' }"
v-model:open="drawerOpen"
:mask="false"
placement="right"
width="450"
:closable="false"
:headerStyle="{ color: '#FAFCFE' }">
<div @click="handleToggle" class="drawerController">
<img src="../../assets/components/arrow-right.png" alt="">
</div>
<div style="padding:16px 16px 0">
<slot />
</div>
</a-drawer>
</transition>
</div>
</template>
<script setup>
import { ref, defineOptions, watch } from 'vue';
import { ref, defineOptions } from 'vue';
// 便
// (便)
defineOptions({
name: 'rightDrawer'
});
const drawerOpen = ref(true);
const shujv = ref('123456789');
// const afterOpenChange = (open) => {
// if (!open) {
// drawerOpen.value = false;
// }
// };
const handleToggle = () => {
drawerOpen.value = !drawerOpen.value;
};
</script>
<style lang="scss">
@ -39,19 +46,16 @@ const shujv = ref('123456789');
height: 100%;
position: relative;
box-sizing: border-box;
//
flex-shrink: 0;
.drawerController1 {
width: 18px;
position: absolute;
right: 0px;
right: -2px;
cursor: pointer;
z-index: 10;
height: 88px;
line-height: 88px;
top: 45%;
// left: -18px;
vertical-align: middle;
background-image: url(../../assets/components/bg-toggle.e1dabcf3.svg);
background-repeat: no-repeat;
@ -59,14 +63,16 @@ const shujv = ref('123456789');
justify-content: center;
align-items: center;
}
}
.ant-drawer {
margin: 5px 0px;
.ant-drawer-content{
overflow:visible;
}
.ant-drawer-content-wrapper {
border: 2px solid #c5d6e2 !important;
box-shadow: 3px 3px 3px 9px #e5edf3 !important;

View File

@ -0,0 +1,264 @@
<!-- SidePanelItem.vue -->
<template>
<div class="qgc-side-pannel-item">
<div class="qgc_title">
<div class="title_left">
<span>{{ title }}</span>
<span v-if="prompt.show" class="title_icon">
<a-tooltip placement="top" :title="prompt.value" :get-popup-container="getPopupContainer">
<QuestionCircleOutlined />
</a-tooltip>
</span>
<span v-if="clickprompt.show" class="title_icon">
<a-tooltip placement="top" trigger="click" :title="clickprompt.value"
:get-popup-container="getPopupContainer">
<InfoCircleOutlined />
</a-tooltip>
</span>
</div>
<div class="title_right">
<div v-if="select.show">
<a-select v-model:value="selectValue" show-search placeholder="请选择" style="width: 142px"
:options="select.options" :filter-option="filterOption" @focus="handleFocus" @blur="handleBlur"
@change="handleChange"></a-select>
</div>
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="">
<img v-else src="@/assets/components/arrow-down.png" alt="">
</div>
<div v-if="moreSelect.show">
<a-tree-select v-model:value="moreSelectValue" show-search style="width: 170px"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" placeholder="Please select"
allow-clear tree-default-expand-all :tree-data="moreSelect.options"
tree-node-filter-prop="label">
</a-tree-select>
</div>
<div v-if="datetimePicker.show">
<!-- 添加 locale 属性来设置语言 -->
<a-date-picker v-model:value="datetimeValue" show-time
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
:picker="datetimePicker.picker" placeholder="请选择时间" style="width: 180px"
@change="handleDateTimeChange" :locale="locale" />
<!-- 修改为 locale 变量 -->
</div>
<div v-if="scopeDate.show">
<a-range-picker v-model:value="scopeDateValue" :locale="locale" :picker="scopeDate.picker"
:format="'YYYY-MM-DD'" :range-separator="' 至 '" style="width: 200px" />
</div>
</div>
</div>
<div class="body">
<slot v-if="isExpand" />
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import {
QuestionCircleOutlined,
InfoCircleOutlined
} from '@ant-design/icons-vue';
import type { SelectProps } from 'ant-design-vue';
//
import zhCN from 'ant-design-vue/es/locale/zh_CN';
// dayjs
import dayjs, { Dayjs } from 'dayjs';
import 'dayjs/locale/zh-cn'; //
dayjs.locale('zh-cn'); //
console.log(dayjs().format('MMMM'));
const locale = ref(zhCN);
//
interface PromptConfig {
show: boolean;
value: string;
}
interface SelectConfig {
picker: any;
format: any;
show: boolean;
value: string | undefined;
options: SelectProps['options'];
}
// 便
defineOptions({
name: 'SidePanelItem'
});
// props
const props = defineProps({
title: { //
type: String,
default: ''
},
shrink: { //
type: Boolean,
default: false
},
prompt: { //
type: Object as () => PromptConfig,
default: () => ({
show: false,
value: '',
})
},
clickprompt: { //
type: Object as () => PromptConfig,
default: () => ({
show: false,
value: '',
})
},
select: { //
type: Object as () => SelectConfig,
default: () => ({
show: false,
value: undefined,
options: []
})
},
moreSelect: {//
type: Object as () => SelectConfig,
default: () => ({
show: false,
value: undefined,
options: []
})
},
datetimePicker: { //
type: Object as () => SelectConfig,
default: () => ({
show: false,
value: undefined,
format: null, //YYYY-MM-DD HH
picker: 'date' //date | week | month | quarter | year
})
},
scopeDate: { //
type: Object as () => SelectConfig,
default: () => ({
show: false,
value: undefined,
picker: 'month' //date | week | month | quarter | year
})
},
});
const isExpand = ref(true);
const selectValue = ref(props.select.value)
const moreSelectValue = ref(props.select.value)
const datetimeValue = ref<Dayjs | null>(props.datetimePicker.value ? dayjs(props.datetimePicker.value) : null);
const scopeDateValue = ref(props.scopeDate.value);
// // locale
// const locale = zhCN;
// console.log(locale, "zhCN");
//
const handleChange = (value: string) => {
console.log(`selected ${value}`);
};
const handleBlur = () => {
console.log('blur');
};
const handleFocus = () => {
console.log('focus');
};
const filterOption = (input: string, option?: { value: string }) => {
if (!option) return false;
return option.value.toLowerCase().includes(input.toLowerCase());
};
//
const getPopupContainer = (trigger: HTMLElement) => {
return trigger.parentElement;
};
//
const handleDateTimeChange = (date: any | null, dateString: string) => {
console.log('Selected DateTime:', date, dateString);
};
//
onMounted(() => {
console.log(props.select);
});
</script>
<style lang="scss">
.qgc-side-pannel-item {
width: 100%;
display: flex;
justify-content: space-between;
flex-direction: column;
.qgc_title {
width: 100%;
background-color: #e5edf3;
border-radius: 2px;
font-size: 16px;
color: #2f6b98;
line-height: 36px;
padding-left: 16px;
padding-right: 8px;
display: flex;
justify-content: space-between;
position: relative;
font-weight: 500;
.title_shrink {
cursor: pointer;
}
.title_left {
display: flex;
align-items: center;
.title_icon {
display: inline-block;
margin-left: 5px;
cursor: pointer;
}
}
.title_right {
display: flex;
align-items: center;
}
}
.qgc_title:before {
position: absolute;
content: "";
display: inline-block;
left: 0;
width: 2px;
background-color: #005293;
top: 2px;
height: 32px;
border-radius: 3px;
}
.body {
width: 100%;
font-size: 14px;
line-height: 22px;
padding: 16px 0 0;
margin-bottom: 16px;
text-overflow: ellipsis;
overflow: hidden;
height: calc(100% - 36px);
box-sizing: border-box;
p {
text-indent: 2em;
}
}
}
</style>

View File

@ -0,0 +1,141 @@
<!-- SidePanelItem.vue -->
<template>
<div class="basic_body">
<div ref="chartContainer" class="chart-container"></div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
// 便
defineOptions({
name: 'developStatusChart'
});
const chartContainer = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
//
onMounted(() => {
if (chartContainer.value) {
initChart();
}
});
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
});
const initChart = () => {
if (!chartContainer.value) return;
chartInstance = echarts.init(chartContainer.value);
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)',
// position: function (point, params, dom, rect, size) {
// //
// return [size.viewSize.width - 120, point[1]];
// }
},
legend: {
bottom: '5%',
right: '5%',
orient: 'vertical', //
data: ['已建', '在建'],
itemWidth: 14,
itemHeight: 14,
itemStyle: {
borderRadius: 3
},
textStyle: {
fontSize: 11
},
itemGap: 12 //
},
series: [
{
name: '建设状态',
type: 'pie',
radius: ['80%', '95%'], //
center: ['35%', '50%'], //
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
borderColor: '#fff',
borderWidth: 1
},
label: {
show: false //
},
emphasis: {
label: {
show: false
}
},
labelLine: {
show: false // 线
},
data: [
{ value: 80, name: '已建', itemStyle: { color: '#4CAF50' } },
{ value: 20, name: '在建', itemStyle: { color: '#2196F3' } }
]
},
{
name: '中心圆',
type: 'pie',
radius: ['0%', '55%'], //
center: ['35%', '50%'], //
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 0,
borderColor: '#fff',
borderWidth: 0
},
label: {
show: false
},
emphasis: {
label: {
show: false
}
},
labelLine: {
show: false
},
data: [
{ value: 80, name: '已建', itemStyle: { color: '#4CAF50' } },
{ value: 20, name: '在建', itemStyle: { color: '#2196F3' } }
]
}
]
};
chartInstance.setOption(option);
//
window.addEventListener('resize', () => {
chartInstance?.resize();
});
};
</script>
<style lang="scss">
.basic_body {
width: 100%;
height: 100%;
.chart-container {
width: 185px; //
height: 100px; //
}
}
</style>

View File

@ -0,0 +1,89 @@
<!-- engEnvironmentData/index.vue -->
<template>
<div class="eng-environment-data">
<div class="title">监测数据接入情况</div>
<div class="data-list">
<div class="data-item" v-for="item in dataList" :key="item.label">
<div class="item-content">
<span class="color-bar" :style="{ backgroundColor: item.color }"></span>
<span class="label">{{ item.label }}</span>
<span class="value" style=" color: #2f6b98 ">{{ item.value }}</span>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
//
defineOptions({
name: 'EngEnvironmentData'
});
//
const dataList = ref([
{
label: '大中型已建在建电站',
value: '707',
color: '#00b894'
},
{
label: '已接入电站运行数据',
value: '452',
color: '#0984e3'
},
{
label: '已开展全过程监测工作',
value: '42',
color: '#6c5ce7'
}
]);
//
onMounted(() => {
});
</script>
<style lang="scss" scoped>
.eng-environment-data {
padding: 0px 5px;
background: #fff;
border-radius: 4px;
.title {
font-size: 14px;
font-weight: 500;
color: #333;
text-align: center;
}
.data-list {
.data-item {
border: 1px solid #edf2f8;
margin-bottom: 3px;
cursor: pointer;
.item-content {
display: flex;
align-items: center;
padding: 6px 4px;
.color-bar {
width: 1.6px;
height: 14px;
margin-right: 6px;
border-radius: 2px;
}
.label {
flex: 1;
}
}
}
}
}
</style>

View File

@ -0,0 +1,35 @@
<!-- SidePanelItem.vue -->
<template>
<div>
<SidePanelItem title="基本情况介绍" :shrink="true">
<p v-if="title_text" class="basic_body1">{{ title_text }}</p>
<div v-else class="zanwushujv"> <a-empty /></div>
</SidePanelItem>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
// 便
defineOptions({
name: 'jidiInfoMod'
});
const title_text = ref('我国水能资源丰富,主要集中在金沙江、长江上游、雅砻江、黄河上游、大渡河、南盘江-红水河、乌江和西南诸河等流域总技术可开发量约3.81亿kW占全国技术可开发量的55.5%。截至2023年年底雅砻江、金沙江、大渡河、乌江、长江上游、南盘江-红水河等流域已建和在建比例超80%,水能资源开发程度较高,剩余待开发水利资源主要集中在西南诸河,发展潜力巨大。')
//
onMounted(() => {
});
</script>
<style lang="scss">
.zanwushujv {
display: flex;
align-items: center;
justify-content: center;
}
.basic_body1 {
width: 100%;
}
</style>

View File

@ -1,88 +0,0 @@
<!-- SidePanelItem.vue -->
<template>
<div class="rightContentDrawer">
<div @click="drawerOpen = !drawerOpen" class="drawerController1"
:style="drawerOpen ? 'display:none' : 'display:block'">
<img src="../../assets/components/arrow-left.png" alt="">
</div>
<a-drawer v-model:open="drawerOpen" :mask="false" placement="right" width="450" :closable="false"
@after-open-change="afterOpenChange" :headerStyle="{ color: '#FAFCFE' }">
<div @click="drawerOpen = !drawerOpen" class="drawerController">
<img src="../../assets/components/arrow-right.png" alt="">
</div>
<div style="padding:16px 16px 0">
<slot />
</div>
</a-drawer>
</div>
</template>
<script setup>
import { ref, defineOptions, watch } from 'vue';
// 便
defineOptions({
name: 'rightDrawer'
});
const drawerOpen = ref(true);
const shujv = ref('123456789');
// const afterOpenChange = (open) => {
// if (!open) {
// drawerOpen.value = false;
// }
// };
</script>
<style lang="less">
.rightContentDrawer {
height: 100%;
position: relative;
box-sizing: border-box;
//
flex-shrink: 0;
.drawerController1 {
width: 18px;
position: absolute;
right: 0px;
cursor: pointer;
z-index: 10;
height: 88px;
line-height: 88px;
top: 45%;
// left: -18px;
vertical-align: middle;
background-image: url(../../assets/components/bg-toggle.e1dabcf3.svg);
background-repeat: no-repeat;
}
}
.ant-drawer {
margin: 5px 0px;
.ant-drawer-content-wrapper {
border: 2px solid #c5d6e2 !important;
box-shadow: 3px 3px 3px 9px #e5edf3 !important;
}
.ant-drawer-body {
padding: 0px !important;
}
}
.drawerController {
width: 18px;
position: absolute;
right: 10px;
cursor: pointer;
z-index: 10;
height: 88px;
line-height: 88px;
top: 45%;
left: -18px;
vertical-align: middle;
background-image: url(../../assets/components/bg-toggle.e1dabcf3.svg);
background-repeat: no-repeat;
}
</style>

View File

@ -0,0 +1,297 @@
<!-- DataTable.vue -->
<template>
<div class="data-table-container">
<a-table :columns="columns" :data-source="tableData" :pagination="false" size="middle" :customRow="customRow"
class="custom-table">
</a-table>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
//
defineOptions({
name: 'DataTable'
});
//
const columns: ColumnsType = [
{
title: '基地名称',
dataIndex: 'name',
key: 'name',
fixed: 'left',
// width: 65,
align: 'left'
},
{
title: '装机容量 (万 kW)',
key: 'capacity',
align: 'center',
children: [
{
title: '总计',
dataIndex: 'total',
key: 'total',
width: 65,
align: 'center'
},
{
title: '已建',
dataIndex: 'built',
key: 'built',
width: 65,
align: 'center'
},
{
title: '在建',
dataIndex: 'building',
key: 'building',
width: 65,
align: 'center'
},
{
title: '未建',
dataIndex: 'unbuilt',
key: 'unbuilt',
width: 65,
align: 'center'
},
]
}
];
//
const tableData = ref([
{
key: '1',
name: '金沙江干流',
total: '7952.00',
built: '6370.00',
building: '902.00',
unbuilt: '680.00'
},
{
key: '2',
name: '雅砻江干流',
total: '2773.65',
built: '1920.00',
building: '492.00',
unbuilt: '361.65'
},
{
key: '3',
name: '大渡河干流',
total: '2689.65',
built: '1925.65',
building: '534.00',
unbuilt: '230.00'
},
{
key: '4',
name: '乌江干流',
total: '1181.36',
built: '1133.36',
building: '48.00',
unbuilt: '0.00'
},
{
key: '5',
name: '长江上游干流',
total: '3212.65',
built: '2523.65',
building: '0.00',
unbuilt: '689.00'
},
{
key: '6',
name: '湘西',
total: '1054.32',
built: '959.30',
building: '38.52',
unbuilt: '56.65'
},
{
key: '7',
name: '黄河上游干流',
total: '2794.59',
built: '1749.89',
building: '0.00',
unbuilt: '1044.65'
},
{
key: '8',
name: '黄河中游干流',
total: '835.65',
built: '401.65',
building: '0.00',
unbuilt: '434.00'
},
{
key: '9',
name: '南盘江 - 红水河',
total: '1271.00',
built: '1271.00',
building: '0.00',
unbuilt: '0.00'
},
{
key: '10',
name: '东北',
total: '1331.95',
built: '749.05',
building: '0.00',
unbuilt: '582.90'
},
{
key: '11',
name: '澜沧江干流',
total: '2535.00',
built: '2275.00',
building: '260.00',
unbuilt: '0.00'
},
{
key: '12',
name: '怒江干流',
total: '3138.00',
built: '0.00',
building: '102.00',
unbuilt: '3036.00'
},
{
key: '13',
name: '闽浙赣',
total: '962.08',
built: '920.68',
building: '0.00',
unbuilt: '41.40'
},
{
key: '14',
name: '其他',
total: '7460.57',
built: '7273.27',
building: '121.90',
unbuilt: '65.40'
},
{
key: '15',
name: '总计',
total: '7460.57',
built: '7273.27',
building: '121.90',
unbuilt: '65.40'
}
]);
//
const customRow = (record: any, index: number) => {
return {
style: {
backgroundColor: index % 2 === 1 ? '#fafafa' : '#ffffff'
}
};
};
//
onMounted(() => {
});
</script>
<style lang="scss" scoped>
.data-table-container {
padding: 0;
background: #fff;
margin-top: 10px;
}
.custom-table {
:deep(.ant-table) {
font-size: 13px;
border: 1px solid #e8e8e8;
.ant-table-thead {
>tr {
>th {
background-color: #e5eff8 !important;
color: #2f6b98;
font-weight: 600;
border: 1px solid #e8e8e8 !important;
padding: 6px 8px;
text-align: center;
&:first-child {
background-color: #e5eff8 !important;
text-align: left;
}
}
&:first-child {
>th {
background-color: #e5eff8 !important;
&:first-child {
background-color: #e5eff8 !important;
}
}
}
}
}
.ant-table-tbody {
>tr {
>td {
border: 1px solid #e8e8e8;
padding: 6px 8px;
text-align: center;
&:first-child {
text-align: left;
font-weight: 500;
}
}
&:hover {
background-color: #e6f7ff !important;
}
}
}
.ant-table-footer {
padding: 0;
background-color: #fafafa;
.table-footer {
.footer-row {
display: flex;
align-items: center;
padding: 12px 8px;
border-top: 1px solid #e8e8e8;
background-color: #fafafa;
.footer-item {
flex: 1;
text-align: center;
font-weight: 600;
color: #333;
&:first-child {
flex: 0 0 140px;
text-align: center;
}
}
}
}
}
// footer
.ant-table-footer::before {
display: none;
}
}
}
</style>

View File

@ -0,0 +1,53 @@
<!-- SidePanelItem.vue -->
<template>
<div class="basic_body">
<SidePanelItem title="大中型水电开发及环境监测数据接入情况" :prompt="prompt">
<div>
<div class="body_top">
<!-- {/* 水电开发情况图表 */} -->
<div style="flex: 1;">
<div>水电开发情况</div>
<div style="color: #757575;font-size: 12px;line-height: 16px;">图释数量/装机容量</div>
<div>
<DevelopStatusChart />
</div>
</div>
<!-- style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center" }} -->
<div style="flex: 1;display: flex; flex-direction: column; align-items: center;">
<EngEnvironmentData />
</div>
</div>
<DataTable />
</div>
<!-- <div v-else class="zanwushujv"> <a-empty /></div> -->
</SidePanelItem>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import DevelopStatusChart from "@/components/developStatusChart/index.vue"
import EngEnvironmentData from "@/components/engEnvironmentData/index.vue"
import DataTable from "./DataTable.vue"
// 便
defineOptions({
name: 'shuidianhuangjingjieruMod'
});
const prompt = ref({
show: true,
value: '统计大中型已建,在建水电站',
})
//
onMounted(() => {
});
</script>
<style lang="scss">
.body_top {
display: flex;
justify-content: space-between;
}
</style>

View File

@ -1,6 +1,8 @@
<script setup lang="ts">
import JidiSelectorMod from "@/modules/jidiSelectorMod.vue";
import RightDrawer from "@/components/RightDrawer/index.vue";
import jidiInfoMod from '@/modules/jidiInfoMod/index.vue';
import shuidianhuangjingjieruMod from '@/modules/shuidianhuangjingjieruMod/index.vue';
</script>
<template>
@ -11,11 +13,8 @@ import RightDrawer from "@/components/RightDrawer/index.vue";
</div>
<div class="rightContent">
<RightDrawer>
<!-- 789231 -->
<!-- <SidePanel title={currentItem ? currentItem.basename : ""}>
<JidiInfoMod />
<ShuidianhuangjingjieruMod />
</SidePanel> -->
<jidiInfoMod />
<shuidianhuangjingjieruMod />
</RightDrawer>
</div>
</div>
@ -26,7 +25,7 @@ import RightDrawer from "@/components/RightDrawer/index.vue";
display: flex;
justify-content: space-between;
.rightContent{
height: 85vh;
height: 88vh;
}
}
</style>