Commit c080970da51be466601206a110f09c20a5bd39a5
1 parent
fd564a27
1.企业公告发布的内容。需要窗口弹出。即登录进去弹出公告内容,内容强制查看,确认已查看,未确认不能关闭,
Showing
1 changed file
with
353 additions
and
4 deletions
src/components/Common/PersonCenter/PersonCenter.js
| @@ -15,9 +15,12 @@ import { | @@ -15,9 +15,12 @@ import { | ||
| 15 | } from '@ant-design/icons'; | 15 | } from '@ant-design/icons'; |
| 16 | 16 | ||
| 17 | import { Form } from '@ant-design/compatible'; | 17 | import { Form } from '@ant-design/compatible'; |
| 18 | -// import '@ant-design/compatible/assets/index.css'; | 18 | +import '@ant-design/compatible/assets/index.css'; |
| 19 | 19 | ||
| 20 | -import { Button, Input, Modal, Menu, Badge, message, Dropdown, Space, notification, Popover, Tabs } from 'antd-v4'; | 20 | +import BraftEditor from 'braft-editor'; |
| 21 | +import 'braft-editor/dist/index.css'; | ||
| 22 | + | ||
| 23 | +import { Button, Input, Modal, Menu, Badge, message, Dropdown, Space, notification, Popover, Tabs, Checkbox } from 'antd'; | ||
| 21 | import styles from '@/routes/indexPage.less'; | 24 | import styles from '@/routes/indexPage.less'; |
| 22 | import config from '@/utils/config'; | 25 | import config from '@/utils/config'; |
| 23 | import * as commonUtils from '@/utils/utils'; | 26 | import * as commonUtils from '@/utils/utils'; |
| @@ -54,6 +57,8 @@ class PersonCenter extends Component { | @@ -54,6 +57,8 @@ class PersonCenter extends Component { | ||
| 54 | addFaceVisible: false, | 57 | addFaceVisible: false, |
| 55 | sUserName: props.app.userinfo.sUserName, | 58 | sUserName: props.app.userinfo.sUserName, |
| 56 | menuSearchPopoverVisible: false, | 59 | menuSearchPopoverVisible: false, |
| 60 | + noticeVisible: false, // 新增:公告弹窗显示状态 | ||
| 61 | + noticeChecked: false, // 新增:复选框选中状态 | ||
| 57 | }; | 62 | }; |
| 58 | } | 63 | } |
| 59 | componentWillMount() { | 64 | componentWillMount() { |
| @@ -67,6 +72,8 @@ class PersonCenter extends Component { | @@ -67,6 +72,8 @@ class PersonCenter extends Component { | ||
| 67 | 72 | ||
| 68 | message.warn(changePwd, 10); | 73 | message.warn(changePwd, 10); |
| 69 | } | 74 | } |
| 75 | + // 检查是否有公告数据需要显示 | ||
| 76 | + this.checkNoticeData(); | ||
| 70 | } | 77 | } |
| 71 | 78 | ||
| 72 | componentWillReceiveProps(nextProps) { | 79 | componentWillReceiveProps(nextProps) { |
| @@ -77,6 +84,8 @@ class PersonCenter extends Component { | @@ -77,6 +84,8 @@ class PersonCenter extends Component { | ||
| 77 | audio.play(); | 84 | audio.play(); |
| 78 | } | 85 | } |
| 79 | } | 86 | } |
| 87 | + // 检查是否有新的公告数据 | ||
| 88 | + this.checkNoticeData(nextProps); | ||
| 80 | } | 89 | } |
| 81 | shouldComponentUpdate(nextProps) { | 90 | shouldComponentUpdate(nextProps) { |
| 82 | const { pwdVisible } = this.state; | 91 | const { pwdVisible } = this.state; |
| @@ -188,6 +197,116 @@ class PersonCenter extends Component { | @@ -188,6 +197,116 @@ class PersonCenter extends Component { | ||
| 188 | dispatch({ type: 'app/addPane', payload: { pane } }); | 197 | dispatch({ type: 'app/addPane', payload: { pane } }); |
| 189 | }; | 198 | }; |
| 190 | 199 | ||
| 200 | + | ||
| 201 | + // 检查公告数据 | ||
| 202 | + checkNoticeData = async (nextProps = null) => { | ||
| 203 | + const props = nextProps || this.props; | ||
| 204 | + const { noticeVisible = false } = this.state; | ||
| 205 | + const msg = props.app.unRead; | ||
| 206 | + const { sModelsId, formSrcRoute = '' } = props; | ||
| 207 | + const msgObj = commonUtils.isJSON(msg) ? JSON.parse(msg) : {}; | ||
| 208 | + const msgData = msgObj.data; /* 推送信息 */ | ||
| 209 | + | ||
| 210 | + // 如果有公告数据且尚未显示过,则显示公告弹窗 | ||
| 211 | + if (commonUtils.isNotEmptyObject(msgData) && commonUtils.isNotEmptyObject(msgData.COPYTO) && | ||
| 212 | + msgData.COPYTO.sMsgId && !noticeVisible) { | ||
| 213 | + /* 根据sMsgId, sSlaveId 获取接口 */ | ||
| 214 | + // eslint-disable-next-line prefer-destructuring | ||
| 215 | + const sMsgId = msgData.COPYTO.sMsgId; | ||
| 216 | + const sMsgSlaveId = msgData.COPYTO.sSlaveId; | ||
| 217 | + // 1. 在调用接口前先检查是否已经显示过该公告 | ||
| 218 | + const noticeKey = `${sMsgId}_${sMsgSlaveId}`; | ||
| 219 | + | ||
| 220 | + const shownNotices = JSON.parse(localStorage.getItem('shownNotices') || '[]'); | ||
| 221 | + | ||
| 222 | + // 如果已经显示过,直接返回,不调用接口 | ||
| 223 | + if (shownNotices.includes(noticeKey)) { | ||
| 224 | + console.log('公告已显示过,跳过接口调用:', noticeKey); | ||
| 225 | + return; | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + const dataUrl = `${commonConfig.server_host}notice/getNoticeDetail?sModelsId=${sModelsId}&sName=${formSrcRoute}`; | ||
| 229 | + const condition = { | ||
| 230 | + sMsgId, | ||
| 231 | + sSlaveId: sMsgSlaveId, | ||
| 232 | + }; | ||
| 233 | + const configReturn = (await commonServices.postValueService(props.app?.token, condition, dataUrl)).data; | ||
| 234 | + if (configReturn?.code === 1) { | ||
| 235 | + const dataReturn = configReturn.dataset.rows; | ||
| 236 | + | ||
| 237 | + if (commonUtils.isNotEmptyArr(dataReturn) && commonUtils.isNotEmptyObject(dataReturn[0])) { | ||
| 238 | + const noticeData = dataReturn[0]; | ||
| 239 | + | ||
| 240 | + // 3. 再次确认是否已显示(防止并发请求) | ||
| 241 | + const currentShownNotices = JSON.parse(localStorage.getItem('shownNotices') || '[]'); | ||
| 242 | + console.log('currentShownNotices1', currentShownNotices); | ||
| 243 | + if (!currentShownNotices.includes(noticeKey)) { | ||
| 244 | + this.setState({ | ||
| 245 | + noticeVisible: true, | ||
| 246 | + noticeData, | ||
| 247 | + noticeTitle: noticeData?.msgData?.sTitle, | ||
| 248 | + noticeChecked: false, | ||
| 249 | + sMsgId, | ||
| 250 | + sMsgSlaveId, | ||
| 251 | + }); | ||
| 252 | + } | ||
| 253 | + } | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + // 处理公告弹窗确定按钮点击 | ||
| 259 | + handleNoticeConfirm = async () => { | ||
| 260 | + if (this.state.noticeChecked) { | ||
| 261 | + /* 调用接口把数据更新为已读 */ | ||
| 262 | + const { app } = this.props; | ||
| 263 | + const { sMsgId, sMsgSlaveId } = this.state; | ||
| 264 | + | ||
| 265 | + const { token } = app; | ||
| 266 | + const configUrl = `${config.server_host}notice/updateNotice?sModelsId=${100}`; | ||
| 267 | + const condition = { | ||
| 268 | + sMsgId, | ||
| 269 | + sSlaveId: sMsgSlaveId, | ||
| 270 | + }; | ||
| 271 | + const configReturn = (await commonServices.postValueService(token, condition, configUrl)).data; | ||
| 272 | + if (configReturn?.code === 1) { | ||
| 273 | + message.success('公告已阅读'); | ||
| 274 | + // 在用户确认阅读后,才将公告记录存储到shownNotices中 | ||
| 275 | + const noticeKey = `${sMsgId}_${sMsgSlaveId}`; | ||
| 276 | + const currentShownNotices = JSON.parse(localStorage.getItem('shownNotices') || '[]'); | ||
| 277 | + | ||
| 278 | + if (!currentShownNotices.includes(noticeKey)) { | ||
| 279 | + currentShownNotices.push(noticeKey); | ||
| 280 | + // 限制存储最近100条记录,防止localStorage过大 | ||
| 281 | + if (currentShownNotices.length > 100) { | ||
| 282 | + currentShownNotices.shift(); | ||
| 283 | + } | ||
| 284 | + localStorage.setItem('shownNotices', JSON.stringify(currentShownNotices)); | ||
| 285 | + } | ||
| 286 | + } else { | ||
| 287 | + message.error(configReturn?.msg); | ||
| 288 | + } | ||
| 289 | + this.setState({ | ||
| 290 | + noticeVisible: false, | ||
| 291 | + }); | ||
| 292 | + } | ||
| 293 | + } | ||
| 294 | + | ||
| 295 | + // 处理公告弹窗取消按钮点击 | ||
| 296 | + handleNoticeCancel = () => { | ||
| 297 | + this.setState({ | ||
| 298 | + noticeVisible: false, | ||
| 299 | + }); | ||
| 300 | + } | ||
| 301 | + | ||
| 302 | + // 处理复选框变化 | ||
| 303 | + handleNoticeCheckChange = (e) => { | ||
| 304 | + this.setState({ | ||
| 305 | + noticeChecked: e.target.checked, | ||
| 306 | + }); | ||
| 307 | + } | ||
| 308 | + | ||
| 309 | + | ||
| 191 | handleSubmitPwd = (e) => { | 310 | handleSubmitPwd = (e) => { |
| 192 | /* 阻止表单提交动作 */ | 311 | /* 阻止表单提交动作 */ |
| 193 | e.preventDefault(); | 312 | e.preventDefault(); |
| @@ -618,6 +737,14 @@ class PersonCenter extends Component { | @@ -618,6 +737,14 @@ class PersonCenter extends Component { | ||
| 618 | handleCancel={this.handleCancel} | 737 | handleCancel={this.handleCancel} |
| 619 | /> | 738 | /> |
| 620 | } | 739 | } |
| 740 | + {/* 新增公告弹窗组件 */} | ||
| 741 | + <PersonCenterNotice | ||
| 742 | + {...props} | ||
| 743 | + {...state} | ||
| 744 | + handleNoticeConfirm={this.handleNoticeConfirm} | ||
| 745 | + handleNoticeCancel={this.handleNoticeCancel} | ||
| 746 | + handleNoticeCheckChange={this.handleNoticeCheckChange} | ||
| 747 | + /> | ||
| 621 | </div> | 748 | </div> |
| 622 | ); | 749 | ); |
| 623 | } | 750 | } |
| @@ -894,13 +1021,13 @@ const PersonCenterOnlineUser = Form.create({ | @@ -894,13 +1021,13 @@ const PersonCenterOnlineUser = Form.create({ | ||
| 894 | const gdsconfigformslaveNew = gdsconfigformslave.map(column => { | 1021 | const gdsconfigformslaveNew = gdsconfigformslave.map(column => { |
| 895 | const { sLanguage } = userinfo || {}; | 1022 | const { sLanguage } = userinfo || {}; |
| 896 | let showName = column.sChinese; // 默认中文 | 1023 | let showName = column.sChinese; // 默认中文 |
| 897 | - | 1024 | + |
| 898 | if (sLanguage === 'sEnglish' && column.sEnglishName) { | 1025 | if (sLanguage === 'sEnglish' && column.sEnglishName) { |
| 899 | showName = column.sEnglishName; | 1026 | showName = column.sEnglishName; |
| 900 | } else if (sLanguage === 'sBig5' && column.sBig5Name) { | 1027 | } else if (sLanguage === 'sBig5' && column.sBig5Name) { |
| 901 | showName = column.sBig5Name; | 1028 | showName = column.sBig5Name; |
| 902 | } | 1029 | } |
| 903 | - | 1030 | + |
| 904 | return { | 1031 | return { |
| 905 | ...column, | 1032 | ...column, |
| 906 | showName | 1033 | showName |
| @@ -975,4 +1102,226 @@ const PersonCenterOnlineUser = Form.create({ | @@ -975,4 +1102,226 @@ const PersonCenterOnlineUser = Form.create({ | ||
| 975 | ); | 1102 | ); |
| 976 | }); | 1103 | }); |
| 977 | 1104 | ||
| 1105 | +// 新增公告弹窗组件 | ||
| 1106 | +const PersonCenterNotice = Form.create({ | ||
| 1107 | + mapPropsToFields(props) { | ||
| 1108 | + }, | ||
| 1109 | +})((props) => { | ||
| 1110 | + const { | ||
| 1111 | + handleNoticeConfirm, | ||
| 1112 | + handleNoticeCancel, | ||
| 1113 | + handleNoticeCheckChange, | ||
| 1114 | + noticeVisible, | ||
| 1115 | + noticeData = {}, | ||
| 1116 | + noticeChecked, | ||
| 1117 | + app, | ||
| 1118 | + } = props; | ||
| 1119 | + | ||
| 1120 | + const noticeMsgData = noticeData?.msgData ; | ||
| 1121 | + | ||
| 1122 | + | ||
| 1123 | + const noticeFileData = noticeData?.fileData || [] ; | ||
| 1124 | + | ||
| 1125 | + | ||
| 1126 | + const NoticeTitle = noticeMsgData?.sTitle; | ||
| 1127 | + const NoticeContent = noticeMsgData?.sAbstractfullmemo || '暂无内容'; | ||
| 1128 | + | ||
| 1129 | + const Confirm = commonUtils.isNotEmptyObject(commonFunc.showMessage(app.commonConst, 'Confirm')) | ||
| 1130 | + ? commonFunc.showMessage(app.commonConst, 'Confirm') | ||
| 1131 | + : '确定'; | ||
| 1132 | + const Cancel = commonUtils.isNotEmptyObject(commonFunc.showMessage(app.commonConst, 'Cancel')) | ||
| 1133 | + ? commonFunc.showMessage(app.commonConst, 'Cancel') | ||
| 1134 | + : '取消'; | ||
| 1135 | + const NoticeAgree = commonUtils.isNotEmptyObject(commonFunc.showMessage(app.commonConst, 'NoticeAgree')) | ||
| 1136 | + ? commonFunc.showMessage(app.commonConst, 'NoticeAgree') | ||
| 1137 | + : '我已阅读并同意'; | ||
| 1138 | + | ||
| 1139 | + return ( | ||
| 1140 | + <div> | ||
| 1141 | + { | ||
| 1142 | + noticeVisible ? | ||
| 1143 | + <AntdDraggableModal | ||
| 1144 | + title={NoticeTitle} | ||
| 1145 | + width={1200} | ||
| 1146 | + visible={noticeVisible} | ||
| 1147 | + onCancel={handleNoticeCancel} | ||
| 1148 | + closable={false} | ||
| 1149 | + footer={[ | ||
| 1150 | + // 外层容器控制整体边距和左对齐 | ||
| 1151 | + <div | ||
| 1152 | + style={{ | ||
| 1153 | + display: 'flex', | ||
| 1154 | + alignItems: 'center', | ||
| 1155 | + width: '100%', | ||
| 1156 | + justifyContent: 'flex-start', | ||
| 1157 | + padding: '0px 24px 16px 16px', | ||
| 1158 | + margin: '0', // 清除外部默认边距 | ||
| 1159 | + }} | ||
| 1160 | + > | ||
| 1161 | + <Checkbox | ||
| 1162 | + key="agree" | ||
| 1163 | + checked={noticeChecked} | ||
| 1164 | + onChange={handleNoticeCheckChange} | ||
| 1165 | + style={{ | ||
| 1166 | + marginRight: 16, | ||
| 1167 | + fontSize: 14, | ||
| 1168 | + color: '#333', | ||
| 1169 | + // 确保复选框与按钮垂直居中对齐 | ||
| 1170 | + marginTop: -2 | ||
| 1171 | + }} | ||
| 1172 | + > | ||
| 1173 | + {NoticeAgree} | ||
| 1174 | + </Checkbox> | ||
| 1175 | + <Button | ||
| 1176 | + key="confirm" | ||
| 1177 | + type="primary" | ||
| 1178 | + onClick={handleNoticeConfirm} | ||
| 1179 | + disabled={!noticeChecked} | ||
| 1180 | + style={{ | ||
| 1181 | + padding: '0px 20px', // 增加按钮内边距让文字居中更明显 | ||
| 1182 | + fontSize: 14, | ||
| 1183 | + borderRadius: 4, | ||
| 1184 | + transition: 'all 0.2s', | ||
| 1185 | + opacity: noticeChecked ? 1 : 0.6, | ||
| 1186 | + cursor: noticeChecked ? 'pointer' : 'not-allowed', | ||
| 1187 | + // 确保按钮文字垂直居中 | ||
| 1188 | + height: 30, | ||
| 1189 | + lineHeight: '30px' | ||
| 1190 | + }} | ||
| 1191 | + > | ||
| 1192 | + {Confirm} | ||
| 1193 | + </Button> | ||
| 1194 | + </div> | ||
| 1195 | + ]} | ||
| 1196 | + > | ||
| 1197 | + <div style={{ | ||
| 1198 | + maxHeight: '800px', | ||
| 1199 | + overflowY: 'auto', | ||
| 1200 | + padding: '20px', | ||
| 1201 | + border: '1px solid #f0f0f0', | ||
| 1202 | + borderRadius: '8px', | ||
| 1203 | + backgroundColor: '#f5f7fa', | ||
| 1204 | + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.05)' | ||
| 1205 | + }}> | ||
| 1206 | + {/* 富文本内容区域 */} | ||
| 1207 | + {NoticeContent ? ( | ||
| 1208 | + <div style={{ | ||
| 1209 | + backgroundColor: '#fff', | ||
| 1210 | + borderRadius: '8px', | ||
| 1211 | + padding: '20px', | ||
| 1212 | + minHeight: '200px', | ||
| 1213 | + border: '1px solid #e8e8e8', | ||
| 1214 | + transition: 'border-color 0.3s' | ||
| 1215 | + }}> | ||
| 1216 | + <BraftEditor | ||
| 1217 | + value={BraftEditor.createEditorState(noticeMsgData?.sAbstractfullmemo)} | ||
| 1218 | + readOnly | ||
| 1219 | + controls={[]} | ||
| 1220 | + contentStyle={{ | ||
| 1221 | + height: 'auto', | ||
| 1222 | + minHeight: '400px', | ||
| 1223 | + maxHeight: '600px', | ||
| 1224 | + lineHeight: '1.8', | ||
| 1225 | + fontSize: '15px' | ||
| 1226 | + }} | ||
| 1227 | + style={{ | ||
| 1228 | + border: 'none', | ||
| 1229 | + borderRadius: '4px' | ||
| 1230 | + }} | ||
| 1231 | + /> | ||
| 1232 | + </div> | ||
| 1233 | + ) : ( | ||
| 1234 | + <div style={{ | ||
| 1235 | + lineHeight: '1.8', | ||
| 1236 | + fontSize: '15px', | ||
| 1237 | + whiteSpace: 'pre-wrap', | ||
| 1238 | + wordBreak: 'break-word', | ||
| 1239 | + padding: '20px', | ||
| 1240 | + backgroundColor: '#fff', | ||
| 1241 | + border: '1px solid #e8e8e8', | ||
| 1242 | + borderRadius: '8px', | ||
| 1243 | + minHeight: '200px', | ||
| 1244 | + color: '#666', | ||
| 1245 | + textAlign: 'center' | ||
| 1246 | + }}> | ||
| 1247 | + 暂无公告内容 | ||
| 1248 | + </div> | ||
| 1249 | + )} | ||
| 1250 | + | ||
| 1251 | + {/* 附件区域 - 与富文本区域明显分隔 */} | ||
| 1252 | + {commonUtils.isNotEmptyArr(noticeFileData) && ( | ||
| 1253 | + <div style={{ | ||
| 1254 | + marginTop: '20px', | ||
| 1255 | + padding: '15px 20px', | ||
| 1256 | + backgroundColor: '#fff', | ||
| 1257 | + borderRadius: '8px', | ||
| 1258 | + border: '1px solid #e8e8e8', | ||
| 1259 | + }}> | ||
| 1260 | + <div style={{ | ||
| 1261 | + display: 'flex', | ||
| 1262 | + alignItems: 'center', | ||
| 1263 | + flexWrap: 'wrap' | ||
| 1264 | + }}> | ||
| 1265 | + {/* 左侧"附件"文字 */} | ||
| 1266 | + <div style={{ | ||
| 1267 | + fontSize: '14px', | ||
| 1268 | + color: '#333', | ||
| 1269 | + fontWeight: 500, | ||
| 1270 | + marginRight: '15px', | ||
| 1271 | + padding: '3px 0', | ||
| 1272 | + minWidth: '50px' | ||
| 1273 | + }}> | ||
| 1274 | + 附件: | ||
| 1275 | + </div> | ||
| 1276 | + | ||
| 1277 | + {/* 右侧附件链接区域 */} | ||
| 1278 | + <div style={{ | ||
| 1279 | + display: 'flex', | ||
| 1280 | + gap: '16px', | ||
| 1281 | + flexWrap: 'wrap', | ||
| 1282 | + flexGrow: 1 | ||
| 1283 | + }}> | ||
| 1284 | + {noticeFileData.map(item => { | ||
| 1285 | + const {sId: uid, sFileName: name, sPicturePath} = item; | ||
| 1286 | + const fileUrl = `${commonConfig.server_host}file/download?savePathStr=${sPicturePath}&sModelsId=100`; | ||
| 1287 | + | ||
| 1288 | + return ( | ||
| 1289 | + <a | ||
| 1290 | + key={uid} | ||
| 1291 | + href={fileUrl} | ||
| 1292 | + target="_blank" | ||
| 1293 | + rel="noopener noreferrer" | ||
| 1294 | + style={{ | ||
| 1295 | + display: 'inline-flex', | ||
| 1296 | + alignItems: 'center', | ||
| 1297 | + color: '#1890ff', | ||
| 1298 | + fontSize: '14px', | ||
| 1299 | + textDecoration: 'none', | ||
| 1300 | + padding: '3px 0', | ||
| 1301 | + transition: 'color 0.2s', | ||
| 1302 | + whiteSpace: 'nowrap' | ||
| 1303 | + }} | ||
| 1304 | + onMouseOver={(e) => e.currentTarget.style.textDecoration = 'underline'} | ||
| 1305 | + onMouseOut={(e) => e.currentTarget.style.textDecoration = 'none'} | ||
| 1306 | + > | ||
| 1307 | + <span style={{marginRight: '5px'}}> | ||
| 1308 | + <i className="fa fa-file-o" aria-hidden="true"></i> | ||
| 1309 | + </span> | ||
| 1310 | + {name} | ||
| 1311 | + </a> | ||
| 1312 | + ); | ||
| 1313 | + })} | ||
| 1314 | + </div> | ||
| 1315 | + </div> | ||
| 1316 | + </div> | ||
| 1317 | + )} | ||
| 1318 | + </div> | ||
| 1319 | + | ||
| 1320 | + </AntdDraggableModal> | ||
| 1321 | + : '' | ||
| 1322 | + } | ||
| 1323 | + </div> | ||
| 1324 | + ); | ||
| 1325 | +}); | ||
| 1326 | + | ||
| 978 | export default PersonCenter; | 1327 | export default PersonCenter; |