feat():完善体制模块开发

qzc-dev
李神峰 2026-01-30 17:44:04 +08:00
parent 84016faa09
commit 793319bbdc
23 changed files with 1158 additions and 41 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -44,6 +44,14 @@ const apiurl = {
export: service + '/projectEvents/export',
info :service + '/wholeCycle/get'
}
},
sz: {
jqjz: {
budgetInfo: service + '/fundBudget/get/',
manageInfo: service + '/screen/mechanisms/equipment',
managePic: service + '/screen/manageHouseImg/get',
wzPage:service + '/rescue/goods/page/query'
}
}
}

View File

@ -132,7 +132,7 @@ const AllWeatherControl = () => {
className="reservoir-card"
style={{ backgroundImage: `url(${smallCard})` }}
>
<div className={`value ${item.isPrimary ? 'primary' : ''} ${item.isNegative ? 'negative' : 'positive'}`}>
<div className={`value ${item.isPrimary ? 'primary' : ''} ${item.label == '距汛限'?item.isNegative ? 'negative' : 'positive':""}`}>
<span className={`num ${item.underline ? 'underline' : ''}`}>{item.value}</span>
<span className="unit">{item.unit}</span>
</div>

View File

@ -22,7 +22,7 @@
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
margin-bottom: 15px;
.title-wrapper {
display: flex;

View File

@ -20,18 +20,19 @@ const ManagementCycle = () => {
value: info?.dispatchTime,
underline: true,
clickable: true,
fileId:info?.dispatchFileIds?.length? info?.dispatchFileIds[0] + '':undefined // Assuming this field exists
fileId:info?.dispatchFileIds?.length? info?.dispatchFileIds[0]:undefined // Assuming this field exists
},
{
label: '应急预案',
value: info?.emergencyTime,
underline: true,
clickable: true,
fileId:info?.emergencyFileIds?.length? info?.emergencyFileIds[0] + '':undefined // Assuming this field exists
fileId:info?.emergencyFileIds?.length? info?.emergencyFileIds[0]:undefined // Assuming this field exists
},
];
const handleItemClick = (item) => {
if (!item.fileId) return;
const url = '/gunshiApp/ss/resPlanB/file/download/';
// if (!item?.dispatchFileIds || item?.dispatchFileIds.length) return;
// const field = item.label == '调度规程' ? item?.dispatchFileIds[0] + '' :
@ -85,7 +86,7 @@ const ManagementCycle = () => {
<PdfView
visible={pdfVisible}
title={pdfConfig.title}
url={'/gunshiApp/ss/resPlanB/file/download'}
url={pdfConfig.url}
fileId={pdfConfig.fileId}
onClose={() => setPdfVisible(false)}
/>

View File

@ -17,7 +17,7 @@
.cycle-card {
width: calc((100% - 20px) / 3);
height: 80px; // Slightly taller to accommodate text comfortably
height: 70px; // Slightly taller to accommodate text comfortably
margin-bottom: 15px;
margin-right: 10px;

View File

@ -69,7 +69,7 @@
align-items: center;
flex: 1;
font-size: 14px;
justify-content: space-between;
.label {
color: #CCF3FF;
margin-right: 10px;

View File

@ -0,0 +1,46 @@
import React, { useState, useEffect } from 'react';
import { Table, Input, Button } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
import CommonModal from '@/views/Home/components/UI/CommonModal';
import usePageTable from '@/components/crud/usePageTable';
import { createCrudService } from '@/components/crud/_';
import apiurl from '@/service/apiurl';
const OrgnizeModal = ({ visible, onClose,title }) => {
const columns = [
{ title: '序号', dataIndex: 'inx', key: 'inx', width: 60, align: 'center' },
{ title: '用户姓名', dataIndex: 'spec', key: 'spec', align: 'center' },
{ title: '手机号码', dataIndex: 'phone', key: 'phone', align: 'center' },
{ title: '部门', dataIndex: 'contactPerson', key: 'contactPerson', align: 'center' },
];
const { tableProps, search} = usePageTable(createCrudService(apiurl.sz.jqjz.wzPage).find);
useEffect(() => {
if (visible) {
search()
}
}, [visible]);
return (
<CommonModal
visible={visible}
onClose={onClose}
title={title}
width="70%"
>
<div className="material-modal-content">
<div className="table-wrapper">
<Table
columns={columns}
{...tableProps}
className="custom-table"
size="middle"
/>
</div>
</div>
</CommonModal>
);
};
export default OrgnizeModal;

View File

@ -0,0 +1,115 @@
import React, { useState, useEffect } from 'react';
import { UserOutlined, BankOutlined, ApartmentOutlined } from '@ant-design/icons';
import textBg from '@/assets/images/card/textbg.png';
import arrowIcon from '@/assets/images/card/arrow.png';
import OrgnizeModal from './OrgnizeModal'
import './index.less';
const PerfectSystem = ({ data }) => {
const [modalVisible, setModalVisible] = useState(false);
const [modalTitle, setModalTitle] = useState('');
const managementInfo = [
{ label: '管理单位', value: data?.managName ?? '-', icon: <BankOutlined />, type: 'unit' },
{ label: '负责人', value: data?.chargePerson ?? '-', icon: <UserOutlined />, type: 'person' },
{ label: '归口管理部门', value: data?.admDep ?? '-', icon: <ApartmentOutlined />, type: 'dept' },
];
const leftOrg = [
{ name: '工程科', count: 3 },
{ name: '办公室', count: 2 },
{ name: '财务科', count: 1 },
{ name: '...', count: null },
];
const rightOrg = [
{ name: '后勤保障', subName: '中心', count: 5 },
{ name: '...', count: null },
];
const handleCardClick = (item) => {
setModalTitle(item.name);
setModalVisible(true);
};
return (
<div className="perfect-system">
<div className="section">
<div className="section-title">
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
<span>管理单位</span>
</div>
<div className="info-list">
{managementInfo.map((item, index) => (
<div key={index} className="info-item" style={{ backgroundImage: `url(${textBg})` }}>
<div className={`icon-box ${item.type}`}>
{item.icon}
</div>
<div className="info-content">
<span className="label">{item.label}</span>
<span className="value">{item.value}</span>
</div>
</div>
))}
</div>
</div>
<div className="section mt-15">
<div className="section-title">
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
<span>水库组织机构</span>
</div>
<div className="org-chart">
<div className="side-column left">
{leftOrg.map((item, idx) => (
<div
key={idx}
className={`org-node ${item.count === null ? 'placeholder' : ''}`}
style={{cursor:'pointer'}}
onClick={() => handleCardClick(item)}
>
<span className="node-text" style={{borderBottom:"1px solid #00a0e9"}}>{item.name}{item.count !== null ? `(${item.count})` : ''}</span>
<div className="connector-line"></div>
</div>
))}
</div>
<div className="center-column">
<div className="main-node">
<span className="vertical-text">双石水库管理处</span>
</div>
</div>
<div className="side-column right">
{rightOrg.map((item, idx) => (
<div
key={idx}
className={`org-node ${item.count === null ? 'placeholder' : ''}`}
style={{cursor:'pointer'}}
onClick={() => handleCardClick(item)}
>
<div className="connector-line"></div>
<span className="node-text" style={{borderBottom:"1px solid #00a0e9"}}>
{item.name}
{item.subName && <><br />{item.subName}</>}
{item.count !== null ? `(${item.count})` : ''}
</span>
</div>
))}
{/* Fill empty space to match left side height roughly */}
<div className="org-node placeholder" style={{ opacity: 0 }}>
<div className="connector-line"></div>
</div>
<div className="org-node placeholder" style={{ opacity: 0 }}>
<div className="connector-line"></div>
</div>
</div>
</div>
<OrgnizeModal
visible={modalVisible}
title={modalTitle}
onClose={() => setModalVisible(false)}
/>
</div>
</div>
);
};
export default PerfectSystem;

View File

@ -0,0 +1,206 @@
.perfect-system {
width: 100%;
height: 100%;
color: #fff;
padding: 5px;
overflow-y: auto;
// Scrollbar hidden
&::-webkit-scrollbar {
display: none;
}
.section {
margin-bottom: 10px;
&.mt-15 {
margin-top: 15px;
}
.section-title {
display: flex;
align-items: center;
margin-bottom: 10px;
.arrow-icon {
width: 20px;
height: 18px;
margin-right: 8px;
object-fit: contain;
}
span {
font-size: 14px;
color: #fff;
text-shadow: 0 0 5px rgba(0, 160, 233, 0.5);
}
}
.info-list {
display: flex;
flex-direction: column;
gap: 5px;
.info-item {
display: flex;
align-items: center;
padding: 5px 10px;
background-size: 100% 100%;
background-repeat: no-repeat;
.icon-box {
width: 18px;
height: 18px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
margin-right: 15px;
font-size: 18px;
&.unit {
background: linear-gradient(135deg, #1890ff 0%, #0050b3 100%);
}
&.person {
background: linear-gradient(135deg, #00eaff 0%, #006d75 100%);
}
&.dept {
background: linear-gradient(135deg, #722ed1 0%, #391085 100%);
}
}
.info-content {
display: flex;
align-items: center;
flex: 1;
font-size: 14px;
justify-content: space-between;
.label {
color: #CCF3FF;
margin-right: 10px;
min-width: 120px;
}
.value {
color: #CCF3FF;
font-weight: 500;
}
}
}
}
.org-chart {
display: flex;
justify-content: space-between;
align-items: stretch;
padding: 0 5px;
height: 200px;
position: relative;
.center-column {
flex: 0 0 60px;
display: flex;
justify-content: center;
align-items: center;
z-index: 2;
.main-node {
background: url('../../../../../../../assets/images/card/ognize.png');
background-size: 100% 100%;
background-repeat: no-repeat;
padding: 10px 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.vertical-text {
writing-mode: vertical-rl;
font-size: 16px;
letter-spacing: 4px;
color: #fff;
text-orientation: upright;
}
}
}
.side-column {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 5px 0;
position: relative;
.org-node {
position: relative;
background: url('../../../../../../../assets/images/card/smallCard.png');
background-size: 100% 100%;
background-repeat: no-repeat;
border-radius: 2px;
padding: 5px;
text-align: center;
color: #fff;
font-size: 13px;
min-height: 32px;
display: flex;
align-items: center;
justify-content: center;
width: 90px;
box-shadow: inset 0 0 10px rgba(0, 160, 233, 0.2);
&.placeholder {
background: transparent;
border: none;
box-shadow: none;
.node-text {
font-size: 20px;
color: rgba(255, 255, 255, 0.5);
// border-bottom: 1px solid #00a0e9;
}
.connector-line {
border-top-color: rgba(0, 160, 233, 0.3);
}
}
.connector-line {
position: absolute;
top: 50%;
height: 1px;
border-top: 1px dashed #00a0e9;
z-index: 1;
}
}
&.left {
align-items: flex-start;
.org-node {
.connector-line {
left: 100%;
width: 500px; // Large width to ensure it reaches center
z-index: -1;
}
}
}
&.right {
align-items: flex-end;
.org-node {
.connector-line {
right: 100%;
width: 500px; // Large width to ensure it reaches center
z-index: -1;
}
}
}
}
}
}
}

View File

@ -0,0 +1,62 @@
import React, { useState, useEffect } from 'react';
import CommonModal from '@/views/Home/components/UI/CommonModal';
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
import './index.less';
const GalleryModal = ({ visible, onClose, title, data = [] }) => {
const [selectedIndex, setSelectedIndex] = useState(0);
useEffect(() => {
if (visible) {
setSelectedIndex(0);
}
}, [visible]);
if (!visible) return null;
const currentItem = data[selectedIndex] || {};
const handlePrev = () => {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : data.length - 1));
};
const handleNext = () => {
setSelectedIndex((prev) => (prev < data.length - 1 ? prev + 1 : 0));
};
return (
<CommonModal
visible={visible}
onClose={onClose}
title={title}
width={'60%'}
>
<div className="gallery-modal-content">
<div className="main-image-container">
<img src={currentItem.url} alt={currentItem.name} className="main-image" />
</div>
<div className="image-info">
图片名称{currentItem.name}
</div>
<div className="thumbnail-strip">
<div className="scroll-btn left" onClick={handlePrev}><LeftOutlined /></div>
<div className="thumbnails-wrapper">
{data.map((item, index) => (
<div
key={index}
className={`thumbnail-item ${index === selectedIndex ? 'thumb-active' : ''}`}
onClick={() => setSelectedIndex(index)}
>
<img src={item.url} alt={item.name} />
</div>
))}
</div>
<div className="scroll-btn right" onClick={handleNext}><RightOutlined /></div>
</div>
</div>
</CommonModal>
);
};
export default GalleryModal;

View File

@ -0,0 +1,116 @@
.gallery-modal-content {
display: flex;
flex-direction: column;
height: 700px;
color: #fff;
.main-image-container {
flex: 1;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background: #000;
overflow: hidden;
.main-image {
width: 100%;
height: 100%;
// object-fit: contain;
}
.nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
// background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 20px;
color: #fff;
transition: all 0.3s;
&:hover {
background: rgba(0, 160, 233, 0.8);
}
&.prev { left: 20px; }
&.next { right: 20px; }
}
}
.image-info {
height: 40px;
line-height: 40px;
text-align: center;
font-size: 14px;
}
.thumbnail-strip {
height: 100px;
// background: #111;
display: flex;
align-items: center;
padding: 0 10px;
border-top: 1px solid #0181e6;
.scroll-btn {
width: 30px;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 18px;
&:hover { color: #00a0e9; }
}
.thumbnails-wrapper {
flex: 1;
display: flex;
gap: 10px;
overflow-x: auto;
padding: 10px;
&::-webkit-scrollbar {
height: 4px;
background: #333;
}
&::-webkit-scrollbar-thumb {
background: #666;
border-radius: 2px;
}
.thumbnail-item {
width: 120px;
height: 70px;
flex-shrink: 0;
cursor: pointer;
border: 2px solid transparent;
transition: all 0.2s;
user-select: none;
outline: none;
background: transparent;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
&.thumb-active {
border-color: #00a0e9;
}
&:hover {
border-color: rgba(0, 160, 233, 0.6);
}
}
}
}
}

View File

@ -0,0 +1,109 @@
import React, { useState, useEffect } from 'react';
import { Table, Input, Button } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
import CommonModal from '@/views/Home/components/UI/CommonModal';
import usePageTable from '@/components/crud/usePageTable';
import { createCrudService } from '@/components/crud/_';
import apiurl from '@/service/apiurl';
import './index.less';
const MaterialModal = ({ visible, onClose }) => {
const unitType = {
1:'个',
2:'件',
3:'米',
4:'把',
5:'台',
6:'套',
7:'副',
8:'箱',
9:'卷',
10:'立方米',
11:'平方米',
}
const [searchText, setSearchText] = useState('');
const columns = [
{ title: '序号', dataIndex: 'inx', key: 'inx', width: 60, align: 'center' },
{
title: '物资名称',
dataIndex: 'goodsName',
key: 'goodsName',
align: 'center',
sorter: (a, b) => (a.goodsName || '').localeCompare(b.goodsName || ''),
showSorterTooltip: { title: '整理' }
},
{
title: '物资类型', dataIndex: 'goodsType', key: 'goodsType', align: 'center',
render: (_, record) => <span>{ record.goodsType === 1 ? "抢险物资" : "救生器材"}</span>
},
{ title: '规格', dataIndex: 'spec', key: 'spec', align: 'center' },
{
title: '单位', dataIndex: 'unit', key: 'unit', align: 'center',
render: (v) => <span>{unitType[v]}</span>
},
{ title: '库存数量', dataIndex: 'storeQuantity', key: 'storeQuantity', align: 'center' },
{
title: '仓库地点',
dataIndex: 'storeLocation',
key: 'storeLocation',
align: 'center',
sorter: (a, b) => (a.storeLocation || '').localeCompare(b.storeLocation || ''),
showSorterTooltip: { title: '整理' }
},
{ title: '联系人', dataIndex: 'contactPerson', key: 'contactPerson', align: 'center' },
{ title: '联系电话', dataIndex: 'phone', key: 'phone', align: 'center' },
];
const { tableProps, search} = usePageTable(createCrudService(apiurl.sz.jqjz.wzPage).find);
const handleSearch = () => {
let params = {
search: {
goodsName:searchText
}
}
search(params);
};
useEffect(() => {
if (visible) {
handleSearch()
}
setSearchText('')
}, [visible]);
return (
<CommonModal
visible={visible}
onClose={onClose}
title="防汛物资"
width="70%"
>
<div className="material-modal-content">
<div className="search-bar">
<span className="label">物资名称</span>
<Input
value={searchText}
onChange={e => setSearchText(e.target.value)}
placeholder="请输入物资名称"
style={{ width: 240 }}
allowClear
/>
<Button type="primary" icon={<SearchOutlined />} onClick={handleSearch} className="search-btn ant-btn-ghost-blue">查询</Button>
</div>
<div className="table-wrapper">
<Table
columns={columns}
{...tableProps}
className="custom-table"
size="middle"
/>
</div>
</div>
</CommonModal>
);
};
export default MaterialModal;

View File

@ -0,0 +1,24 @@
.material-modal-content {
padding: 10px;
color: #fff;
.search-bar {
display: flex;
align-items: center;
margin-bottom: 20px;
.label {
font-size: 14px;
margin-right: 10px;
color: #fff;
}
.search-btn {
margin-left: 10px;
}
}
}

View File

@ -0,0 +1,187 @@
import React, { useState,useEffect } from 'react';
import moment from 'moment';
import arrowIcon from '@/assets/images/card/arrow.png';
import smallCard from '@/assets/images/card/smallCard.png';
import homeImg from '@/assets/images/home.png'; // Placeholder for gallery
import jingfeiIcon from '@/assets/images/card/jingfei.png';
import lightBg from '@/assets/images/card/light.png';
import GalleryModal from './GalleryModal';
import MaterialModal from './MaterialModal';
import YearSelect from '@/views/Home/components/UI/YearSelect';
import apiurl from '@/service/apiurl';
import { httpget } from '@/utils/request';
import { config } from '@/config';
import './index.less';
const SoundMechanism = () => {
const [year, setYear] = useState(moment().format('YYYY'));
const [modalVisible, setModalVisible] = useState(false);
const [materialVisible, setMaterialVisible] = useState(false);
const [modalTitle, setModalTitle] = useState('');
const [galleryData, setGalleryData] = useState([]);
const [manageInfo, setManageInfo] = useState({}) //管理设施
const [budgetInfo, setBudgetInfo] = useState({}) //经费
// Mock data for gallery
const houseImages = [
{ name: '管理用房.jpg', url: homeImg },
{ name: '监控室.jpg', url: homeImg },
{ name: '水库全景.jpg', url: homeImg },
{ name: '会议室.jpg', url: homeImg },
{ name: '值班室.jpg', url: homeImg },
{ name: '物资仓库.jpg', url: homeImg },
];
// Facility Data (Mocked as per UI)
const facilities = [
{ value: manageInfo?.managementHousing??'-', unit: 'm²', label: '管理用房', underline: true, clickable: true},
{ value: manageInfo?.rainWaterCount??'-', unit: '个', label: '雨水情测报' },
{ value: manageInfo?.safeCheckCount??'-', unit: '个', label: '安全监测设施' },
{ value: manageInfo?.cctvCount??'-', unit: '个', label: '视频监控设施' },
{ value: manageInfo?.goodsTypeCount??'-', unit: '项', label: '防汛物资种类', underline: true, clickable: true, type: 'material' },
{ value: manageInfo?.roadLength??'-', unit: '米', label: '防汛道路' },
];
const handleCardClick = (item) => {
if (!item.clickable) return;
if (item.label == '管理用房') {
getManagePic()
} else if (item.type === 'material') {
setMaterialVisible(true);
} else {
setModalTitle(item.label);
setModalVisible(true);
}
};
// Funding Data (Mocked as per UI)
const fundingData = [
{ label: '年度收入预算', value: budgetInfo?.annualExpenditureBudget ?? '-', unit: '万元' },
{ label: '年度支出预算', value: budgetInfo?.annualIncomeBudget ?? '-', unit: '万元' },
];
// 获取管理设施
const getManage= async () => {
try {
const result = await httpget(apiurl.sz.jqjz.manageInfo)
if (result.code == 200) {
setManageInfo(result?.data)
}
} catch (error) {
console.log(error);
}
}
// 获取管理用房图片
const getManagePic= async () => {
try {
const result = await httpget(apiurl.sz.jqjz.managePic)
if (result.code == 200) {
const files = result.data?.files || [];
if (files.length > 0) {
setGalleryData(files.map(item=>({name:item.fileName,url:config.minioIp +item.filePath})))
setModalTitle('管理用房');
setModalVisible(true);
}
}
} catch (error) {
console.log(error);
}
}
// 获取年度预算
const getBudget = async (params) => {
try {
const result = await httpget(apiurl.sz.jqjz.budgetInfo + params)
if (result.code == 200) {
setBudgetInfo(result.data)
}
} catch (error) {
console.log(error);
}
}
useEffect(() => {
getBudget(year)
}, [year])
useEffect(() => {
getManage()
}, [])
return (
<div className="sound-mechanism">
{/* Section 1: Management Facilities */}
<div className="section">
<div className="section-title">
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
<span>管理设施</span>
</div>
<div className="facility-grid">
{facilities.map((item, index) => (
<div
key={index}
className={`facility-card ${item.clickable ? 'clickable' : ''}`}
style={{ backgroundImage: `url(${smallCard})` }}
onClick={() => handleCardClick(item)}
>
<div className={`value-wrapper ${item.underline ? 'underlined' : ''}`}>
<span className="value">{item.value}</span>
<span className="unit">{item.unit}</span>
</div>
<div className="label">{item.label}</div>
</div>
))}
</div>
</div>
<GalleryModal
visible={modalVisible}
title={modalTitle}
onClose={() => setModalVisible(false)}
data={galleryData}
/>
<MaterialModal
visible={materialVisible}
onClose={() => setMaterialVisible(false)}
/>
{/* Section 2: Funding Guarantee */}
<div className="section mt-15">
<div className="section-header">
<div className="title-wrapper">
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
<span>经费保障</span>
</div>
<YearSelect
value={year}
onChange={setYear}
/>
</div>
<div className="funding-container">
{fundingData.map((item, index) => (
<div key={index} className="funding-item">
<div className="icon-wrapper" style={{width:40}}>
<img src={jingfeiIcon} alt="icon" className="jingfei-icon" />
</div>
<div
className="content"
style={{ backgroundImage: `url(${lightBg})` }}
>
<div className="value-row">
<span className="value">{item.value}</span>
<span className="unit">{item.unit}</span>
</div>
<div className="label">{item.label}</div>
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default SoundMechanism;

View File

@ -0,0 +1,160 @@
.sound-mechanism {
width: 100%;
height: 100%;
color: #fff;
padding: 5px;
overflow-y: hidden;
// Scrollbar hidden
&::-webkit-scrollbar {
display: none;
}
.section {
margin-bottom: 0px;
&.mt-15 { margin-top: 5px; }
.section-title, .section-header .title-wrapper {
display: flex;
align-items: center;
margin-bottom: 10px;
.arrow-icon {
width: 20px;
height: 18px;
margin-right: 8px;
object-fit: contain;
}
span {
font-size: 14px;
color: #fff;
text-shadow: 0 0 5px rgba(0, 160, 233, 0.5);
}
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.facility-grid {
display: flex;
flex-wrap: wrap;
.facility-card {
width: calc((100% - 20px) / 3);
height: 70px;
margin-bottom: 15px;
margin-right: 10px;
&:nth-child(3n) { margin-right: 0; }
background-size: 100% 100%;
background-repeat: no-repeat;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 5px;
&.clickable {
cursor: pointer;
}
.value-wrapper {
margin-bottom: 4px;
&.underlined {
border-bottom: 1px solid #00a0e9;
padding-bottom: 2px;
}
.value {
font-size: 20px;
font-weight: bold;
color: #00D8FF;
margin-right: 4px;
}
.unit {
font-size: 12px;
color: rgba(255, 255, 255);
}
}
.label {
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
}
}
}
.funding-container {
display: flex;
justify-content: space-between;
padding: 0 10px;
margin-top: 15px;
.funding-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
position: relative;
// Add a glow effect at the bottom like in the UI
&::after {
content: '';
position: absolute;
bottom: -10px;
left: 20%;
width: 60%;
height: 10px;
background: radial-gradient(ellipse at center, rgba(0, 160, 233, 0.4) 0%, rgba(0,0,0,0) 70%);
z-index: 0;
}
.icon-wrapper {
position: relative;
z-index: 1;
.jingfei-icon {
width: 60px;
height: 60px;
object-fit: contain;
}
}
.content {
display: flex;
flex-direction: column;
z-index: 1;
background-size: 100% 100%;
background-repeat: no-repeat;
justify-content: center;
padding-left: 10px;
width: 140px;
height: 60px;
.value-row {
.value {
font-size: 20px;
font-weight: bold;
color: #00D8FF;
margin-right: 4px;
}
.unit {
font-size: 14px;
color: #fff;
}
}
.label {
font-size: 14px;
color: #fff;
margin-top: 2px;
}
}
}
}
}
}

View File

@ -1,16 +1,36 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import CommonCard from '../../UI/CommonCard';
import PerfectSystem from './components/PerfectSystem';
import SoundMechanism from './components/SoundMechanism';
import { httppost } from '@/utils/request';
import apiurl from '@/service/apiurl';
import './index.less';
const SiZhi = () => {
const [infos, setInfos] = useState({});
const getInfo = async () => {
try {
const result = await httppost(apiurl.sq.qfg.info);
if (result.code == 200) {
const info = result.data[0];
setInfos(info);
}
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getInfo();
}, []);
return (
<div className="sizhi-view">
<div className="side-panel left">
<CommonCard title="完善体系" className="panel-card card-1">
<div className="placeholder-content">内容填充区域</div>
<PerfectSystem data={infos}/>
</CommonCard>
<CommonCard title="健全机制" className="panel-card card-2">
<div className="placeholder-content">内容填充区域</div>
<SoundMechanism />
</CommonCard>
</div>

View File

@ -1,22 +1,27 @@
import React from 'react';
import { Select } from 'antd';
import { DatePicker } from 'antd';
import moment from 'moment';
import './index.less';
const { Option } = Select;
const YearSelect = ({ value, onChange, className, style, ...props }) => {
const handleChange = (date, dateString) => {
if (onChange) {
onChange(dateString);
}
};
const YearSelect = ({ defaultValue = "2025", style, className, ...props }) => (
<Select
defaultValue={defaultValue}
style={{ width: 80, color: '#fff',marginRight:15,marginTop:5, ...style }}
bordered={false}
dropdownClassName="year-select-dropdown"
className={`year-select ${className || ''}`}
return (
<DatePicker
picker="year"
value={value ? moment(value, 'YYYY') : null}
onChange={handleChange}
className={`custom-year-select ${className || ''}`}
dropdownClassName="custom-year-select-dropdown"
style={style}
allowClear={false}
{...props}
>
<Option value="2025">2025</Option>
<Option value="2024">2024</Option>
<Option value="2023">2023</Option>
</Select>
);
/>
);
};
export default YearSelect;

View File

@ -1,23 +1,81 @@
.year-select {
color: #fff;
.ant-select-selector {
.custom-year-select {
background: transparent !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important; // Added border
border-radius: 4px; // Optional: rounded corners for better look
width: 90px;
input {
color: #fff !important;
background-color: transparent !important;
font-size: 16px;
font-weight: normal; // Changed from bold to normal
cursor: pointer;
}
.ant-select-arrow {
&:hover {
border-color: #00a0e9 !important; // Highlight border on hover
}
.ant-picker-suffix {
color: #00a0e9;
}
.ant-picker-clear {
background: transparent;
color: #fff;
}
&.ant-picker-focused {
box-shadow: none;
}
}
// Global styles for dropdown (since it renders in body)
.year-select-dropdown {
background-color: rgba(0, 20, 50, 0.9) !important;
border: 1px solid rgba(0, 160, 233, 0.3);
.custom-year-select-dropdown {
background-color: rgba(0, 40, 70, 0.95) !important;
border: 1px solid #00a0e9;
.ant-select-item {
.ant-picker-header {
color: #fff;
&:hover, &.ant-select-item-option-selected {
background-color: rgba(0, 160, 233, 0.3);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
button {
color: #fff;
&:hover { color: #00a0e9; }
}
.ant-picker-header-super-prev-btn, .ant-picker-header-super-next-btn {
color: #fff;
}
}
.ant-picker-body {
color: #fff;
}
.ant-picker-content {
th, td { color: #fff; }
}
.ant-picker-cell {
color: rgba(255, 255, 255, 0.6);
&:hover .ant-picker-cell-inner {
background-color: rgba(0, 160, 233, 0.3) !important;
}
}
.ant-picker-cell-in-view {
color: #fff;
}
.ant-picker-cell-selected .ant-picker-cell-inner {
background-color: #00a0e9 !important;
color: #fff;
}
.ant-picker-year-panel {
.ant-picker-cell-inner {
color: #fff;
&:hover {
background: rgba(0, 160, 233, 0.3);
}
}
}
}