feat():档案全周期开发
parent
34546864f4
commit
b57adda8e5
|
|
@ -1,2 +1,2 @@
|
|||
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 {
|
||||
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.)
|
||||
|
|
@ -271,14 +287,42 @@ input:-webkit-autofill:active {
|
|||
}
|
||||
|
||||
.ant-picker-cell {
|
||||
color: rgba(255, 255, 255, 0.5); // Default inactive color
|
||||
|
||||
&:hover .ant-picker-cell-inner {
|
||||
background: rgba(0, 160, 233, 0.3);
|
||||
background: rgba(0, 160, 233, 0.3) !important;
|
||||
}
|
||||
|
||||
&-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;
|
||||
}
|
||||
&-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)
|
||||
// ==============================================================================
|
||||
.ant-btn-ghost-blue {
|
||||
background: rgba(24, 144, 255, 0.3) !important; // Slight blue tint background
|
||||
border: 1px solid #1890ff !important;
|
||||
background: rgba(18, 56, 102, 0.6) !important;
|
||||
border: 1px solid #00a0e9 !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 {
|
||||
background: rgba(24, 144, 255, 0.3) !important;
|
||||
border-color: #40a9ff !important;
|
||||
color: #fff !important;
|
||||
.ant-timeline-item-label {
|
||||
color: #fff;
|
||||
width: calc(50% - 12px);
|
||||
}
|
||||
|
||||
.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: {
|
||||
rainList: {
|
||||
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 {};
|
||||
}
|
||||
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 = {}) {
|
||||
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 = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
|
@ -117,8 +126,8 @@ export function httppost(url, data = {}) {
|
|||
},
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
|
||||
return send(url, options);
|
||||
const fun = type == 'blob' ? sendFile(url, options) : send(url, options);
|
||||
return fun;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 需要判断的对象
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@ const AllWeatherControl = () => {
|
|||
|
||||
// Reservoir Data
|
||||
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: '-1.67', unit: 'm', isNegative: true },
|
||||
{ label: '副坝坝前', value: '104.17', unit: 'm' },
|
||||
{ label: '当前库容', value: '3867.0', unit: '万m³', isPrimary: true },
|
||||
{ label: '有效库容', value: '2867.0', unit: '万m³', isPrimary: true },
|
||||
{ label: '当前库容', value: '3867.0', unit: '万m³'},
|
||||
{ label: '有效库容', value: '2867.0', unit: '万m³'},
|
||||
];
|
||||
|
||||
const getRainList = async () => {
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
&.primary { color: #00eaff; }
|
||||
&.positive { color: #ff4d52; }
|
||||
&.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 AllWeatherControl from './components/AllWeatherControl';
|
||||
import ManagementCycle from './components/ManagementCycle';
|
||||
import CycleArchive from './components/ModalComponents/CycleArchive';
|
||||
import AllWeatherModal from './components/ModalComponents/AllWeatherModal';
|
||||
import CommonModal from '../../UI/CommonModal';
|
||||
import { httppost } from '@/utils/request';
|
||||
import apiurl from '@/service/apiurl';
|
||||
|
|
@ -16,6 +18,7 @@ import './index.less';
|
|||
|
||||
const SiQuan = () => {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [modalType, setModalType] = useState('monitor'); // 'monitor' | 'cycle' | 'allweather'
|
||||
const [infos, setInfos] = useState({});
|
||||
const [activeTab, setActiveTab] = useState('kqys'); // Default active tab
|
||||
|
||||
|
|
@ -24,6 +27,12 @@ const SiQuan = () => {
|
|||
{ label: '工程要素', value: 'gcys' },
|
||||
{ label: '下游要素', value: 'xyys' },
|
||||
];
|
||||
const tabsAllWeather = [
|
||||
{ label: '雨情监测', value: 'rain' },
|
||||
{ label: '水库水情', value: 'reservoir' },
|
||||
{ label: '出入库流量', value: 'flow' },
|
||||
{ label: '安全监测', value: 'safety' },
|
||||
];
|
||||
|
||||
const getInfo = async () => {
|
||||
try {
|
||||
|
|
@ -42,6 +51,17 @@ const SiQuan = () => {
|
|||
}, []);
|
||||
|
||||
const handleOpenModal = () => {
|
||||
setModalType('monitor');
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const handleOpenCycleModal = () => {
|
||||
setModalType('cycle');
|
||||
setModalVisible(true);
|
||||
};
|
||||
const handleOpenAllWeatherModal = () => {
|
||||
setModalType('allweather');
|
||||
setActiveTab('rain');
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
|
|
@ -76,11 +96,15 @@ const SiQuan = () => {
|
|||
<CommonCard
|
||||
title="管控全天候"
|
||||
className="panel-card card-1"
|
||||
headerExtra={<ThreeDots onClick={() => console.log('管控全天候 clicked')} />}
|
||||
headerExtra={<ThreeDots onClick={handleOpenAllWeatherModal} />}
|
||||
>
|
||||
<AllWeatherControl />
|
||||
</CommonCard>
|
||||
<CommonCard title="管理全周期" className="panel-card card-3">
|
||||
<CommonCard
|
||||
title="管理全周期"
|
||||
className="panel-card card-3"
|
||||
headerExtra={<ThreeDots onClick={handleOpenCycleModal} />}
|
||||
>
|
||||
<ManagementCycle />
|
||||
</CommonCard>
|
||||
</div>
|
||||
|
|
@ -89,17 +113,23 @@ const SiQuan = () => {
|
|||
<CommonModal
|
||||
visible={modalVisible}
|
||||
onClose={handleCloseModal}
|
||||
title="掌握全要素"
|
||||
tabs={tabs}
|
||||
title={modalType === 'monitor' ? "掌握全要素" : (modalType === 'cycle' ? "全周期档案" : "管控全天候")}
|
||||
tabs={modalType === 'monitor' ? tabs : (modalType === 'allweather' ? tabsAllWeather : [])}
|
||||
activeTab={activeTab}
|
||||
onTabChange={handleTabChange}
|
||||
width={modalType === 'cycle' ? '70%': undefined}
|
||||
>
|
||||
{/* Content changes based on activeTab */}
|
||||
<div style={{color: '#fff', height: '100%' }}>
|
||||
{activeTab === 'kqys' && <ReservoirAreaElements />}
|
||||
{activeTab === 'gcys' && <EngineeringElements data={infos} />}
|
||||
{activeTab === 'xyys' && <DownstreamElements />}
|
||||
{/* {activeTab === 'glys' && <ManagementElements />} */}
|
||||
{modalType === 'monitor' && (
|
||||
<>
|
||||
{activeTab === 'kqys' && <ReservoirAreaElements />}
|
||||
{activeTab === 'gcys' && <EngineeringElements data={infos} />}
|
||||
{activeTab === 'xyys' && <DownstreamElements />}
|
||||
</>
|
||||
)}
|
||||
{modalType === 'cycle' && <CycleArchive />}
|
||||
{modalType === 'allweather' && <AllWeatherModal active={activeTab} />}
|
||||
</div>
|
||||
</CommonModal>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { CloseOutlined } from '@ant-design/icons';
|
|||
import titleBg from '@/assets/images/modal/title.png';
|
||||
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(() => {
|
||||
if (visible) {
|
||||
//当弹框打开时,如果不加这行代码,用户滚动鼠标滚轮时,背后的页面(Home 页)也会跟着滚动
|
||||
|
|
@ -45,7 +45,7 @@ const CommonModal = ({ visible, onClose, title, children, width, tabs = [], acti
|
|||
<CloseOutlined />
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="modal-body" style={bodyStyle}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,46 +1,26 @@
|
|||
import React from "react";
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import CommonModal from '../CommonModal';
|
||||
|
||||
const PdfView = ({ visible, title, onClose, url, fileId }) => {
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
zIndex: 2000,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<div style={{ width: '60%', height: '80vh', display: 'flex', flexDirection: 'column', position: 'relative' }}>
|
||||
<CloseOutlined
|
||||
onClick={onClose}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-30px',
|
||||
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>
|
||||
<CommonModal
|
||||
visible={visible}
|
||||
title={title}
|
||||
onClose={onClose}
|
||||
width="60%"
|
||||
bodyStyle={{ padding: 0, overflow: 'hidden' }}
|
||||
>
|
||||
<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}
|
||||
/>
|
||||
</CommonModal>
|
||||
)
|
||||
}
|
||||
export default PdfView
|
||||
Loading…
Reference in New Issue