diff --git a/src/assets/layout/dt.png b/src/assets/layout/dt.png
new file mode 100644
index 0000000..6f42e6a
Binary files /dev/null and b/src/assets/layout/dt.png differ
diff --git a/src/assets/layout/dt@2x.png b/src/assets/layout/dt@2x.png
new file mode 100644
index 0000000..a62ba30
Binary files /dev/null and b/src/assets/layout/dt@2x.png differ
diff --git a/src/assets/layout/kqmj.png b/src/assets/layout/kqmj.png
new file mode 100644
index 0000000..cbe2963
Binary files /dev/null and b/src/assets/layout/kqmj.png differ
diff --git a/src/assets/layout/kqmj@2x.png b/src/assets/layout/kqmj@2x.png
new file mode 100644
index 0000000..524c018
Binary files /dev/null and b/src/assets/layout/kqmj@2x.png differ
diff --git a/src/assets/layout/scgm.png b/src/assets/layout/scgm.png
new file mode 100644
index 0000000..7dc54ca
Binary files /dev/null and b/src/assets/layout/scgm.png differ
diff --git a/src/assets/layout/scgm@2x.png b/src/assets/layout/scgm@2x.png
new file mode 100644
index 0000000..6e8d25b
Binary files /dev/null and b/src/assets/layout/scgm@2x.png differ
diff --git a/src/components/CustomPages/BI/BI064FormRunAnalysis.js b/src/components/CustomPages/BI/BI064FormRunAnalysis.js
index 4be82a9..8902ed2 100644
--- a/src/components/CustomPages/BI/BI064FormRunAnalysis.js
+++ b/src/components/CustomPages/BI/BI064FormRunAnalysis.js
@@ -1,14 +1,23 @@
import React from 'react';
import { connect } from 'dva';
+import moment from 'moment'
import { initFilter } from '../../../utils/common';
-import { Table, Row, Spin, Card, Modal, Col, Button, Form } from 'antd';
+import { Table, Row, Spin, Card, Modal, DatePicker,Col, Button, Form ,Select} from 'antd';
import DropDownPagination from '../../common/DropDownPaginationEx';
+const { Option } = Select;
class BI064FormRunAnalysis extends React.Component {
constructor(props) {
super(props);
this.state = {
depart: {},
inputText: '',
+ inputText2: '',
+ inputText3: '',
+ startTime:moment(this.getDate(), 'YYYY-MM-DD 00:00:00'),//moment(new Date()).add(-1, "month").format('YYYY-MM-DD 00:00:00'),
+ endTime:moment(this.getEndDate(), 'YYYY-MM-DD 23:59:59'),
+ selectForms:['安全风险辨识与评估计划','安全风险辨识表', '安全风险辨识结果表', '安全风险辨识结果汇总表', '安全风险评估表',
+ '安全风险评估汇总表', '风险检查关联表','安全检查通知','安全检查记录','安全检查记录汇总','隐患上报','手动隐患上报完善',
+ '隐患确认单','隐患整改通知单', '隐患整改记录', '延期整改申请'],
retData: [], // 表单运行数据
companyData: [], // 公司数据
loading: true,
@@ -65,7 +74,50 @@ class BI064FormRunAnalysis extends React.Component {
],
};
}
+ getDate=()=>{
+ let date = new Date()
+ let y = date.getFullYear()
+ let m = date.getMonth() + 1
+ let d = date.getDate();
+ // 当前日期
+ let nowDate = y + "-" + (m < 10 ? "0" + m : m) + "-" + (d < 10 ? "0" + d : d);
+ // 一月的时候年份要减一
+ // if(m == 1) {
+ // y--
+ // m = 12
+ // }else if(m == 3 && d > 28) {
+
+ // //三月要考虑是否为闰年
+ // m--
+ // if(y%4 == 0 && y%100!=0 || y%400 == 0) {
+ // d = 29
+ // }else {
+ // d-28
+ // }
+ // }else if((m != 12 || m != 8 )&& d == 31) {
+
+ // //31号的月份要考虑上个月是否有31号
+ // m--
+ // d = 30
+ // }else {
+ // m--
+ // }
+ // 一个月前的日期(默认月份)
+ //let pastDate = y + (m < 10 ? "0" + m : m) + (d < 10 ? "0" + d : d)
+ let pastDate = moment(nowDate).add(-1, "day").format('YYYY-MM-DD 00:00:00')
+ return pastDate;
+}
+getEndDate=()=>{
+ let date = new Date()
+ let y = date.getFullYear()
+ let m = date.getMonth() + 1
+ let d = date.getDate();
+ // 当前日期
+ let nowDate = y + "-" + (m < 10 ? "0" + m : m) + "-" + (d < 10 ? "0" + d : d);
+ let pastDate = moment(nowDate).add(-1, "day").add(23,'hour').add(59,'m').add(59,'s')//format('YYYY-MM-DD 23:59:59')
+ return pastDate;
+}
componentDidMount() {
this.loadData();
}
@@ -84,6 +136,49 @@ class BI064FormRunAnalysis extends React.Component {
inputText: record ? record?.NAME : this.state.inputText,
});
};
+ handleSearch2 = (value) => {
+ let temps = ['安全风险辨识与评估计划','安全风险辨识表', '安全风险辨识结果表', '安全风险辨识结果汇总表', '安全风险评估表',
+ '安全风险评估汇总表', '风险检查关联表','安全检查通知','安全检查记录','安全检查记录汇总','隐患上报','手动隐患上报完善',
+ '隐患确认单','隐患整改通知单', '隐患整改记录', '延期整改申请'];
+
+ if(value === "双重预防机制" )
+ {
+ temps = ['安全风险辨识与评估计划','安全风险辨识表', '安全风险辨识结果表', '安全风险辨识结果汇总表', '安全风险评估表',
+ '安全风险评估汇总表', '风险检查关联表','安全检查通知','安全检查记录','安全检查记录汇总','隐患上报','手动隐患上报完善',
+ '隐患确认单','隐患整改通知单', '隐患整改记录', '延期整改申请'];
+ }
+ if(value === "作业现场安全管理" )
+ {
+
+ temps = [ '班前会议', '岗位当班记录', '交接班记录', '班组活动记录','一般作业活动记录', '作业方案讨论记录','关键许可工作票',
+ '安全技术交底表','关键许可作业活动记录','矿山动火作业工作票','技术交底表','安全交底表','领导带班下井记录'];
+ }
+ if(value === "安全教育与培训" )
+ {
+
+ temps = ['安全意识调查', '培训需求调查','培训通知', '培训通知(转训)','培训记录', '培训效果评估调查表', '三级安全教育培训记录',
+ '新三级安全教育记录', '新三级安全教育卡' ];
+ }
+ this.setState({
+ inputText2: value,
+ selectForms:temps
+ })
+ }
+ handleSearch3 = (value) => {
+ this.setState({
+ inputText3: value
+ })
+ }
+ startChange=(value)=>{
+ this.setState({
+ startTime:value
+ })
+ };
+ endChange=(value)=>{
+ this.setState({
+ endTime:value
+ })
+ };
// 获取公司数据
getBaseData = () => {
const json = initFilter(this.props.login.OrgId);
@@ -111,7 +206,7 @@ class BI064FormRunAnalysis extends React.Component {
// 获取表单运行数据
getrealData = () => {
// const json = initFilter(this.props.login.OrgId);
- let json = initFilter('00300000-0000-0000-0000-000000000000', this.state.inputText);
+ let json = initFilter('00300000-0000-0000-0000-000000000000', this.state.inputText,"","","",this.state.startTime,this.state.endTime);
this.props.dispatch({
type: 'app/getDataByPost',
url: 'BI/BIStatiscialAnalysisController/GetTaskViewInfo',
@@ -119,6 +214,14 @@ class BI064FormRunAnalysis extends React.Component {
onlyData: false,
onComplete: (ret) => {
if (ret && ret.Data) {
+ if(this.state.inputText2)
+ {
+ ret.Data= ret.Data.filter((t) => t.MOULD_NAME === this.state.inputText2);
+ }
+ if(this.state.inputText3)
+ {
+ ret.Data= ret.Data.filter((t) => t.FORM_NAME === this.state.inputText3);
+ }
this.setState({ retData: ret.Data, loading: false }, () => {
this.processData();
});
@@ -134,6 +237,7 @@ class BI064FormRunAnalysis extends React.Component {
const { retData, companyData, inputText } = this.state;
if (!retData.length || !companyData.length) return;
let companyDataTemp = companyData.filter((t) => t.NAME === inputText);
+
// 获取所有模块和表单的列表
const modules = [...new Set(retData.map((item) => item.MOULD_NAME))];
const forms = [];
@@ -454,7 +558,7 @@ class BI064FormRunAnalysis extends React.Component {
};
render() {
- const { tableData, columns, loading } = this.state;
+ const { tableData, columns, loading,selectForms } = this.state;
const formItemLayout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
@@ -516,7 +620,66 @@ class BI064FormRunAnalysis extends React.Component {
/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layout/FullOther/HomeContent.js b/src/layout/FullOther/HomeContent.js
index e5fa090..45851c5 100644
--- a/src/layout/FullOther/HomeContent.js
+++ b/src/layout/FullOther/HomeContent.js
@@ -939,6 +939,79 @@ class HomeContent extends React.Component {
{/* 右侧区域 */}
+
+ {/*
*/}
+
+
集团企业信息汇总
+
+
集团公司
+
吐鲁番金源矿冶有限责任公司
+
子公司数量:{trainingData?.SumSubCount || '-'}家
+
+
+ {trainingData.listSubTypeCount && Object.keys(trainingData.listSubTypeCount).length > 0 ? (
+ <>
+
+
+ {Object.entries(trainingData.listSubTypeCount).map(([key, value], index) => (
+
+ {key}
+ {value}家
+
+ ))}
+
+
+ >
+ ) : (
+ // 默认显示(当没有数据时,保持原有3个的样子)
+
+
+ 露天矿
+ 1家
+
+
+ 地下矿
+ 12家
+
+
+ 尾矿库
+ 13家
+
+
+ )}
+
+
+
+
+
})
+
+
+
生产规模
+
+ {trainingData?.SumSubMode || '-'}吨/年
+
+
+
+
+
+
})
+
+
+
矿区面积
+
+ {trainingData?.SumSubArea || '-'}平方公里
+
+
+
+
+
+
+
+
+
@@ -958,12 +1031,6 @@ class HomeContent extends React.Component {
-
-
-
-
{/* 公告详情弹窗 */}
diff --git a/src/layout/FullScreenInter.js b/src/layout/FullScreenInter.js
index 3a184d9..487e7bf 100644
--- a/src/layout/FullScreenInter.js
+++ b/src/layout/FullScreenInter.js
@@ -308,16 +308,7 @@ class FullScreen extends React.Component {
onComplete: (ret) => {
if (ret && !this.isUnmounted) {
this.setState({
- trainingData: {
- listNAME: ret.listNAME || [],
- YearCount: ret.YearCount || [],
- MonthRecordCount: ret.MonthRecordCount || [],
- MonthPersonCount: ret.MonthPersonCount || [],
- listAnnourcement: ret.listAnnourcement || [],
- TITLE: ret.TITLE || '',
- listVideoImg: ret.listVideoImg || [],
- playSet: ret.playSet || [],
- },
+ trainingData: ret,
});
}
},
diff --git a/src/layout/fullinter.less b/src/layout/fullinter.less
index acb7f3f..ac31893 100644
--- a/src/layout/fullinter.less
+++ b/src/layout/fullinter.less
@@ -162,6 +162,277 @@
flex-direction: column;
overflow: hidden;
}
+.infoContainer {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: 20px;
+ height: 100%;
+}
+.infoTitle {
+ font-size: 20px;
+ font-weight: bold;
+ text-align: left;
+ color: #000;
+ margin-bottom: 24px;
+}
+.infoCard {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ position: relative;
+ background: url('../assets/layout/dt.png') no-repeat center center;
+ background-size: cover;
+ border-radius: 10px;
+ padding: 28px 28px 36px 28px;
+ align-items: flex-start;
+}
+.infoCardMark {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ background-color: #1668fe;
+ padding: 6px 17px;
+ border-radius: 0 10px;
+ font-size: 14px;
+ color: #fff;
+}
+.infoCardTitle {
+ font-size: 20px;
+ color: #082a61;
+ font-weight: bold;
+ text-align: left;
+ margin-bottom: 14px;
+}
+.infoCardDetail {
+ font-size: 13px;
+ color: #1f69ff;
+ text-align: left;
+ background: rgba(255, 255, 255, 0.5);
+ border-radius: 13px;
+ padding: 4px 16px;
+}
+// 替换原有的 .infoBanner 样式
+.infoBanner {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ align-items: center;
+ margin: 20px 0;
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+// 滚动容器
+.bannerScrollContainer {
+ flex: 1;
+ overflow-x: auto;
+ overflow-y: hidden;
+ scroll-behavior: smooth;
+ -webkit-overflow-scrolling: touch;
+
+ // 隐藏滚动条(可选,如果想要保持美观)
+ &::-webkit-scrollbar {
+ height: 4px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 2px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #ccc;
+ border-radius: 2px;
+
+ &:hover {
+ background: #999;
+ }
+ }
+}
+
+.bannerScrollContent {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-around; // 保持平均分布
+ gap: 30px;
+ padding: 0 10px;
+}
+
+// 当项目数量 <= 3 时,每个项目平均分布
+.infoBanner:not(.hasScroll) .infoSpan {
+ flex: 1;
+}
+
+// 修改 infoSpan 内部样式 - 保持固定宽度
+.infoSpan {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ flex-shrink: 0; // 防止被压缩
+ width: 100px; // 固定宽度,保持和3个时一样大
+ min-width: 100px;
+
+ > span:first-child {
+ color: #5f7291;
+ font-size: 18px;
+ font-weight: 500;
+ white-space: nowrap;
+ }
+
+ > span:last-child {
+ color: #000;
+ font-size: 27px;
+ font-weight: bold;
+ white-space: nowrap;
+ }
+
+ // 添加分割线效果
+ position: relative;
+
+ &:not(:last-child)::after {
+ content: '';
+ position: absolute;
+ right: -15px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 1px;
+ height: 40px;
+ background: linear-gradient(to bottom, transparent, #ccc, transparent);
+ }
+}
+
+// 左右箭头样式
+.bannerArrowLeft,
+.bannerArrowRight {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 32px;
+ height: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ z-index: 10;
+ background-color: rgba(0, 0, 0, 0.4);
+ border-radius: 4px;
+ transition: all 0.3s ease;
+
+ i,
+ .anticon {
+ font-size: 18px;
+ color: #fff;
+ }
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.7);
+ transform: translateY(-50%) scale(1.05);
+ }
+}
+
+.bannerArrowLeft {
+ left: 0;
+}
+
+.bannerArrowRight {
+ right: 0;
+}
+
+// 修改 infoSpan 内部样式
+.infoSpan {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px; // 标题和数值间距 10px
+
+ > span:first-child {
+ color: #5f7291;
+ font-size: 18px;
+ font-weight: 500;
+ }
+
+ > span:last-child {
+ color: #000;
+ font-size: 27px;
+ font-weight: bold;
+ }
+}
+.infoBottom {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ align-items: center;
+ background-color: #f5f7fc;
+ border-radius: 10px;
+ // padding: 26px;
+ margin-top: 10px;
+ position: relative;
+
+ .infoBottomContent {
+ flex: 1;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ gap: 12px;
+ position: relative;
+ padding: 26px;
+
+ &:not(:last-child)::after {
+ content: '';
+ position: absolute;
+ right: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 2px;
+ height: 60px;
+ background: linear-gradient(to bottom, transparent, #e6e9f3, transparent);
+ }
+ }
+}
+
+.infoBottomImg {
+ width: 40px;
+ height: 40px;
+ flex-shrink: 0;
+ margin-right: 10px;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ }
+}
+
+.infoBottomContentRight {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+}
+
+.infoBottomTitle {
+ font-size: 18px;
+ color: #5f7291;
+ margin-bottom: 4px;
+ font-weight: 500;
+ white-space: nowrap;
+}
+
+.infoBottomNum {
+ font-size: 16px;
+ color: #1d6fe9;
+
+ white-space: nowrap;
+ span {
+ font-size: 20px;
+ font-weight: bold;
+ }
+}
// 左侧和右侧卡片样式
.riskCard {
diff --git a/src/routes/Home.js b/src/routes/Home.js
index ea8c55a..189eaf7 100644
--- a/src/routes/Home.js
+++ b/src/routes/Home.js
@@ -2,7 +2,17 @@ import React, { useState, useEffect, useRef, Component } from 'react';
import { connect } from 'dva';
import storage from '../utils/storage';
import IconFont from '../utils/iconFont';
-import { initFilter, addRuleAndGroups, guid, extendInclude, extendRule, extend, extendOrder } from '../utils/common';
+import {
+ initFilter,
+ addRuleAndGroups,
+ guid,
+ extendInclude,
+ extendRule,
+ extend,
+ extendOrder,
+ showFiles,
+ GetFileModel,
+} from '../utils/common';
import './home.less';
// 组件库
import {
@@ -28,6 +38,7 @@ import FormPage from '../components/FormPage';
import backlog from '../assets/home/backlog.png';
import backlog_i from '../assets/home/backlog_icon.png';
import styles from './dashboard.css';
+import styleshome from './annourcement.less';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import moment from 'moment';
@@ -182,6 +193,13 @@ class Home extends React.Component {
},
taskSelects: [],
mineType: localStorage.getItem('currentMineType') === '20' ? '10' : localStorage.getItem('currentMineType'),
+ annourcement: [],
+ announcementDetail: null, // 公告详情
+ announcementModalVisible: false, // 公告弹窗可见性
+ announcementDetailLoading: false, // 公告详情加载状态
+ fileForm: {
+ visible: false,
+ },
};
// this.enums = [];
this.reportType = '';
@@ -618,20 +636,20 @@ class Home extends React.Component {
};
//风险等级占比
riskLevel = () => {
- const userInfo = storage('lacal').getItem('webUserInfo').val;
let json = initFilter(this.props.login.OrgId);
extendOrder(json, 'NUM', 0);
- extendRule(json, 'COLOR', 1, this.state.mineType);
+ extendOrder(json, 'CREATE_TIME', 1);
this.props.dispatch({
type: 'app/getDataByPost',
- url: 'FM/RiskLevelProportion/OrderPaged',
+ url: 'PF/PFAnnourcement/OrderPaged',
payload: json,
onlyData: false,
onComplete: (data) => {
if (data && data.IsSuccessful) {
if (data.Data && data.Data.length > 0) {
+ console.log('风险等级占比', data.Data);
this.setState({
- riskLevelPage: data.Data,
+ annourcement: data.Data,
});
}
}
@@ -1369,9 +1387,141 @@ class Home extends React.Component {
detailForm: detailForm,
});
};
+ // 添加获取公告详情的方法
+ getAnnouncementDetail = (announcement) => {
+ return new Promise((resolve, reject) => {
+ const orgId = storage('lacal').getItem('webOrgId')?.val;
+ const json = initFilter(orgId);
+ extendRule(json, 'ID', 1, announcement.ID);
+ json.Include = ['Nav_Orgs', 'Nav_Orgs.Nav_OrgSub', 'Nav_Files.Nav_ImgFile'];
+
+ this.setState({ announcementDetailLoading: true, announcementDetail: null });
+
+ this.props.dispatch({
+ type: 'app/getDataByPost',
+ payload: json,
+ url: 'PF/PFAnnourcement/Get',
+ onComplete: (ret) => {
+ if (ret) {
+ this.setState({
+ announcementDetail: ret,
+ announcementDetailLoading: false,
+ });
+ resolve(ret);
+ } else {
+ message.error('暂无公告详情');
+ this.setState({ announcementDetailLoading: false });
+ reject(new Error('获取公告详情失败'));
+ }
+ },
+ onError: (error) => {
+ this.setState({ announcementDetailLoading: false });
+ reject(error);
+ },
+ });
+ });
+ };
+
+ // 添加点击公告的处理方法
+ handleAnnouncementClick = async (announcement) => {
+ try {
+ const detail = await this.getAnnouncementDetail(announcement);
+ this.setState({
+ announcementModalVisible: true,
+ });
+ } catch (error) {
+ console.error('获取公告详情失败:', error);
+ message.error('获取公告详情失败');
+ }
+ };
+
+ // 添加关闭公告弹窗的方法
+ handleAnnouncementModalClose = () => {
+ this.setState({
+ announcementModalVisible: false,
+ announcementDetail: null,
+ });
+ };
+
+ // 添加渲染公告弹窗内容的方法
+ renderAnnouncementModal = () => {
+ const { announcementDetail, announcementDetailLoading } = this.state;
+
+ if (announcementDetailLoading) {
+ return (
+
+
+
+ );
+ }
+
+ if (!announcementDetail) return null;
+
+ const { TITLE, ABSTRACT, START, END, CONTENT, CREATE_USER_NAME, Nav_Files = [] } = announcementDetail;
+
+ return (
+
+ {/* 标题 */}
+
+ {TITLE}
+
+
+ {/* 摘要 */}
+ {ABSTRACT && (
+
+ {ABSTRACT}
+
+ )}
+
+ {/* 日期和发布人 */}
+
+ {START ? START.split(' ')[0] : '--'} 至 {END ? END.split(' ')[0] : '--'}
+ {CREATE_USER_NAME && 发布人:{CREATE_USER_NAME}}
+
+
+ {/* 分割线 */}
+
+
+ {/* 正文内容 */}
+
暂无内容
' }}
+ />
+
+ {/* 附件列表 */}
+ {Nav_Files && Nav_Files.length > 0 && (
+
+
+ 附件 ({Nav_Files.length}个)
+
+ {showFiles(Nav_Files, config.picServerHost, this)}
+ {GetFileModel(Modal, FormPage, this, this.state.fileForm.visible)}
+
+ )}
+
+ );
+ };
render() {
- const { tableData } = this.state;
+ const { tableData, annourcement } = this.state;
const thingsData = this.state.delayData;
const settings = {
dots: false,
@@ -1431,6 +1581,20 @@ class Home extends React.Component {
size="small"
/>
+
+ 关闭
+ ,
+ ]}
+ width="600px"
+ bodyStyle={{ padding: '20px', maxHeight: '70vh', overflowY: 'auto' }}
+ >
+ {this.renderAnnouncementModal()}
+
-
-
-
- 风险等级占比
+
+
+
+
+ 公司公告
+
+
共 {annourcement?.length || 0} 条公告
- {this.state.riskLevelPage.length > 0 ? (
-
- this.showDetailModal('BI013_RISKANALYSISMODEL')}>
-
+
+ {annourcement?.length > 0 ? (
+
+ {annourcement.map((item, index) => (
+ - this.handleAnnouncementClick(item)}
+ >
+
+ {item.TITLE}
+
+ {item.START}
+
+ ))}
+
+ ) : (
+
+
+ 暂无公告
-
- ) : null}
+ )}
+
- {this.state.riskLevelPage.length > 0 ? : null}
diff --git a/src/routes/annourcement.less b/src/routes/annourcement.less
new file mode 100644
index 0000000..07e34a0
--- /dev/null
+++ b/src/routes/annourcement.less
@@ -0,0 +1,98 @@
+.announcementCard {
+ flex: 3;
+ background-color: #fff;
+ border-radius: 4px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ min-height: 0;
+ height: 100%;
+}
+
+.announcementHeader {
+ width: 100%;
+ padding: 8px 20px;
+ border-bottom: 2px solid #1890ff;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-shrink: 0;
+}
+
+.announcementTitle {
+ display: flex;
+ align-items: center;
+}
+
+.announcementIcon {
+ font-size: 16px;
+ color: #1890ff;
+ margin-right: 8px;
+}
+
+.announcementTitle span {
+ font-size: 18px;
+ color: #333333;
+}
+
+.announcementCount {
+ font-size: 14px;
+ color: #999;
+}
+
+.announcementList {
+ flex: 1;
+ overflow-y: auto;
+ padding: 8px 0;
+}
+
+.announcementUl {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.announcementItem {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 16px;
+ border-bottom: 1px solid #f0f0f0;
+ cursor: pointer;
+ transition: background-color 0.3s;
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+}
+
+.announcementItemTitle {
+ font-size: 14px;
+ color: #333;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ flex: 1;
+ text-align: left;
+}
+
+.announcementItemTime {
+ font-size: 14px;
+ color: #999;
+ margin-left: 16px;
+ white-space: nowrap;
+}
+
+.emptyAnnouncement {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ color: #999;
+}
+
+.emptyIcon {
+ font-size: 48px;
+ margin-bottom: 16px;
+}