feat():档案全周期开发
parent
34546864f4
commit
b57adda8e5
|
|
@ -1,2 +1,2 @@
|
||||||
GENERATE_SOURCEMAP=false
|
GENERATE_SOURCEMAP=false
|
||||||
PUBLIC_URL=/ssDp
|
PUBLIC_URL=/ssDp
|
||||||
|
|
@ -238,6 +238,22 @@ input:-webkit-autofill:active {
|
||||||
&:not(.ant-select-disabled):hover .ant-select-selector {
|
&:not(.ant-select-disabled):hover .ant-select-selector {
|
||||||
border-color: #3b7cff !important;
|
border-color: #3b7cff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Multiple Select Tags
|
||||||
|
&.ant-select-multiple .ant-select-selection-item {
|
||||||
|
background: rgba(0, 160, 233, 0.2); // Light blue transparent
|
||||||
|
border: 1px solid #00a0e9;
|
||||||
|
color: #fff;
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
|
||||||
|
.ant-select-selection-item-remove {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dropdowns (Select, DatePicker, etc.)
|
// Dropdowns (Select, DatePicker, etc.)
|
||||||
|
|
@ -271,14 +287,42 @@ input:-webkit-autofill:active {
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-picker-cell {
|
.ant-picker-cell {
|
||||||
|
color: rgba(255, 255, 255, 0.5); // Default inactive color
|
||||||
|
|
||||||
&:hover .ant-picker-cell-inner {
|
&:hover .ant-picker-cell-inner {
|
||||||
background: rgba(0, 160, 233, 0.3);
|
background: rgba(0, 160, 233, 0.3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-in-view {
|
&-in-view {
|
||||||
|
color: #fff; // Active month days
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix: Remove white background from cells
|
||||||
|
.ant-picker-cell-inner {
|
||||||
|
background: transparent !important;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-selected .ant-picker-cell-inner {
|
||||||
|
background: #00a0e9 !important; // Project Cyan
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
&-selected .ant-picker-cell-inner {
|
|
||||||
background: #3b7cff !important;
|
&-today .ant-picker-cell-inner {
|
||||||
|
border: 1px solid #00a0e9;
|
||||||
|
&::before {
|
||||||
|
border: 1px solid #00a0e9 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range Selection
|
||||||
|
&-in-range::before {
|
||||||
|
background: rgba(0, 160, 233, 0.2) !important;
|
||||||
|
}
|
||||||
|
&-range-start .ant-picker-cell-inner,
|
||||||
|
&-range-end .ant-picker-cell-inner {
|
||||||
|
background: #00a0e9 !important;
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,14 +352,76 @@ input:-webkit-autofill:active {
|
||||||
// 4. Button Component (Ghost Blue Style)
|
// 4. Button Component (Ghost Blue Style)
|
||||||
// ==============================================================================
|
// ==============================================================================
|
||||||
.ant-btn-ghost-blue {
|
.ant-btn-ghost-blue {
|
||||||
background: rgba(24, 144, 255, 0.3) !important; // Slight blue tint background
|
background: rgba(18, 56, 102, 0.6) !important;
|
||||||
border: 1px solid #1890ff !important;
|
border: 1px solid #00a0e9 !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
box-shadow: 0 0 8px rgba(24, 144, 255, 0.3) inset;
|
box-shadow: 0 0 8px rgba(0, 160, 233, 0.2) inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn.ant-btn-ghost-blue,
|
||||||
|
.ant-btn.ant-btn-primary.ant-btn-ghost-blue {
|
||||||
|
background: rgba(18, 56, 102, 0.6) !important;
|
||||||
|
border-color: #00a0e9 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn.ant-btn-ghost-blue:hover,
|
||||||
|
.ant-btn.ant-btn-ghost-blue:focus,
|
||||||
|
.ant-btn.ant-btn-primary.ant-btn-ghost-blue:hover,
|
||||||
|
.ant-btn.ant-btn-primary.ant-btn-ghost-blue:focus {
|
||||||
|
background: rgba(0, 160, 233, 0.3) !important;
|
||||||
|
border-color: #33b5ed !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// 5. Timeline Component
|
||||||
|
// ==============================================================================
|
||||||
|
.ant-timeline {
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
&:hover, &:focus {
|
.ant-timeline-item-label {
|
||||||
background: rgba(24, 144, 255, 0.3) !important;
|
color: #fff;
|
||||||
border-color: #40a9ff !important;
|
width: calc(50% - 12px);
|
||||||
color: #fff !important;
|
}
|
||||||
|
|
||||||
|
.ant-timeline-item-tail {
|
||||||
|
border-left: 2px solid rgba(0, 160, 233, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-timeline-item-head {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: #00a0e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-timeline-item-head-blue {
|
||||||
|
color: #00a0e9;
|
||||||
|
border-color: #00a0e9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// 6. Button Component (Global Override for Dark Theme)
|
||||||
|
// ==============================================================================
|
||||||
|
.ant-btn {
|
||||||
|
&.ant-btn-primary {
|
||||||
|
background: #00a0e9; // Project Theme Cyan
|
||||||
|
border-color: #00a0e9;
|
||||||
|
&:hover, &:focus {
|
||||||
|
background: #33b5ed;
|
||||||
|
border-color: #33b5ed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default button style in dark mode
|
||||||
|
&:not(.ant-btn-primary):not(.ant-btn-link):not(.ant-btn-text):not(.ant-btn-danger) {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
color: #00a0e9;
|
||||||
|
border-color: #00a0e9;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const config = {
|
||||||
|
ip: 'http://223.75.53.141:83',
|
||||||
|
minioIp:"http://223.75.53.141:9100/gs-ss"
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,11 @@ const apiurl = {
|
||||||
qth: {
|
qth: {
|
||||||
rainList: {
|
rainList: {
|
||||||
list:service + '/attResBase/rainBasinDivision/queryStPptnDetails/list',
|
list:service + '/attResBase/rainBasinDivision/queryStPptnDetails/list',
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
qzq: {
|
||||||
|
list: service + '/projectEvents/doc/page',
|
||||||
|
export: service + '/projectEvents/export'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,15 @@ async function send(url, options) {
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
async function sendFile(url, options) {
|
||||||
|
try {
|
||||||
|
const res = await request(url, options,'blob');
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
message.error(e);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
export function httpget(url, data = {}) {
|
export function httpget(url, data = {}) {
|
||||||
const params = [];
|
const params = [];
|
||||||
|
|
@ -106,7 +115,7 @@ export function ry_httpget(url, data = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function httppost(url, data = {}) {
|
export function httppost(url, data = {},type) {
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -117,8 +126,8 @@ export function httppost(url, data = {}) {
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
|
const fun = type == 'blob' ? sendFile(url, options) : send(url, options);
|
||||||
return send(url, options);
|
return fun;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function httpPostFile(url, data = {}) {
|
export function httpPostFile(url, data = {}) {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,22 @@ export function objType(obj) {
|
||||||
}
|
}
|
||||||
return typeof obj === 'object' || typeof obj === 'function' ? class2type[toString.call(obj)] || 'object' : typeof obj;
|
return typeof obj === 'object' || typeof obj === 'function' ? class2type[toString.call(obj)] || 'object' : typeof obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const exportFile = (name, res) => {
|
||||||
|
// 创建a标签
|
||||||
|
let blob = new Blob([res],{ type: 'application/octet-stream' })
|
||||||
|
// 创建下载链接
|
||||||
|
const downloadUrl = URL.createObjectURL(blob);
|
||||||
|
// 创建下载链接的 <a> 元素
|
||||||
|
const downloadLink = document.createElement("a");
|
||||||
|
downloadLink.href = downloadUrl;
|
||||||
|
downloadLink.download = name;
|
||||||
|
// 模拟点击下载链接
|
||||||
|
downloadLink.click();
|
||||||
|
// 清理创建的 URL 对象
|
||||||
|
URL.revokeObjectURL(downloadUrl);
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 是否数组
|
* 是否数组
|
||||||
* @param {object} obj 需要判断的对象
|
* @param {object} obj 需要判断的对象
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@ const AllWeatherControl = () => {
|
||||||
|
|
||||||
// Reservoir Data
|
// Reservoir Data
|
||||||
const reservoirData = [
|
const reservoirData = [
|
||||||
{ label: '主坝坝前', value: '103.17', unit: 'm', isPrimary: true, showArrow: true, underline: true },
|
{ label: '主坝坝前', value: '103.17', unit: 'm', showArrow: true, underline: true },
|
||||||
{ label: '汛限水位', value: '104.50', unit: 'm' },
|
{ label: '汛限水位', value: '104.50', unit: 'm' },
|
||||||
{ label: '距汛限', value: '-1.67', unit: 'm', isNegative: true },
|
{ label: '距汛限', value: '-1.67', unit: 'm', isNegative: true },
|
||||||
{ label: '副坝坝前', value: '104.17', unit: 'm' },
|
{ label: '副坝坝前', value: '104.17', unit: 'm' },
|
||||||
{ label: '当前库容', value: '3867.0', unit: '万m³', isPrimary: true },
|
{ label: '当前库容', value: '3867.0', unit: '万m³'},
|
||||||
{ label: '有效库容', value: '2867.0', unit: '万m³', isPrimary: true },
|
{ label: '有效库容', value: '2867.0', unit: '万m³'},
|
||||||
];
|
];
|
||||||
|
|
||||||
const getRainList = async () => {
|
const getRainList = async () => {
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.primary { color: #00eaff; }
|
&.positive { color: #ff4d52; }
|
||||||
&.negative { color: #68c639; }
|
&.negative { color: #68c639; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { DatePicker, Button, Table } from 'antd';
|
||||||
|
import ReactEcharts from 'echarts-for-react';
|
||||||
|
import usePageTable from '@/components/crud/usePageTable';
|
||||||
|
import { createCrudService } from '@/components/crud/_';
|
||||||
|
import apiurl from '@/service/apiurl';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
const RainPanel = () => {
|
||||||
|
const { tableProps, search } = usePageTable(createCrudService(apiurl.sq.qth.rainList).find);
|
||||||
|
const [range, setRange] = useState();
|
||||||
|
useEffect(() => {
|
||||||
|
search({ search: {} });
|
||||||
|
}, []);
|
||||||
|
const columns = [
|
||||||
|
{ title: '时间', dataIndex: 'tm', key: 'tm', width: 140, align: 'center' },
|
||||||
|
{ title: '小时雨量(mm)', dataIndex: 'drp', key: 'drp', align: 'center' },
|
||||||
|
];
|
||||||
|
const option = useMemo(() => ({
|
||||||
|
title: { text: '雨量趋势', left: 'center', textStyle: { color: '#fff' } },
|
||||||
|
tooltip: { trigger: 'axis' },
|
||||||
|
grid: { left: '8%', right: '4%', bottom: '10%', top: '15%' },
|
||||||
|
xAxis: { type: 'category', data: (tableProps.dataSource || []).map(i => i.tm), axisLabel: { color: '#fff' } },
|
||||||
|
yAxis: { type: 'value', axisLabel: { color: '#fff' }, splitLine: { show: true, lineStyle: { color: 'rgba(255,255,255,0.2)' } } },
|
||||||
|
series: [{ type: 'line', smooth: true, data: (tableProps.dataSource || []).map(i => i.drp), areaStyle: { color: 'rgba(0,160,233,0.25)' }, lineStyle: { color: '#00a0e9' } }],
|
||||||
|
}), [tableProps.dataSource]);
|
||||||
|
return (
|
||||||
|
<div className="awm-grid">
|
||||||
|
<div className="awm-left">
|
||||||
|
<div className="awm-toolbar">
|
||||||
|
<RangePicker value={range} onChange={setRange} />
|
||||||
|
<Button type="primary" className="ant-btn-ghost-blue" onClick={() => search({ search: { range } })}>查询</Button>
|
||||||
|
</div>
|
||||||
|
<Table columns={columns} {...tableProps} size="small" pagination={false} scroll={{ y: 360 }} />
|
||||||
|
</div>
|
||||||
|
<div className="awm-right">
|
||||||
|
<ReactEcharts option={option} style={{ height: '100%', width: '100%' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReservoirPanel = () => {
|
||||||
|
const columns = [
|
||||||
|
{ title: '时间', dataIndex: 'tm', key: 'tm', width: 140, align: 'center' },
|
||||||
|
{ title: '水位(m)', dataIndex: 'rz', key: 'rz', align: 'center' },
|
||||||
|
{ title: '库容(万m³)', dataIndex: 'w', key: 'w', align: 'center' },
|
||||||
|
];
|
||||||
|
const data = [];
|
||||||
|
const option = {
|
||||||
|
title: { text: '水位趋势', left: 'center', textStyle: { color: '#fff' } },
|
||||||
|
grid: { left: '8%', right: '4%', bottom: '10%', top: '15%' },
|
||||||
|
tooltip: { trigger: 'axis' },
|
||||||
|
xAxis: { type: 'category', data: data.map(i => i.tm), axisLabel: { color: '#fff' } },
|
||||||
|
yAxis: { type: 'value', axisLabel: { color: '#fff' }, splitLine: { show: true, lineStyle: { color: 'rgba(255,255,255,0.2)' } } },
|
||||||
|
series: [{ type: 'line', smooth: true, data: data.map(i => i.rz), lineStyle: { color: '#00a0e9' } }],
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="awm-grid">
|
||||||
|
<div className="awm-left">
|
||||||
|
<Table columns={columns} dataSource={data} size="small" pagination={false} scroll={{ y: 360 }} />
|
||||||
|
</div>
|
||||||
|
<div className="awm-right">
|
||||||
|
<ReactEcharts option={option} style={{ height: '100%', width: '100%' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlowPanel = () => {
|
||||||
|
const columns = [
|
||||||
|
{ title: '时间', dataIndex: 'tm', key: 'tm', width: 140, align: 'center' },
|
||||||
|
{ title: '入库(m³/s)', dataIndex: 'qin', key: 'qin', align: 'center' },
|
||||||
|
{ title: '出库(m³/s)', dataIndex: 'qout', key: 'qout', align: 'center' },
|
||||||
|
];
|
||||||
|
const data = [];
|
||||||
|
const option = {
|
||||||
|
title: { text: '出入库流量', left: 'center', textStyle: { color: '#fff' } },
|
||||||
|
grid: { left: '8%', right: '4%', bottom: '10%', top: '15%' },
|
||||||
|
tooltip: { trigger: 'axis' },
|
||||||
|
legend: { data: ['入库', '出库'], textStyle: { color: '#fff' } },
|
||||||
|
xAxis: { type: 'category', data: data.map(i => i.tm), axisLabel: { color: '#fff' } },
|
||||||
|
yAxis: { type: 'value', axisLabel: { color: '#fff' }, splitLine: { show: true, lineStyle: { color: 'rgba(255,255,255,0.2)' } } },
|
||||||
|
series: [
|
||||||
|
{ name: '入库', type: 'line', data: data.map(i => i.qin), lineStyle: { color: '#00a0e9' } },
|
||||||
|
{ name: '出库', type: 'line', data: data.map(i => i.qout), lineStyle: { color: '#3b7cff' } },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="awm-grid">
|
||||||
|
<div className="awm-left">
|
||||||
|
<Table columns={columns} dataSource={data} size="small" pagination={false} scroll={{ y: 360 }} />
|
||||||
|
</div>
|
||||||
|
<div className="awm-right">
|
||||||
|
<ReactEcharts option={option} style={{ height: '100%', width: '100%' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SafetyPanel = () => {
|
||||||
|
return (
|
||||||
|
<div className="awm-empty">内容待接入</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AllWeatherModal = ({ active }) => {
|
||||||
|
if (active === 'rain') return <RainPanel />;
|
||||||
|
if (active === 'reservoir') return <ReservoirPanel />;
|
||||||
|
if (active === 'flow') return <FlowPanel />;
|
||||||
|
return <SafetyPanel />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AllWeatherModal;
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
.all-weather-modal {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.awm-grid {
|
||||||
|
height: calc(100% - 48px);
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.awm-left {
|
||||||
|
flex: 0 0 420px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.awm-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.awm-right {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.awm-empty {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: rgba(255,255,255,0.6);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,187 @@
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Timeline, Select, DatePicker, Button, Tag, Image, Tooltip } from 'antd';
|
||||||
|
import { SearchOutlined, ReloadOutlined, DownloadOutlined, FilePdfOutlined, FileOutlined } from '@ant-design/icons';
|
||||||
|
import apiurl from '@/service/apiurl';
|
||||||
|
import usePageTable from '@/components/crud/usePageTable';
|
||||||
|
import { createCrudService } from '@/components/crud/_';
|
||||||
|
import { exportFile } from '@/utils/tools'
|
||||||
|
import { httppost } from '@/utils/request';
|
||||||
|
import { config } from '@/config';
|
||||||
|
import PdfView from '@/views/Home/components/UI/PdfView';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
const CycleArchive = () => {
|
||||||
|
const { tableProps, search, refresh } = usePageTable(createCrudService(apiurl.sq.qzq.list).find);
|
||||||
|
const [keyword, setKeyword] = useState([]);
|
||||||
|
const [dates, setDates] = useState();
|
||||||
|
const [pdfInfo, setPdfInfo] = useState({ visible: false, title: '', fileId: '' });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
search({ search: {} });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
const params = {
|
||||||
|
search: {
|
||||||
|
types: keyword.length > 0 ? keyword : undefined,
|
||||||
|
dateSo: dates ? {
|
||||||
|
start: dates[0]?.format('YYYY-MM-DD'),
|
||||||
|
end: dates[1]?.format('YYYY-MM-DD')
|
||||||
|
} : undefined
|
||||||
|
}
|
||||||
|
};
|
||||||
|
search(params);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setKeyword([]);
|
||||||
|
setDates(undefined);
|
||||||
|
search({ search: {} });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onExport = () => {
|
||||||
|
let params = {
|
||||||
|
search: {
|
||||||
|
types: keyword.length > 0 ? keyword : undefined,
|
||||||
|
dateSo: dates ? {
|
||||||
|
start: dates[0]?.format('YYYY-MM-DD'),
|
||||||
|
end: dates[1]?.format('YYYY-MM-DD')
|
||||||
|
} : undefined
|
||||||
|
},
|
||||||
|
pageSo:{
|
||||||
|
pageNum:1,pageSize:9999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
httppost(apiurl.sq.qzq.export, params,'blob').then(res => {
|
||||||
|
exportFile(`全周期档案.xlsx`,res.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileClick = (file) => {
|
||||||
|
const fileType = file.fileName?.split('.').pop()?.toLowerCase();
|
||||||
|
if (fileType === 'pdf') {
|
||||||
|
setPdfInfo({
|
||||||
|
visible: true,
|
||||||
|
title: file.fileName,
|
||||||
|
fileId: file.fileId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Download for non-pdf files
|
||||||
|
window.open(config.minioIp + file.filePath, '_blank');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderFileIcon = (file) => {
|
||||||
|
const fileType = file.fileName?.split('.').pop()?.toLowerCase();
|
||||||
|
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(fileType);
|
||||||
|
|
||||||
|
if (isImage) {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
width={60}
|
||||||
|
height={60}
|
||||||
|
src={config.minioIp + file.filePath}
|
||||||
|
style={{ objectFit: 'cover', borderRadius: '4px' }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (fileType === 'pdf') {
|
||||||
|
return <FilePdfOutlined style={{ fontSize: '40px', color: '#ff4d4f' }} />;
|
||||||
|
} else {
|
||||||
|
return <FileOutlined style={{ fontSize: '40px', color: '#1890ff' }} />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="cycle-archive-container">
|
||||||
|
{/* Filters */}
|
||||||
|
<div className="filter-bar">
|
||||||
|
<div className="filter-item">
|
||||||
|
<span className="label">类型:</span>
|
||||||
|
<Select
|
||||||
|
options={[
|
||||||
|
{value:1,label:'大事记'}, {value:2,label:'调度指令'}, {value:3,label:'维修养护'},{value:4,label:'安全鉴定'}, {value:5,label:"除险加固"}
|
||||||
|
]}
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入类型"
|
||||||
|
style={{ width: 200 }}
|
||||||
|
value={keyword}
|
||||||
|
onChange={(e) => setKeyword(e)}
|
||||||
|
mode='multiple'
|
||||||
|
maxTagCount='responsive'
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="filter-item">
|
||||||
|
<span className="label">发生日期:</span>
|
||||||
|
<RangePicker
|
||||||
|
style={{ width: 300 }}
|
||||||
|
value={dates}
|
||||||
|
onChange={(val) => setDates(val)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="action-buttons">
|
||||||
|
<Button type="primary" className="ant-btn-ghost-blue" icon={<SearchOutlined />} onClick={handleSearch}>查询</Button>
|
||||||
|
<Button icon={<ReloadOutlined />} onClick={handleReset}>重置</Button>
|
||||||
|
<Button type="primary" icon={<DownloadOutlined />} className="ant-btn-ghost-blue" onClick={onExport}>导出</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Timeline Content */}
|
||||||
|
<div className="timeline-content">
|
||||||
|
<Timeline>
|
||||||
|
{tableProps.dataSource?.map((item, index) => (
|
||||||
|
<Timeline.Item
|
||||||
|
key={index}
|
||||||
|
label={
|
||||||
|
<div className="timeline-label-content">
|
||||||
|
<div className="date">{item.eventsDate}</div>
|
||||||
|
<Tag color="cyan" className="custom-tag">{item.typeName}</Tag>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="timeline-item-content">
|
||||||
|
<div className="item-header">
|
||||||
|
<span className="title">{item.eventsDesc}</span>
|
||||||
|
</div>
|
||||||
|
<div className="item-body">
|
||||||
|
<div className="attachment-label">附件:</div>
|
||||||
|
<div className="image-grid">
|
||||||
|
{item.files?.length > 0 && item.files.map((file, idx) => (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="image-item"
|
||||||
|
onClick={() => !['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(file.fileName?.split('.').pop()?.toLowerCase()) && handleFileClick(file)}
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
>
|
||||||
|
<Tooltip title={file.fileName}>
|
||||||
|
<div className="file-preview">
|
||||||
|
{renderFileIcon(file)}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
<span className="image-name">{file.fileName}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Timeline.Item>
|
||||||
|
))}
|
||||||
|
</Timeline>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{pdfInfo.visible && (
|
||||||
|
<PdfView
|
||||||
|
visible={pdfInfo.visible}
|
||||||
|
onClose={() => setPdfInfo({ ...pdfInfo, visible: false })}
|
||||||
|
title={pdfInfo.title}
|
||||||
|
fileId={pdfInfo.fileId}
|
||||||
|
url={"/gunshiApp/ss/projectEvents/file/download/"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CycleArchive;
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
.cycle-archive-container {
|
||||||
|
padding: 0px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.filter-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.filter-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.label {
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden; // Prevent horizontal scrollbar
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.ant-timeline-item {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-timeline-item-label {
|
||||||
|
color: #00a0e9;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 120px !important; // Fixed width for label
|
||||||
|
position: absolute !important;
|
||||||
|
left: 0 !important;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 15px;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-label-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.date {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tag {
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
background: #00a0e9; // Match theme
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: center;
|
||||||
|
width: fit-content;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-timeline-item-tail {
|
||||||
|
border-left: 2px solid rgba(0, 160, 233, 0.3);
|
||||||
|
left: 126px !important; // 120px (label) + ~6px (center of gap)
|
||||||
|
height: calc(100% - 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-timeline-item-head {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: #00a0e9;
|
||||||
|
left: 126px !important; // Match tail
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-timeline-item-content {
|
||||||
|
margin-left: 140px !important; // 126px + padding
|
||||||
|
width: calc(100% - 140px) !important; // Take remaining width
|
||||||
|
top: 0;
|
||||||
|
padding-top: 0; // Align with label
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item-content {
|
||||||
|
background: transparent;
|
||||||
|
// border: 1px solid rgba(255, 255, 255, 0.1); // Removed border as per "clean" look
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-body {
|
||||||
|
.attachment-label {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 15px;
|
||||||
|
|
||||||
|
.image-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
gap: 10px;
|
||||||
|
width: 280px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-color: #00a0e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-preview {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-name {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ import EngineeringElements from './components/ModalComponents/EngineeringElement
|
||||||
import DownstreamElements from './components/ModalComponents/DownstreamElements';
|
import DownstreamElements from './components/ModalComponents/DownstreamElements';
|
||||||
import AllWeatherControl from './components/AllWeatherControl';
|
import AllWeatherControl from './components/AllWeatherControl';
|
||||||
import ManagementCycle from './components/ManagementCycle';
|
import ManagementCycle from './components/ManagementCycle';
|
||||||
|
import CycleArchive from './components/ModalComponents/CycleArchive';
|
||||||
|
import AllWeatherModal from './components/ModalComponents/AllWeatherModal';
|
||||||
import CommonModal from '../../UI/CommonModal';
|
import CommonModal from '../../UI/CommonModal';
|
||||||
import { httppost } from '@/utils/request';
|
import { httppost } from '@/utils/request';
|
||||||
import apiurl from '@/service/apiurl';
|
import apiurl from '@/service/apiurl';
|
||||||
|
|
@ -16,6 +18,7 @@ import './index.less';
|
||||||
|
|
||||||
const SiQuan = () => {
|
const SiQuan = () => {
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
|
const [modalType, setModalType] = useState('monitor'); // 'monitor' | 'cycle' | 'allweather'
|
||||||
const [infos, setInfos] = useState({});
|
const [infos, setInfos] = useState({});
|
||||||
const [activeTab, setActiveTab] = useState('kqys'); // Default active tab
|
const [activeTab, setActiveTab] = useState('kqys'); // Default active tab
|
||||||
|
|
||||||
|
|
@ -24,6 +27,12 @@ const SiQuan = () => {
|
||||||
{ label: '工程要素', value: 'gcys' },
|
{ label: '工程要素', value: 'gcys' },
|
||||||
{ label: '下游要素', value: 'xyys' },
|
{ label: '下游要素', value: 'xyys' },
|
||||||
];
|
];
|
||||||
|
const tabsAllWeather = [
|
||||||
|
{ label: '雨情监测', value: 'rain' },
|
||||||
|
{ label: '水库水情', value: 'reservoir' },
|
||||||
|
{ label: '出入库流量', value: 'flow' },
|
||||||
|
{ label: '安全监测', value: 'safety' },
|
||||||
|
];
|
||||||
|
|
||||||
const getInfo = async () => {
|
const getInfo = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -42,6 +51,17 @@ const SiQuan = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleOpenModal = () => {
|
const handleOpenModal = () => {
|
||||||
|
setModalType('monitor');
|
||||||
|
setModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenCycleModal = () => {
|
||||||
|
setModalType('cycle');
|
||||||
|
setModalVisible(true);
|
||||||
|
};
|
||||||
|
const handleOpenAllWeatherModal = () => {
|
||||||
|
setModalType('allweather');
|
||||||
|
setActiveTab('rain');
|
||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -76,11 +96,15 @@ const SiQuan = () => {
|
||||||
<CommonCard
|
<CommonCard
|
||||||
title="管控全天候"
|
title="管控全天候"
|
||||||
className="panel-card card-1"
|
className="panel-card card-1"
|
||||||
headerExtra={<ThreeDots onClick={() => console.log('管控全天候 clicked')} />}
|
headerExtra={<ThreeDots onClick={handleOpenAllWeatherModal} />}
|
||||||
>
|
>
|
||||||
<AllWeatherControl />
|
<AllWeatherControl />
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
<CommonCard title="管理全周期" className="panel-card card-3">
|
<CommonCard
|
||||||
|
title="管理全周期"
|
||||||
|
className="panel-card card-3"
|
||||||
|
headerExtra={<ThreeDots onClick={handleOpenCycleModal} />}
|
||||||
|
>
|
||||||
<ManagementCycle />
|
<ManagementCycle />
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -89,17 +113,23 @@ const SiQuan = () => {
|
||||||
<CommonModal
|
<CommonModal
|
||||||
visible={modalVisible}
|
visible={modalVisible}
|
||||||
onClose={handleCloseModal}
|
onClose={handleCloseModal}
|
||||||
title="掌握全要素"
|
title={modalType === 'monitor' ? "掌握全要素" : (modalType === 'cycle' ? "全周期档案" : "管控全天候")}
|
||||||
tabs={tabs}
|
tabs={modalType === 'monitor' ? tabs : (modalType === 'allweather' ? tabsAllWeather : [])}
|
||||||
activeTab={activeTab}
|
activeTab={activeTab}
|
||||||
onTabChange={handleTabChange}
|
onTabChange={handleTabChange}
|
||||||
|
width={modalType === 'cycle' ? '70%': undefined}
|
||||||
>
|
>
|
||||||
{/* Content changes based on activeTab */}
|
{/* Content changes based on activeTab */}
|
||||||
<div style={{color: '#fff', height: '100%' }}>
|
<div style={{color: '#fff', height: '100%' }}>
|
||||||
{activeTab === 'kqys' && <ReservoirAreaElements />}
|
{modalType === 'monitor' && (
|
||||||
{activeTab === 'gcys' && <EngineeringElements data={infos} />}
|
<>
|
||||||
{activeTab === 'xyys' && <DownstreamElements />}
|
{activeTab === 'kqys' && <ReservoirAreaElements />}
|
||||||
{/* {activeTab === 'glys' && <ManagementElements />} */}
|
{activeTab === 'gcys' && <EngineeringElements data={infos} />}
|
||||||
|
{activeTab === 'xyys' && <DownstreamElements />}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{modalType === 'cycle' && <CycleArchive />}
|
||||||
|
{modalType === 'allweather' && <AllWeatherModal active={activeTab} />}
|
||||||
</div>
|
</div>
|
||||||
</CommonModal>
|
</CommonModal>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { CloseOutlined } from '@ant-design/icons';
|
||||||
import titleBg from '@/assets/images/modal/title.png';
|
import titleBg from '@/assets/images/modal/title.png';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const CommonModal = ({ visible, onClose, title, children, width, tabs = [], activeTab, onTabChange }) => {
|
const CommonModal = ({ visible, onClose, title, children, width, tabs = [], activeTab, onTabChange, bodyStyle = {} }) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
//当弹框打开时,如果不加这行代码,用户滚动鼠标滚轮时,背后的页面(Home 页)也会跟着滚动
|
//当弹框打开时,如果不加这行代码,用户滚动鼠标滚轮时,背后的页面(Home 页)也会跟着滚动
|
||||||
|
|
@ -45,7 +45,7 @@ const CommonModal = ({ visible, onClose, title, children, width, tabs = [], acti
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-body">
|
<div className="modal-body" style={bodyStyle}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,26 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import CommonModal from '../CommonModal';
|
||||||
|
|
||||||
const PdfView = ({ visible, title, onClose, url, fileId }) => {
|
const PdfView = ({ visible, title, onClose, url, fileId }) => {
|
||||||
if (!visible) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<CommonModal
|
||||||
position: 'fixed',
|
visible={visible}
|
||||||
top: 0,
|
title={title}
|
||||||
left: 0,
|
onClose={onClose}
|
||||||
width: '100vw',
|
width="60%"
|
||||||
height: '100vh',
|
bodyStyle={{ padding: 0, overflow: 'hidden' }}
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
>
|
||||||
zIndex: 2000,
|
<iframe
|
||||||
display: 'flex',
|
style={{
|
||||||
justifyContent: 'center',
|
height: '100%',
|
||||||
alignItems: 'center'
|
width: '100%',
|
||||||
}}>
|
border: 0,
|
||||||
<div style={{ width: '60%', height: '80vh', display: 'flex', flexDirection: 'column', position: 'relative' }}>
|
background: '#fff'
|
||||||
<CloseOutlined
|
}}
|
||||||
onClick={onClose}
|
src={`${process.env.PUBLIC_URL}/static/pdf/web/viewer.html?file=${encodeURIComponent(url + fileId)}`}
|
||||||
style={{
|
title={title}
|
||||||
position: 'absolute',
|
/>
|
||||||
top: '-30px',
|
</CommonModal>
|
||||||
right: 0,
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: '18px',
|
|
||||||
cursor: 'pointer'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<iframe
|
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
border: 0,
|
|
||||||
background: '#fff'
|
|
||||||
}}
|
|
||||||
src={`${process.env.PUBLIC_URL}/static/pdf/web/viewer.html?file=${encodeURIComponent(url + fileId)}`}
|
|
||||||
title={title}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default PdfView
|
export default PdfView
|
||||||
Loading…
Reference in New Issue