Merge pull request '新增通知系统' (#84) from tongChong/forgeplus-react:feature_notification into pre_develop_dev
This commit is contained in:
commit
8818bafad2
|
@ -3875,6 +3875,52 @@
|
|||
"warning": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"cross-env": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
|
||||
"requires": {
|
||||
"cross-spawn": "^7.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"requires": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"requires": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cross-fetch": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.nlark.com/cross-fetch/download/cross-fetch-3.1.4.tgz",
|
||||
|
@ -7240,8 +7286,7 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"optional": true
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -7658,8 +7703,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"optional": true
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -7715,7 +7759,6 @@
|
|||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -7759,14 +7802,12 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"optional": true
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"optional": true
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -16586,6 +16627,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"save-dev": {
|
||||
"version": "0.0.1-security",
|
||||
"resolved": "https://registry.npmjs.org/save-dev/-/save-dev-0.0.1-security.tgz",
|
||||
"integrity": "sha512-k6knZTDNK8PKKbIqnvxiOveJinuw2LcQjqDoaorZWP9M5AR2EPsnpDeSbeoZZ0pHr5ze1uoaKdK8NBGQrJ34Uw=="
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"code-prettify": "^0.1.0",
|
||||
"codemirror": "^5.53.0",
|
||||
"connected-react-router": "4.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^3.5.2",
|
||||
"dompurify": "^2.0.15",
|
||||
"dotenv": "4.0.0",
|
||||
|
@ -103,6 +104,7 @@
|
|||
"redux-thunk": "2.3.0",
|
||||
"rsuite": "^4.3.4",
|
||||
"sass-loader": "7.3.1",
|
||||
"save-dev": "0.0.1-security",
|
||||
"scroll-into-view": "^1.14.2",
|
||||
"showdown": "^1.9.1",
|
||||
"showdown-katex": "^0.8.0",
|
||||
|
@ -122,7 +124,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"start": "node --max_old_space_size=15360 scripts/start.js",
|
||||
"build": "NODE_ENV=production node --max_old_space_size=15360 scripts/build.js",
|
||||
"build": "cross-env NODE_ENV=production node --max_old_space_size=15360 scripts/build.js",
|
||||
"test-build": "NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js",
|
||||
"pre-build": "NODE_ENV=preBuild node --max_old_space_size=15360 scripts/build.js",
|
||||
"gen_stats": "NODE_ENV=production webpack --profile --config=./config/webpack.config.prod.js --json > stats.json",
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/* eslint-disable react/jsx-no-duplicate-props */
|
||||
import React, { useState } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { Modal, Button } from 'antd';
|
||||
import './index.scss';
|
||||
|
||||
// 函数式调用删除、通知等模态框
|
||||
|
||||
InitModal.defaultProps = {
|
||||
okText: '确认', //确定按钮的文字
|
||||
cancelText: '取消', //取消按钮的文字
|
||||
className: '', //传入的模态框类名
|
||||
inputId: 'copyText', //要复制的文本的ID
|
||||
onCancel:()=>{}, //取消的回调
|
||||
onOk:()=>{}, //确认的回调
|
||||
title:'提示', //模态框名字
|
||||
contentTitle:'', //内容标题
|
||||
content:'', //详细内容
|
||||
afterClose:()=>{}, //关闭模态框以后的回调
|
||||
};
|
||||
|
||||
// 使用函数调用删除组件
|
||||
export default function DelModal(props) {
|
||||
renderModal({ ...props, type: 'delete' })
|
||||
}
|
||||
|
||||
// 使用函数调用选择模态框组件
|
||||
export function Confirm(props) {
|
||||
renderModal({ ...props, type: 'confirm' })
|
||||
}
|
||||
|
||||
function renderModal(props) {
|
||||
const { type, afterClose } = props;
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
|
||||
function destroy() {
|
||||
afterClose && afterClose();
|
||||
const unmountResult = ReactDOM.unmountComponentAtNode(div);
|
||||
if (unmountResult && div.parentNode) {
|
||||
div.parentNode.removeChild(div);
|
||||
}
|
||||
}
|
||||
|
||||
function modalType(type) {
|
||||
if (type === 'delete') {
|
||||
return <InitModal
|
||||
title="删除"
|
||||
contentTitle="确定要删除吗?"
|
||||
okText="确认删除"
|
||||
{...props}
|
||||
|
||||
afterClose={destroy}
|
||||
contentTitle={<React.Fragment>
|
||||
<i className="red-circle iconfont icon-shanchu_tc_icon mr3"></i>
|
||||
{props.contentTitle}
|
||||
</React.Fragment>}
|
||||
/>
|
||||
} else if (type === 'confirm') {
|
||||
return <InitModal title="选择" afterClose={destroy} {...props} />
|
||||
} else {
|
||||
return <InitModal title="选择" afterClose={destroy} {...props} />
|
||||
}
|
||||
}
|
||||
|
||||
function render() {
|
||||
setTimeout(() => {
|
||||
ReactDOM.render(
|
||||
modalType(type),
|
||||
div,
|
||||
);
|
||||
});
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
// 选择模态框组件
|
||||
function InitModal({
|
||||
onCancel,
|
||||
onOk,
|
||||
title,
|
||||
contentTitle,
|
||||
content,
|
||||
okText,
|
||||
cancelText,
|
||||
afterClose,
|
||||
className,
|
||||
}) {
|
||||
|
||||
const [visible, setVisible] = useState(true);
|
||||
|
||||
function onCancelModal() {
|
||||
setVisible(false);
|
||||
onCancel && onCancel()
|
||||
}
|
||||
|
||||
function onSuccess() {
|
||||
setVisible(false);
|
||||
onOk && onOk();
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
onCancel={onCancelModal}
|
||||
afterClose={afterClose}
|
||||
title={title}
|
||||
className={`myself-modal ${className}`}
|
||||
centered
|
||||
footer={[
|
||||
<Button type="default" key="back" onClick={onCancelModal}>
|
||||
{cancelText}
|
||||
</Button>,
|
||||
<Button className="foot-submit" key="submit" onClick={onSuccess}>
|
||||
{okText}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<div>
|
||||
{contentTitle && <p className="content-title">{contentTitle}</p>}
|
||||
<p className="content-descibe">{content}</p>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
.myself-modal {
|
||||
.ant-modal-header {
|
||||
padding: 9px 24px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.ant-modal-title {
|
||||
text-align: left;
|
||||
}
|
||||
.ant-modal-close {
|
||||
top: 0px !important;
|
||||
}
|
||||
.ant-modal-close-x {
|
||||
font-size: 24px;
|
||||
}
|
||||
.ant-modal-body {
|
||||
text-align: center;
|
||||
}
|
||||
.content-title {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 2rem 0 1rem !important;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
letter-spacing: 0;
|
||||
line-height: 29px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.red-circle {
|
||||
align-self: flex-start;
|
||||
color: #ca0002;
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
.content-descibe {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 33px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.ant-modal-footer {
|
||||
padding: 2rem 0;
|
||||
text-align: center;
|
||||
border: 0;
|
||||
.ant-btn {
|
||||
width: 6rem;
|
||||
}
|
||||
}
|
||||
.foot-submit {
|
||||
margin-left: 3rem;
|
||||
color: #df0002;
|
||||
&:hover {
|
||||
border-color: #df0002;
|
||||
}
|
||||
}
|
||||
.ant-btn-default:hover,
|
||||
.ant-btn-default:active,
|
||||
.ant-btn-default:focus {
|
||||
background: #f3f4f6;
|
||||
color: #333;
|
||||
border-color: #d0d0d0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Icon } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import Nodata from '../Nodata';
|
||||
|
||||
|
||||
class PullRefresh extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
}
|
||||
this.pullRef = {};
|
||||
// 节流
|
||||
this.onScrollList = _.throttle(this.handleScroll, 200, {
|
||||
leading: false,
|
||||
trailing: true,
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let dom = document.querySelector('.pull-refresh-wrap');
|
||||
dom && dom.addEventListener('scroll', this.onScrollList);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
let dom = document.querySelector('.pull-refresh-wrap');
|
||||
dom && dom.removeEventListener('scroll', this.onScrollList)
|
||||
}
|
||||
|
||||
|
||||
handleScroll = () => {
|
||||
if (this.props.count < this.props.pageSize) return;
|
||||
if (this.props.type === 1 || this.props.type === 2) return;
|
||||
const wrap = this.pullRef;
|
||||
const currentScroll = wrap.scrollTop + wrap.clientHeight
|
||||
|
||||
// 触底
|
||||
if (currentScroll >= (wrap.scrollHeight - 200)) {
|
||||
this.loadData()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handleLoadClick = () => {
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
loadData = () => {
|
||||
this.props.onPullRefresh()
|
||||
}
|
||||
|
||||
|
||||
renderLoading() {
|
||||
switch (this.props.type) {
|
||||
case 0: // 加载更多
|
||||
return <div className='text-center' onClick={this.handleLoadClick}>显示更多</div>
|
||||
case 1: // 加载中
|
||||
return (
|
||||
<div className='text-center'>
|
||||
<Icon type="loading" />
|
||||
<span className='text-center'>加载中...</span>
|
||||
</div>
|
||||
)
|
||||
case 2: // 无样式
|
||||
return <div className='text-center'>没有更多了</div>
|
||||
default:
|
||||
return <div className='text-center'>没有更多了</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { className, count, children } = this.props;
|
||||
return (
|
||||
<div
|
||||
className={`pull-refresh-wrap ${className}`}
|
||||
ref={dom => { this.pullRef = dom }}
|
||||
>
|
||||
|
||||
{children}
|
||||
|
||||
{
|
||||
count < 1 && <Nodata _html="暂无未读消息"/>
|
||||
}
|
||||
|
||||
{/* 大于分页数据才显示loading */}
|
||||
{/* {this.props.count >= this.props.pageSize ? this.renderLoading() : null} */}
|
||||
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
PullRefresh.propTypes = {
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.any,
|
||||
onPullRefresh: PropTypes.func.isRequired,
|
||||
type: PropTypes.oneOf([0, 1, 2]),
|
||||
count: PropTypes.number.isRequired,
|
||||
pageSize: PropTypes.number.isRequired,
|
||||
}
|
||||
|
||||
|
||||
export default PullRefresh
|
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
|||
import AccountProfile from "../../modules/user/AccountProfile";
|
||||
import { getImageUrl } from 'educoder'
|
||||
import axios from 'axios';
|
||||
import { Input , notification , Dropdown , Menu } from 'antd';
|
||||
import { Input , notification , Dropdown ,Popover, Menu,Badge, Button } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import LoginDialog from '../../modules/login/LoginDialog';
|
||||
|
@ -13,6 +13,7 @@ import '../../modules/tpm/TPMIndex.css';
|
|||
import CheckProfile from '../Component/ProfileModal/Profile';
|
||||
|
||||
import './header.scss';
|
||||
import NoticeContent from './NoticeContent';
|
||||
const $ = window.$
|
||||
// TODO 这部分脚本从公共脚本中直接调用
|
||||
const { Search } = Input;
|
||||
|
@ -47,6 +48,7 @@ class NewHeader extends Component {
|
|||
settings: null,
|
||||
visiblemyss: false,
|
||||
openSearch:false,
|
||||
visible:false, //浮动消息框展示控制
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
|
@ -92,9 +94,6 @@ class NewHeader extends Component {
|
|||
this.setState({
|
||||
user: newProps.user
|
||||
})
|
||||
if (newProps.Headertop !== undefined) {
|
||||
old_url = newProps.Headertop.old_url
|
||||
}
|
||||
}
|
||||
|
||||
educoderlogin = () => {
|
||||
|
@ -255,6 +254,7 @@ class NewHeader extends Component {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderMenu=(personal)=>{
|
||||
const { current_user } = this.props;
|
||||
return(
|
||||
|
@ -275,8 +275,12 @@ class NewHeader extends Component {
|
|||
)
|
||||
}
|
||||
|
||||
handleVisibleChange = visible => {
|
||||
this.setState({ visible });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { match} = this.props;
|
||||
const { match ,resetUserInfo ,showNotification} = this.props;
|
||||
let current_user = this.props.user;
|
||||
let {
|
||||
AccountProfiletype,
|
||||
|
@ -285,6 +289,7 @@ class NewHeader extends Component {
|
|||
headtypesonClickbool,
|
||||
headtypess,
|
||||
settings,
|
||||
visible,
|
||||
} = this.state;
|
||||
/*用户名称 用户头像url*/
|
||||
let activeIndex = false;
|
||||
|
@ -395,7 +400,7 @@ class NewHeader extends Component {
|
|||
{
|
||||
settings.navbar && settings.navbar.map((item, key) => {
|
||||
var new_link = item.link;
|
||||
var user_login = this.props.user && this.props.user.login;
|
||||
var user_login = current_user && current_user.login;
|
||||
var is_hidden = item.hidden
|
||||
if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) {
|
||||
if (user_login) {
|
||||
|
@ -426,25 +431,30 @@ class NewHeader extends Component {
|
|||
}
|
||||
</div>
|
||||
<div className="head-right">
|
||||
{/* {search_url ? this.SearchInput(openSearch,search_url):""} */}
|
||||
{ search_url && <HeadSearch {...this.props}/>}
|
||||
{
|
||||
current_user && (current_user.main_site || current_user.login) && (settings && settings.add && settings.add.length>0)?
|
||||
<Dropdown overlay={this.addMenu(settings && settings.add)} placement="bottomRight">
|
||||
<i className="iconfont icon-tianjiafangda color-grey-6 ml30"></i>
|
||||
<i className="iconfont icon-tianjiafangda color-grey-6 ml30 mr15"></i>
|
||||
</Dropdown>:""
|
||||
}
|
||||
|
||||
{this.props.user && this.props.user.login && notice_url ?
|
||||
<div className="ml30 edu-menu-panel">
|
||||
{user && user.login &&
|
||||
<a href={`${notice_url}`} style={{ position: 'relative' }}>
|
||||
<i className="iconfont icon-xiaoxilingdang color-grey-6"></i>
|
||||
<span className="newslight" style={{ display: this.props.Headertop === undefined ? "none" : this.props.Headertop.new_message === true ? "block" : "none" }}>
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
</div>:""
|
||||
{current_user && current_user.login ?
|
||||
<Popover
|
||||
overlayClassName="notice-popover"
|
||||
placement={`bottomRight`}
|
||||
content={<NoticeContent visible={visible} current_user={current_user} showNotification={showNotification} resetUserInfo={resetUserInfo}/>}
|
||||
visible={visible}
|
||||
onVisibleChange={this.handleVisibleChange}
|
||||
destroyTooltipOnHide
|
||||
>
|
||||
<Link to={"/settings/notice"} className="message-icon">
|
||||
<Badge count={current_user.message_unread_total}>
|
||||
<i className="iconfont icon-xiaoxilingdang color-grey-6 ml15 mr15"></i>
|
||||
</Badge>
|
||||
</Link>
|
||||
</Popover>
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
{!user || (user && !user.login) ?
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Badge, Menu } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import AppPullRefresh from './AppPullRefresh';
|
||||
import { noticeSourceType } from '../common/static';
|
||||
import './header.scss';
|
||||
import '../SecuritySetting/notice/manager/Index.scss';
|
||||
import '../SecuritySetting/Index.scss';
|
||||
import '../SecuritySetting/notice/myNotice/Index.scss';
|
||||
|
||||
|
||||
function NoticeContent({ visible, showNotification, resetUserInfo, current_user: { login } }) {
|
||||
const [initialize, setInitialize] = useState(true);
|
||||
const [noticeType, setNoticeType] = useState("notification");
|
||||
const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量
|
||||
|
||||
const [noticeUnreadCount, setNoticeUnreadCount] = useState(0);//未读系统通知数量
|
||||
const [noticePage, setNoticePage] = useState(0);
|
||||
const [noticeUnreadList, setNoticeUnreadList] = useState([]);//未读系统通知列表
|
||||
|
||||
const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量
|
||||
const [atPage, setAtPage] = useState(0);
|
||||
const [atUnreadList, setAtUnreadList] = useState([]);//未读@我列表
|
||||
|
||||
useEffect(() => {
|
||||
resetUserInfo();
|
||||
}, [noticeUnreadCount,atUnreadCount]);
|
||||
|
||||
useEffect(()=>{
|
||||
setNoticePage(0);
|
||||
setAtPage(0);
|
||||
},[visible])
|
||||
|
||||
useEffect(() => {
|
||||
const params = {
|
||||
type: noticeType,
|
||||
limit: 10,
|
||||
page: noticeType === "notification" ? noticePage : noticeType === "atme" ? atPage : "",
|
||||
status: 1,
|
||||
}
|
||||
getMessageList(params);
|
||||
}, [noticePage, atPage]);
|
||||
|
||||
useEffect(() => {
|
||||
const params = {
|
||||
type: noticeType,
|
||||
limit: 10,
|
||||
page: 0,
|
||||
status: 1,
|
||||
};
|
||||
if (initialize) {
|
||||
params.type = "atme"
|
||||
}
|
||||
visible && getMessageList(params);
|
||||
}, [visible]);
|
||||
|
||||
|
||||
function getMessageList(params) {
|
||||
axios.get(`/users/${login}/messages.json`, {
|
||||
params: params,
|
||||
}).then((response) => {
|
||||
if (response && response.data) {
|
||||
setNoticeUnreadCount(response.data.unread_notification);
|
||||
setAtUnreadCount(response.data.unread_atme);
|
||||
if (params.type === "notification") {
|
||||
let list = response.data.messages;
|
||||
if (params.page !== 0) {
|
||||
list = [...noticeUnreadList, ...list];
|
||||
}
|
||||
setNoticeUnreadList(list);
|
||||
if (initialize) {
|
||||
// 如果是第一次加载,根据数据量判断是否切换tab栏
|
||||
setInitialize(false);
|
||||
if (response.data.unread_notification === 0 && response.data.unread_atme !== 0) {
|
||||
setNoticeType("atme");
|
||||
}
|
||||
}
|
||||
} else if (params.type === "atme") {
|
||||
let list = response.data.messages;
|
||||
if (params.page !== 0) {
|
||||
list = [...atUnreadList, ...list];
|
||||
}
|
||||
setAtUnreadList(list);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function readAll() {
|
||||
axios.post(`/users/${login}/messages/read.json`, {
|
||||
type: noticeType,
|
||||
ids: [-1]
|
||||
}).then((response) => {
|
||||
let data = response.data;
|
||||
if (!data) return;
|
||||
if (data.status === 0) {
|
||||
changeReadMarkAll(noticeType);
|
||||
} else {
|
||||
showNotification(data.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function changeReadMarkAll(noticeType) {
|
||||
if (noticeType === "notification") {
|
||||
let list = noticeUnreadList.slice();
|
||||
list.forEach(item => {
|
||||
item.status = 2;
|
||||
})
|
||||
setNoticeUnreadList(list);
|
||||
setNoticeUnreadCount(0);
|
||||
} else if (noticeType === "atme") {
|
||||
let list = atUnreadList.slice();
|
||||
list.forEach(item => {
|
||||
item.status = 2;
|
||||
})
|
||||
setAtUnreadList(list);
|
||||
setAtUnreadCount(0);
|
||||
}
|
||||
}
|
||||
|
||||
// const [letter_unread_list, setLetter_unread_list] = useState([
|
||||
// {
|
||||
// id: 122,
|
||||
// read: 0, //是否已读,0未读,1已读
|
||||
// send_name: "蒋宇航", //消息发送人
|
||||
// send_login: "jiangYuHang", //消息发送人的login,前端根据这个跳转到消息内页
|
||||
// content: "私信内容", //最近一条未读消息的内容
|
||||
// create_time: "2019-03-04 18:08", //发送时间
|
||||
// },
|
||||
// ]);
|
||||
|
||||
function readItem(item) {
|
||||
axios.post(`/users/${login}/messages/read.json`, {
|
||||
type: noticeType,
|
||||
ids: [item.id]
|
||||
}).then((response) => {
|
||||
let data = response.data;
|
||||
if (!data) return;
|
||||
if (data.status === 0) {
|
||||
changeReadMark(item);
|
||||
item.notification_url && window.open(item.notification_url);
|
||||
} else {
|
||||
showNotification(data.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function changeReadMark(item) {
|
||||
if (item.type === "notification") {
|
||||
let list = noticeUnreadList.slice();
|
||||
let index = noticeUnreadList.indexOf(item);
|
||||
list[index].status = 2;
|
||||
setNoticeUnreadList(list);
|
||||
if (noticeUnreadCount > 0) {
|
||||
setNoticeUnreadCount(noticeUnreadCount - 1);
|
||||
}
|
||||
} else if (item.type === "atme") {
|
||||
let list = atUnreadList.slice();
|
||||
let index = atUnreadList.indexOf(item);
|
||||
list[index].status = 2;
|
||||
setAtUnreadList(list);
|
||||
if (atUnreadCount > 0) {
|
||||
setAtUnreadCount(atUnreadCount - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="messageHoverDiv notice01">
|
||||
<div className="sshHead hoverNotice-head">
|
||||
<Menu mode="horizontal" selectedKeys={noticeType} onClick={(e) => setNoticeType(e.key)}>
|
||||
<Menu.Item key="notification"><Badge count={noticeUnreadCount}>系统通知</Badge></Menu.Item>
|
||||
{/* <Menu.Item key="1" id="item-private"><Badge count={letterUnreadCount}>私信</Badge></Menu.Item> */}
|
||||
<Menu.Item key="atme"><Badge count={atUnreadCount}>@我</Badge></Menu.Item>
|
||||
</Menu>
|
||||
</div>
|
||||
|
||||
{/* 系统通知 */}
|
||||
{noticeType === "notification" && <AppPullRefresh
|
||||
className='hoverNotice-body' // 外部添加className加以区分
|
||||
onPullRefresh={() => { setNoticePage(noticePage + 1); }} //触发加载ajax的function
|
||||
// type={2} // 传送加载组件的状态
|
||||
count={noticeUnreadList.length} // 数据当前的总数量
|
||||
pageSize={10} //
|
||||
>
|
||||
{
|
||||
noticeUnreadList.map(item => {
|
||||
return (
|
||||
<div key={item.id + Math.random()} className="noticeCont-back" onClick={() => { readItem(item) }}>
|
||||
<div className={`noticeCont ${item.notification_url?'pointer':''}`}>
|
||||
<span style={{ visibility: item.status === 1 ? 'visible' : 'hidden' }}>
|
||||
<Badge color="#FA2020" />
|
||||
</span>
|
||||
<i className={"iconfont " + noticeSourceType[item.source]}></i>
|
||||
<div className="noticeCont-text">
|
||||
<span className="content-span notice-cont-span" dangerouslySetInnerHTML={{ __html: item.content }}></span>
|
||||
<span className="timeSpan">{item.time_ago}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</AppPullRefresh>
|
||||
}
|
||||
|
||||
{/* @我 */}
|
||||
{noticeType === "atme" && <AppPullRefresh
|
||||
className='hoverNotice-body' // 外部添加className加以区分
|
||||
onPullRefresh={() => { setAtPage(atPage + 1); }} //触发加载ajax的function
|
||||
// type={1} // 传送加载组件的状态
|
||||
count={atUnreadList.length} // 数据当前的总数量
|
||||
pageSize={10} //
|
||||
>
|
||||
{atUnreadList.map(item => {
|
||||
return (
|
||||
<div key={item.id + Math.random()} className="noticeCont-back" onClick={() => { readItem(item) }}>
|
||||
<div className="noticeCont">
|
||||
<span style={{ visibility: item.status === 1 ? 'visible' : 'hidden' }}>
|
||||
<Badge color="#FA2020" />
|
||||
</span>
|
||||
<div className="noticeCont-text">
|
||||
<span className="content-span atme-cont-span" dangerouslySetInnerHTML={{ __html: "<b>" + (item.sender ? item.sender.name : '') + "</b> " + item.content + " 中@我" }}></span>
|
||||
<span className="timeSpan">{item.time_ago}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</AppPullRefresh>
|
||||
}
|
||||
|
||||
{/* 私信 */}
|
||||
{/* {noticeType === "1" ? letter_unread_list.length > 0 ? letter_unread_list.map(item => {
|
||||
return (
|
||||
<div className="noticeCont-back">
|
||||
<div className="noticeCont" style={{ height: item.content.length >= 30 && item.content.length <= 34 ? '65px' : "" }}>
|
||||
<Badge color="#FA2020" />
|
||||
<div className="noticeCont-text">
|
||||
<span>{item.send_name}:</span>
|
||||
<span className="boldSpan" dangerouslySetInnerHTML={{ __html: item.content.length >= 50 ? item.content.substr(0, 50) + "..." : item.content }}></span>
|
||||
<span className="timeSpan">{item.create_time ? timeAgo(item.create_time) : "刚刚"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}) : "暂无数据" : ""} */}
|
||||
<div className="hoverNotice-buttom">
|
||||
<Link to={{pathname:"/settings/notice",query:{noticeType:noticeType}}}>全部消息</Link>
|
||||
{noticeUnreadCount > 0 && noticeType === "notification" && <a onClick={readAll}>所有系统消息一键已读</a>}
|
||||
{atUnreadCount > 0 && noticeType === "atme" && <a onClick={readAll}>所有@我一键已读</a>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
export default NoticeContent;
|
|
@ -24,8 +24,9 @@
|
|||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 50%;
|
||||
margin-left: 30px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.currentMenu{
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
|
@ -126,4 +127,139 @@
|
|||
width: 110px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
// 右上角小铃铛单独样式
|
||||
.notice-popover{
|
||||
//popover小尖尖
|
||||
.ant-popover-arrow{
|
||||
display: none;
|
||||
}
|
||||
|
||||
//popover框
|
||||
.ant-popover-inner-content {
|
||||
width: 386px;
|
||||
height: 446px;
|
||||
box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5);
|
||||
border-radius: 4px;
|
||||
margin-top: -10px;
|
||||
padding: 12px 1px 12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.messageHoverDiv .ant-menu-item{
|
||||
margin-right: 24px !important;
|
||||
}
|
||||
|
||||
.hoverNotice-head{
|
||||
margin-left: 18px;
|
||||
|
||||
& .ant-badge{
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
&>.ant-menu-horizontal {
|
||||
border-bottom: 1px solid #e8e8e8 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.hoverNotice-body{
|
||||
height: 342px;
|
||||
overflow-y: scroll;
|
||||
|
||||
& b{
|
||||
font-weight: 400;
|
||||
text-shadow: 0.5px 0 0 #333;
|
||||
}
|
||||
|
||||
.none_panels{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.message-icon{
|
||||
position: relative;
|
||||
.ant-scroll-number{
|
||||
right:12px;
|
||||
padding: 0 0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.hoverNotice-buttom{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 12px 18px;
|
||||
a{
|
||||
color: #466AFF;
|
||||
&:hover{
|
||||
opacity:0.85;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.noticeCont-back{
|
||||
.pointer{
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover{
|
||||
background: #F3F4F6;
|
||||
}
|
||||
}
|
||||
|
||||
.noticeCont{
|
||||
display: flex;
|
||||
margin: 0 16px 0 18px;
|
||||
padding: 12px 0 10px 0;
|
||||
line-height: 24px;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
cursor: default;
|
||||
i{
|
||||
font-size: 14px !important;
|
||||
margin-right: 6px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.boldSpan{
|
||||
font-weight: 400;
|
||||
text-shadow: 0.5px 0 0 #333;
|
||||
}
|
||||
|
||||
.noticeCont-text{
|
||||
display: flex;
|
||||
color:#333333;
|
||||
flex:auto;
|
||||
justify-content: space-between;
|
||||
|
||||
& .content-span{
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
& .atme-cont-span{
|
||||
width: 272px;
|
||||
}
|
||||
|
||||
& .notice-cont-span{
|
||||
width: 255px;
|
||||
}
|
||||
|
||||
.timeSpan{
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.at-name{
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-center{
|
||||
text-align: center;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import React , { Component } from 'react';
|
||||
|
||||
import nodata from './Images/nodata.png';
|
||||
|
||||
import './css/index.scss';
|
||||
class Nodata extends Component{
|
||||
render(){
|
||||
const { _html , small } = this.props;
|
||||
|
|
|
@ -10,10 +10,6 @@ const Apply = Loadable({
|
|||
loader: () => import("./Apply"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Notify = Loadable({
|
||||
loader: () => import("./Notify"),
|
||||
loading: Loading,
|
||||
});
|
||||
const UndoEvent = Loadable({
|
||||
loader: () => import("./UndoEvent"),
|
||||
loading: Loading,
|
||||
|
@ -23,7 +19,7 @@ function Index(props){
|
|||
const pathname = props.history.location.pathname;
|
||||
const user = props.user;
|
||||
|
||||
const [ menu , setMenu ] = useState("notify");
|
||||
const [ menu , setMenu ] = useState("undo");
|
||||
const [ messagesCount , setMessagesCount ] = useState(0);
|
||||
const [ transferCount , setTransferCount ] = useState(0);
|
||||
const [ applyCount , setApplyCount ] = useState(0);
|
||||
|
@ -48,10 +44,6 @@ function Index(props){
|
|||
useEffect(()=>{
|
||||
if(pathname && username){
|
||||
if(pathname === `/${username}/notice`){
|
||||
setMenu("notify");
|
||||
changeNum(user.undo_messages);
|
||||
}
|
||||
if(pathname === `/${username}/notice/undo`){
|
||||
setMenu("undo");
|
||||
}
|
||||
if(pathname === `/${username}/notice/apply`){
|
||||
|
@ -83,14 +75,8 @@ function Index(props){
|
|||
return (
|
||||
<div>
|
||||
<ul className="noticeMenu">
|
||||
<li className={menu === "notify" ? "active":""}>
|
||||
<Link to={`/${username}/notice`} onClick={changeNum}>
|
||||
<span>通知</span>
|
||||
{messagesCount ? <span className="unNum">{messagesCount}</span>:""}
|
||||
</Link>
|
||||
</li>
|
||||
<li className={menu === "undo" ? "active":""}>
|
||||
<Link to={`/${username}/notice/undo`}>
|
||||
<Link to={`/${username}/notice`}>
|
||||
<span>接收仓库</span>
|
||||
{transferCount ? <span className="unNum">{transferCount}</span>:""}
|
||||
</Link>
|
||||
|
@ -109,16 +95,10 @@ function Index(props){
|
|||
return <Apply {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:username/notice/undo"
|
||||
render={(p) => {
|
||||
return <UndoEvent {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:username/notice"
|
||||
render={(p) => {
|
||||
return <Notify {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
return <UndoEvent {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
</Switch>
|
||||
|
|
|
@ -14,6 +14,16 @@ import { Link } from 'react-router-dom';
|
|||
|
||||
import './Index.scss';
|
||||
|
||||
const MyNoticeIndex = Loadable({
|
||||
loader: () => import("./notice/myNotice/Index"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
const NoticeManager = Loadable({
|
||||
loader: () => import("./notice/manager/Index"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
const SSHNew = Loadable({
|
||||
loader: () => import("./sub/New"),
|
||||
loading: Loading,
|
||||
|
@ -26,6 +36,12 @@ const SSHIndex = Loadable({
|
|||
loader: () => import("./sub/SSH"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
const PrivateLetter = Loadable({
|
||||
loader: () => import("./notice/privateLetter/Index"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
function Index(props){
|
||||
const { current_user } = props;
|
||||
const { pathname } = props.location;
|
||||
|
@ -39,18 +55,35 @@ function Index(props){
|
|||
<img src={getImageUrl(`/${current_user && current_user.image_url}`)} alt=""/>
|
||||
<span>{current_user && current_user.username}</span>
|
||||
</div>
|
||||
<ul className="securityUl">
|
||||
<ul className="securityUl ul-border-buttom">
|
||||
<li>个人信息</li>
|
||||
<li className={pathname.indexOf("/settings/profile")>-1 ?"active":""}><Link to={`/settings/profile`}><i className="iconfont icon-gerenziliao mr5 font-14"></i>基本资料</Link></li>
|
||||
<li className={pathname.indexOf("/settings/profile")>-1 ?"active":""}><Link to={`/settings/profile`}><i className="iconfont icon-gerenziliao mr5 font-14"></i><span className="text-shodow-bold">基本资料</span></Link></li>
|
||||
</ul>
|
||||
<ul className="securityUl ul-border-buttom">
|
||||
<li>消息通知</li>
|
||||
<li className={(pathname.indexOf("/settings/notice")>-1 && pathname.indexOf("/settings/notice/config") == -1) || pathname.indexOf("/settings/notice/privateLetter")>-1 ?"active":""}><Link to={"/settings/notice"}><i className="iconfont icon-wodetongzhi"></i><span className="text-shodow-bold">我的通知</span></Link></li>
|
||||
{/* <li className={pathname.indexOf("/settings/notice/config")>-1 ?"active":""}><Link to={'/settings/notice/config'}><i className="iconfont icon-tongzhiguanli"></i><span className="text-shodow-bold">通知管理</span></Link></li> */}
|
||||
</ul>
|
||||
<ul className="securityUl">
|
||||
<li>安全设置</li>
|
||||
<li className={pathname.indexOf("/settings/SSH")>-1 ?"active":""}><Link to={`/settings/SSH`}><i className="iconfont icon-xuanzhongssh_icon mr5 font-14"></i>SSH密钥</Link></li>
|
||||
<li className={pathname.indexOf("/settings/SSH")>-1 ?"active":""}><Link to={`/settings/SSH`}><i className="iconfont icon-xuanzhongssh_icon mr5 font-14"></i><span className="text-shodow-bold">SSH密钥</span></Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<LongWidth>
|
||||
<Gap>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/settings/notice"
|
||||
render={(p) => (
|
||||
<MyNoticeIndex {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/notice/config"
|
||||
render={(p) => (
|
||||
<NoticeManager {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/SSH/new"
|
||||
render={(p) => (
|
||||
|
@ -69,12 +102,17 @@ function Index(props){
|
|||
<SSHIndex {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/notice/privateLetter"
|
||||
render={(p)=>(
|
||||
<PrivateLetter{...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
</Switch>
|
||||
</Gap>
|
||||
</LongWidth>
|
||||
</Box>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,42 +8,44 @@
|
|||
width: 198px;
|
||||
border: 1px solid rgba(153, 153, 153, 0.22);
|
||||
border-radius: 4px;
|
||||
min-height: 400px;
|
||||
// min-height: 400px;
|
||||
margin-bottom: 30px;
|
||||
.userDetail{
|
||||
background: rgba(153, 153, 153, 0.05);
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
padding:20px 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
text-align: center;
|
||||
height: 105px;
|
||||
img{
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
border-radius: 50%;
|
||||
margin-right: 12px;
|
||||
}
|
||||
span{
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
max-width: 90px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.securityUl{
|
||||
padding:20px 16px;
|
||||
padding-left: 17px;
|
||||
color: #333;
|
||||
margin-bottom: 0px;
|
||||
padding-bottom: 0px;
|
||||
padding-bottom: 12px;
|
||||
padding-top: 5px;
|
||||
li{
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
|
||||
a{
|
||||
color: #666;
|
||||
&:hover{
|
||||
|
@ -52,32 +54,57 @@
|
|||
}
|
||||
&.active a{
|
||||
color: #333;
|
||||
}
|
||||
&:first-child{
|
||||
font-size: 16px;
|
||||
.text-shodow-bold{
|
||||
font-weight: 400;
|
||||
text-shadow: 0.5px 0 #333;
|
||||
}
|
||||
}
|
||||
&.active::before{
|
||||
position: absolute;
|
||||
left: -16px;
|
||||
top:0px;
|
||||
height: 100%;
|
||||
width: 2px;
|
||||
left: -18px;
|
||||
top:6px;
|
||||
height: 15px;
|
||||
width: 3px;
|
||||
border-radius: 2px;
|
||||
content: "";
|
||||
background-color: #2A61FF;
|
||||
background-color: #466AFF;
|
||||
}
|
||||
|
||||
i{
|
||||
font-size: 14px !important;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ul-border-buttom{
|
||||
border-bottom: 1px solid rgba(153, 153, 153, 0.22);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
.sshHead{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding:15px 20px;
|
||||
padding:6px 0px;
|
||||
margin-bottom: 15px;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
&>span{
|
||||
&>.text-shadow07{
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
text-shadow: 0.5px 0 #333;
|
||||
}
|
||||
&>.add-SSH-title{
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
text-shadow: 0.5px 0 #333;
|
||||
}
|
||||
& .but25{
|
||||
padding:0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-list-item{
|
||||
padding:20px;
|
||||
border-bottom: 1px solid #eee!important;
|
||||
|
@ -109,7 +136,7 @@
|
|||
.questionLink{
|
||||
padding:15px 20px;
|
||||
a{
|
||||
color: #4B7AFF;
|
||||
color: #466AFF;
|
||||
&:hover{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
@ -147,4 +174,23 @@
|
|||
flex:1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.but25{
|
||||
margin-bottom: 5px;
|
||||
background-color: #466AFF;
|
||||
color: #fff;
|
||||
border-color: #466AFF;
|
||||
&:hover{
|
||||
opacity: 0.8;
|
||||
background-color: #466AFF;
|
||||
border-color: #466AFF;
|
||||
}
|
||||
&:active{
|
||||
opacity: 1;
|
||||
background-color: #466AFF;
|
||||
border-color: #466AFF;
|
||||
}
|
||||
}
|
||||
.blue-Purple{
|
||||
color: #466AFF !important;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 900 B After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1,46 @@
|
|||
import fetch from './fetch';
|
||||
|
||||
// 获取消息列表
|
||||
export function noticePages(params) {
|
||||
return fetch({
|
||||
url: '/api/notice/noticePages',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 获取单个notice
|
||||
export function getNotice(params) {
|
||||
return fetch({
|
||||
url: '/api/notice/getNotice',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
//新增notice
|
||||
export function addNotice(data) {
|
||||
return fetch({
|
||||
url: '/api/notice/createNotice',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
//更新notice
|
||||
export function updateNotice(data) {
|
||||
return fetch({
|
||||
url: '/api/notice/updateNotice',
|
||||
method: 'PUT',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
//删除notice
|
||||
export function deleteNotice(data) {
|
||||
return fetch({
|
||||
url: '/api/notice/deleteNotice',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
import javaFetch from '../../javaFetch';
|
||||
|
||||
const developUrl = "https://test-search.trustie.net";
|
||||
const testUrl = "https://test-search.trustie.net";
|
||||
const productUrl = "https://wiki-api.trustie.net";
|
||||
|
||||
const { service, actionUrl } = javaFetch(productUrl, testUrl, developUrl);
|
||||
export const httpUrl = actionUrl;
|
||||
export default service;
|
|
@ -0,0 +1,100 @@
|
|||
import { Button, Checkbox } from "antd";
|
||||
import React from "react";
|
||||
|
||||
import './Index.scss';
|
||||
|
||||
function NoticeManager(props){
|
||||
|
||||
return(
|
||||
<div className="notice01">
|
||||
<div className="sshHead">
|
||||
<span className="text-shadow07">通知管理</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="notice-manager-tip">您可以通过通知管理来选择接受通知的方式</span>
|
||||
<div className="manager-cont-top">
|
||||
我创建或负责的
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">易修状态变更</div>
|
||||
<Checkbox defaultChecked='true' disabled>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">易修截止日期到达最后一天</div>
|
||||
<Checkbox defaultChecked='true' disabled>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">合并请求状态变更</div>
|
||||
<Checkbox defaultChecked='true' disabled>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">易修有新的评论</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">合并请求有新的评论</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
|
||||
<div className="manager-cont-top">
|
||||
我管理的仓库
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">被关注</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">被点赞</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">被复刻</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">有新的里程碑</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
|
||||
<div className="manager-cont-top">
|
||||
我关注的仓库
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">被删除</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">被转移</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">有新的易修</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">有新的合并请求</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
<div className="manager-cont">
|
||||
<div className="manager-cont-title">有新的版本发布</div>
|
||||
<Checkbox defaultChecked='true'>站内信</Checkbox>
|
||||
<Checkbox >邮件</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default NoticeManager;
|
|
@ -0,0 +1,87 @@
|
|||
.notice-manager-tip{
|
||||
font-size:16px;
|
||||
font-weight:400;
|
||||
}
|
||||
.manager-cont-top{
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
height: 44px;
|
||||
padding-left: 20px;
|
||||
background: #FAFCFF;
|
||||
border: 1px solid #89a4f7;
|
||||
line-height: 44px;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.manager-cont{
|
||||
padding: 8px 20px 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
|
||||
.manager-cont-title{
|
||||
width: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
.notice01{
|
||||
.ant-checkbox-disabled.ant-checkbox-checked .ant-checkbox-inner::after {
|
||||
border-color: white;
|
||||
}
|
||||
.ant-checkbox-disabled .ant-checkbox-inner {
|
||||
background-color: #999999 !important;
|
||||
border-color: #999999 !important;
|
||||
}
|
||||
|
||||
.ant-checkbox-checked .ant-checkbox-inner {
|
||||
background-color: #466AFF;
|
||||
border: #466AFF;
|
||||
}
|
||||
|
||||
.ant-checkbox-checked::after{
|
||||
border: 1px solid #466AFF;
|
||||
}
|
||||
.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover .ant-checkbox-inner, .ant-checkbox-input:focus + .ant-checkbox-inner,.ant-radio-checked .ant-radio-inner,.ant-radio-wrapper:hover .ant-radio, .ant-radio:hover .ant-radio-inner, .ant-radio-input:focus + .ant-radio-inner {
|
||||
border-color: #466AFF;
|
||||
}
|
||||
|
||||
.ant-checkbox + span,.manager-cont-title {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.ant-radio-inner::after{
|
||||
background-color:#466AFF;
|
||||
}
|
||||
|
||||
.but25{
|
||||
background-color: #466AFF;
|
||||
color: #fff;
|
||||
border-color: #466AFF;
|
||||
&:hover{
|
||||
opacity: 0.8;
|
||||
background-color: #466AFF;
|
||||
border-color: #466AFF;
|
||||
}
|
||||
&:active{
|
||||
opacity: 1;
|
||||
background-color: #466AFF;
|
||||
border-color: #466AFF;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 5px; /*对垂直流动条有效*/
|
||||
}
|
||||
/*定义滑块颜色、内阴影及圆角*/
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 6px;
|
||||
box-shadow: inset 0 0 6px #FFF;
|
||||
background-color: #D4D4D4;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 6px #fff;
|
||||
background-color: #FFF;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import { Badge, Button, Checkbox, Menu, Pagination } from 'antd';
|
||||
import DelModal from '../../../Component/ModalFun';
|
||||
import NoneData from '../../../Nodata.js';
|
||||
import { noticeSourceType } from '../../../common/static';
|
||||
import './Index.scss';
|
||||
import '../manager/Index.scss'
|
||||
|
||||
function MyNotice(props) {
|
||||
let current_user = props.current_user;
|
||||
let resetUserInfo = props.resetUserInfo;
|
||||
//消息悬停框选择tab
|
||||
let popover = props.location.query && props.location.query.noticeType;
|
||||
let pageSize = 15;
|
||||
const [noticeType, setNoticeType] = useState(popover==="atme"?"2":"0");//消息类别tab栏选择
|
||||
const [selectedNum, setSelectedNum] = useState(0);//@我批量删除选择消息条数
|
||||
const [isBatchDelete, setIsBatchDelete] = useState(false);//@我是否批量删除
|
||||
const [batchDeleteCheckedAll, setBatchDeleteCheckAll] = useState(false);//@我批量删除--全选
|
||||
|
||||
const [noticeUnreadCount, setNoticeUnreadCount] = useState();//未读系统通知数量
|
||||
// const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量
|
||||
const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量
|
||||
const [messageList, setMessageList] = useState([]);
|
||||
const [messTotalCount, setMessTotalCount] = useState();//消息总数
|
||||
const [currentPage, setCurrentPage] = useState(1);//当前页数
|
||||
const [onlyUnread, setOnlyUnread] = useState();
|
||||
|
||||
useEffect(()=>{
|
||||
popover==="atme" ? setNoticeType("2"):setNoticeType("0");
|
||||
},[popover])
|
||||
|
||||
useEffect(()=>{
|
||||
resetUserInfo();
|
||||
},[noticeUnreadCount,atUnreadCount])
|
||||
|
||||
useEffect(() => {
|
||||
getMessageList();
|
||||
}, [noticeType, onlyUnread, currentPage, current_user])
|
||||
|
||||
function getMessageList() {
|
||||
const params = {
|
||||
type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "",
|
||||
status: onlyUnread ? onlyUnread : "",
|
||||
limit: pageSize,
|
||||
page: currentPage,
|
||||
};
|
||||
axios.get(`/users/${current_user.login}/messages.json`, {
|
||||
params: params,
|
||||
}).then((response) => {
|
||||
if(response && response.data){
|
||||
setNoticeUnreadCount(response.data.unread_notification);
|
||||
setAtUnreadCount(response.data.unread_atme);
|
||||
setMessageList(response.data.messages);
|
||||
setMessTotalCount(response.data.total_count);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function readNotice(id){
|
||||
if(id){
|
||||
const params = {
|
||||
type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "",
|
||||
ids:id,
|
||||
};
|
||||
axios.post(`/users/${current_user.login}/messages/read.json`,params).then((response)=>{
|
||||
if(response.status === 200){
|
||||
getMessageList();
|
||||
//已读当前页码最后一条数据时跳转到前一页
|
||||
let totlaPage = Math.ceil((messTotalCount-1)/pageSize);
|
||||
setCurrentPage(currentPage>=totlaPage? totlaPage : currentPage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick(e) {
|
||||
setNoticeType(e.key);
|
||||
setCurrentPage(1);
|
||||
setOnlyUnread();
|
||||
if (e.key != "2") {
|
||||
setIsBatchDelete(false);
|
||||
}
|
||||
}
|
||||
|
||||
function onChange(e) {
|
||||
var checkboxNum = 0;
|
||||
let messageListNew=messageList.slice();
|
||||
messageListNew.map((item)=>{
|
||||
if(item.id===e.target.value){
|
||||
item.checkedBatch = e.target.checked;
|
||||
}
|
||||
item.checkedBatch?checkboxNum++:"";
|
||||
});
|
||||
setMessageList(messageListNew);
|
||||
setSelectedNum(checkboxNum);
|
||||
setBatchDeleteCheckAll(checkboxNum === messageList.length);
|
||||
}
|
||||
|
||||
|
||||
function onChangeAll(e) {
|
||||
setBatchDeleteCheckAll(e.target.checked);
|
||||
setSelectedNum(e.target.checked?messageList.length:0);
|
||||
let messageListNew=messageList.slice();
|
||||
messageListNew.map((item)=>{
|
||||
item.checkedBatch = e.target.checked;
|
||||
});
|
||||
setMessageList(messageListNew);
|
||||
}
|
||||
|
||||
function deleteNotice(id) {
|
||||
const ids = [];
|
||||
if(!id){
|
||||
messageList.map(item=>{
|
||||
item.checkedBatch && ids.push(item.id);
|
||||
});
|
||||
}
|
||||
DelModal({
|
||||
title: noticeType === "1" ? '删除私信用户' : id ? '删除消息' : '批量删除',
|
||||
contentTitle: noticeType === "1" ? '您确定要删除与 xxx 的聊天吗?' : id ? '您确定要删除这条@我消息吗?' : '您确定要删除选中的' + selectedNum + '条消息吗?',
|
||||
content: noticeType === "1" ? '此操作将删除与xxx的聊天框和xxx的所有聊天记录,请进行确认以防数据的丢失' : id ? '此操作将删除这条消息,请进行确认以防数据的丢失' : '此操作将删除选中的' + selectedNum + '条消息,请进行确认以防数据的丢失',
|
||||
onOk: () => {
|
||||
const params = {
|
||||
type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "",
|
||||
ids:id?id:ids,
|
||||
};
|
||||
axios.delete(`/users/${current_user.login}/messages.json`,{
|
||||
data:params,
|
||||
}).then((response)=>{
|
||||
if(response.status === 200){
|
||||
getMessageList();
|
||||
//删除当前页码最后一条数据时跳转到前一页
|
||||
let totlaPage = Math.ceil((messTotalCount-1)/pageSize);
|
||||
setCurrentPage(currentPage>=totlaPage? totlaPage : currentPage);
|
||||
setSelectedNum(0);
|
||||
setBatchDeleteCheckAll(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cancelBatchDelete(){
|
||||
setIsBatchDelete(false);
|
||||
setSelectedNum(0);
|
||||
//取消选中效果
|
||||
let messageListNew=messageList.slice();
|
||||
messageListNew.map((item)=>{
|
||||
item.checkedBatch = false;
|
||||
});
|
||||
setMessageList(messageListNew);
|
||||
setBatchDeleteCheckAll(false);
|
||||
}
|
||||
|
||||
//跳转到消息详情页面
|
||||
function turnToMess(item){
|
||||
if(item.notification_url){
|
||||
window.open(`${item.notification_url}`);
|
||||
readNotice([item.id]);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="notice01">
|
||||
<div className="sshHead">
|
||||
<Menu mode="horizontal" selectedKeys={noticeType} onClick={handleClick}>
|
||||
<Menu.Item key="0"><Badge count={noticeUnreadCount} title="">系统通知</Badge></Menu.Item>
|
||||
{/* <Menu.Item key="1" id="item-private"><Badge count={0}>私信</Badge></Menu.Item> */}
|
||||
<Menu.Item key="2"><Badge count={atUnreadCount}>@我</Badge></Menu.Item>
|
||||
</Menu>
|
||||
{(noticeType==="0" && noticeUnreadCount>0) || (noticeType==="2"&& atUnreadCount>0) ? <button className="but25" onClick={()=>{readNotice([-1])}}>所有{noticeType === "0" ? "系统通知" : noticeType === "1" ? "私信" : "@我"}一键已读</button>:""}
|
||||
</div>
|
||||
|
||||
<div className={isBatchDelete ? "invisible " : "visible"}>
|
||||
<div className="vertical-center-style">
|
||||
{onlyUnread===1 || messageList && messageList.length>0 ? <Checkbox checked={onlyUnread} onChange={(e) => e.target.checked ? setOnlyUnread(1) : setOnlyUnread()}>仅看未读{noticeType === "1" ? `私信(12)` : noticeType === "0" ? `消息(${noticeUnreadCount})` : `消息(${atUnreadCount})`}</Checkbox>:""}
|
||||
</div>
|
||||
{noticeType === "2" && messageList && messageList.length > 0 ? <button className="batchDeleteBut" onClick={() => { setIsBatchDelete(true); }}>批量删除</button> : ""}
|
||||
</div>
|
||||
|
||||
{messageList && messageList.length===0 ? <NoneData _html="暂无相关消息"/>:""}
|
||||
|
||||
{messageList && messageList.length>0 && <div className={isBatchDelete ? 'visible' : 'invisible'}>
|
||||
<div className="vertical-center-style">
|
||||
<Checkbox onChange={onChangeAll} checked={batchDeleteCheckedAll}>全选</Checkbox>
|
||||
已选择 <span className="numberSpan">{selectedNum}</span> 项
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={cancelBatchDelete}>取消</button>
|
||||
<button className="deleteBut" onClick={selectedNum > 0 ? ()=>deleteNotice() : () => { }}>删除</button>
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
{messageList && messageList.map(item => {
|
||||
// 系统消息
|
||||
if (noticeType === "0") {
|
||||
// 消息类别
|
||||
return (
|
||||
<div className="mynotice-content vertical-center-style" key={item.id}>
|
||||
<div className="mynotice-cont stretch-style">
|
||||
{item.status === 1 ? <Badge color="#FA2020" /> : <span className="system-notice-blank"></span>}
|
||||
<i className={"iconfont "+noticeSourceType[item.source]}></i>
|
||||
<span className={`sysNotice-length ${item.notification_url?'highlightSpan':''}`} onClick={() => {turnToMess(item)}} dangerouslySetInnerHTML={{__html: item.content}}></span>
|
||||
</div>
|
||||
<div className="mynotice-cont vertical-center-style float-left-little">
|
||||
<span className={item.status === 1?"timeSpan":""}>{item.time_ago}</span>
|
||||
{item.status === 1 && <span className="invisable-read" onClick={()=>readNotice([item.id])}>标记为已读</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
} else if (noticeType === "2") {
|
||||
//@我
|
||||
return (
|
||||
<div className={`mynotice-content vertical-center-style ${isBatchDelete?'batchDel':''}`} key={item.id}>
|
||||
<div className="mynotice-cont vertical-center-style">
|
||||
<Checkbox value={item.id} className={isBatchDelete ? 'visible-checkbox' : 'invisible-checkbox'} onChange={onChange} checked={item.checkedBatch}></Checkbox>
|
||||
{item.sender && <img src={`https://testforgeplus.trustie.net//${item.sender.image_url}`} className="currentImg" onClick={()=>{window.open(`/${item.sender && item.sender.login}`);}}/>}
|
||||
<div className={`atme-notice-text stretch-style ${item.notification_url && 'highlightSpan'}`} onClick={() => {turnToMess(item)}}>
|
||||
{item.status === 1 ? <Badge color="#FA2020"/> : <span className="system-notice-blank"></span>}
|
||||
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: "<b class = 'atme-notice-name'>" + item.sender.name+ "</b> "+ item.content +" 中@我"}}></span>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mynotice-cont vertical-center-style">
|
||||
<span className={!isBatchDelete && item.status === 1?"timeSpan":""}>{item.time_ago}</span>
|
||||
{!isBatchDelete && item.status === 1 && <span className="invisable-read" onClick={()=>readNotice([item.id])}>标记为已读</span>}
|
||||
{!isBatchDelete && <span className="invisable-read float-left-little" onClick={()=>deleteNotice([item.id])}>删除</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
} else{
|
||||
//私信
|
||||
{/* <div className="mynotice-content vertical-center-style">
|
||||
<Badge count={95}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img" /></Badge>
|
||||
<div className="private-letter-right">
|
||||
<div>
|
||||
<span>蒋宇航</span>
|
||||
<span className="timeSpan">4分钟前</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
<div onClick={() => props.history.push('/settings/notice/privateLetter')}>
|
||||
<span className="highlightSpan letter-length-limit">最好的OpenStack控制台,对标OpenStack社区Horizon项目,最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
}
|
||||
})}
|
||||
|
||||
{/* 分页 */}
|
||||
{!isBatchDelete && <div className="paging">
|
||||
<Pagination
|
||||
simple
|
||||
current = {currentPage}
|
||||
pageSize={pageSize}
|
||||
onChange={(page)=>{setCurrentPage(page)}}
|
||||
total = {messTotalCount}
|
||||
hideOnSinglePage
|
||||
></Pagination>
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default MyNotice;
|
|
@ -0,0 +1,259 @@
|
|||
.whiteBack .boies .sshHead{
|
||||
padding:0 10px 0px 0px;
|
||||
}
|
||||
.sshHead{
|
||||
.ant-badge{
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
}
|
||||
.ant-menu-item{
|
||||
padding:0px;
|
||||
margin-right:34px!important;
|
||||
height: 34px;
|
||||
width: 64px;
|
||||
text-align: center;
|
||||
line-height: 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
li.ant-menu-item, .ant-menu-horizontal > .ant-menu-item {
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
& .ant-menu-item-selected{
|
||||
color: #333333;
|
||||
font-weight: 400;
|
||||
text-shadow: 0.5px 0 #333;
|
||||
border-bottom: 2px solid #2A61FF !important;
|
||||
}
|
||||
|
||||
.ant-badge-count, .ant-badge-dot, .ant-badge .ant-scroll-number-custom-component {
|
||||
right: -6px;
|
||||
-webkit-box-shadow: 0 0 0 0;
|
||||
box-shadow: 0 0 0 0;
|
||||
}
|
||||
.ant-badge-multiple-words {
|
||||
padding: 0 0px;
|
||||
}
|
||||
|
||||
.ant-menu-horizontal {
|
||||
border-bottom: 0px solid #e8e8e8;
|
||||
}
|
||||
|
||||
button{
|
||||
padding:0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
color: #333333;
|
||||
background: #FAFBFC;
|
||||
border: 1px solid #D0D0D0;
|
||||
border-radius: 4px;
|
||||
height: 32px;
|
||||
}
|
||||
button:hover {
|
||||
background: #F3F4F6;
|
||||
}
|
||||
button:active {
|
||||
background: #EBECF0;
|
||||
}
|
||||
|
||||
.deleteBut{
|
||||
color: #DF0002;
|
||||
}
|
||||
|
||||
.deleteBut:hover{
|
||||
background: #DF0002;
|
||||
border: 1px solid #DF0002;
|
||||
color:#FFFFFF;
|
||||
}
|
||||
|
||||
.deleteBut:active{
|
||||
background: #CE0002;
|
||||
border: 1px solid #CE0002;
|
||||
color:#FFFFFF;
|
||||
}
|
||||
|
||||
.mynotice-content {
|
||||
justify-content: space-between;
|
||||
padding: 15px 0 15px 10px;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
color: #333333;
|
||||
&:hover{
|
||||
background: #F3F4F6;
|
||||
}
|
||||
|
||||
& img{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& b{
|
||||
font-weight: 400;
|
||||
text-shadow: 0.5px 0 #333;
|
||||
}
|
||||
|
||||
& .invisable-read{
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .invisable-read{
|
||||
display: block;
|
||||
color: #466AFF;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover{
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .timeSpan{
|
||||
display: none;
|
||||
}
|
||||
|
||||
i{
|
||||
font-size: 16px !important;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.boldSpan{
|
||||
font-weight: 400;
|
||||
text-shadow: 0.5px 0 0 #333;
|
||||
margin: 0 8px;
|
||||
}
|
||||
& .currentImg{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
& .private-letter-img + .ant-badge-count{
|
||||
top: 2px;
|
||||
right: 5px;
|
||||
height: 18px;
|
||||
min-width: 18px;
|
||||
line-height: 18px;
|
||||
padding: 0 0;
|
||||
}
|
||||
|
||||
& .highlightSpan:hover{
|
||||
color: #466AFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mynotice-cont{
|
||||
padding:0;
|
||||
cursor: default;
|
||||
& .visible-checkbox{
|
||||
margin-right: 10px;
|
||||
}
|
||||
& .invisible-checkbox{
|
||||
display: none;
|
||||
}
|
||||
.atme-notice-text{
|
||||
margin-left: 6px;
|
||||
|
||||
& .atme-notice-name{
|
||||
margin: 0 0 ;
|
||||
}
|
||||
|
||||
& .atme-length{
|
||||
max-width: 48rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .ant-badge-count, .ant-badge-dot, .ant-badge .ant-scroll-number-custom-component {
|
||||
-webkit-box-shadow: 0 0 0 0;
|
||||
box-shadow: 0 0 0 0;
|
||||
top: 3px;
|
||||
right: 4px;
|
||||
min-width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
& .system-notice-blank{
|
||||
margin-right: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.batchDel{
|
||||
& .currentImg, & .atme-notice-text{
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.baselineDiv{
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.visible {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
color: #333333;
|
||||
margin-bottom: 5px;
|
||||
button{
|
||||
padding:0px 12px;
|
||||
}
|
||||
.batchDeleteBut{
|
||||
border:1px solid #466AFF;
|
||||
color: #466AFF;
|
||||
}
|
||||
}
|
||||
|
||||
.private-letter-right {
|
||||
flex: auto;
|
||||
margin: 0px 10px 0 16px;
|
||||
& div{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.letter-length-limit{
|
||||
max-width: 50rem;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.sysNotice-length{
|
||||
max-width: 52rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
||||
.numberSpan{
|
||||
color: #466AFF;
|
||||
}
|
||||
|
||||
.vertical-center-style{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stretch-style{
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.float-left-little{
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.float-right-little{
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.paging{
|
||||
text-align: center;
|
||||
margin: 12px;
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import './Index.scss'
|
||||
import '../manager/Index.scss'
|
||||
import { Button, Input, Icon, Badge } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
function PrivateLetter(props){
|
||||
|
||||
const { TextArea,Search } = Input;
|
||||
|
||||
function deleteNotice(){
|
||||
alert("删除消息");
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="private-letter notice01">
|
||||
<div className="pl-content">
|
||||
<div className="pl-name vertical-center-style">
|
||||
<Link to="/settings/notice/myNotice"><i className="iconfont icon-zuojiantou"></i></Link>
|
||||
<span>蒋宇航</span>
|
||||
</div>
|
||||
<div className="plcontent-list">
|
||||
<div className="vertical-center-style plclo">
|
||||
<img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg"/>
|
||||
<div className="message-bubble mb-other"></div>
|
||||
<div className="notice-content vertical-center-style">
|
||||
嗨在吗?哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
|
||||
<a className="pld01">删除</a>
|
||||
</div>
|
||||
<span>2021-08-29 11:59</span>
|
||||
</div>
|
||||
|
||||
<div className="notice-my vertical-center-style plclo">
|
||||
<img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg"/>
|
||||
<div className="message-bubble"></div>
|
||||
<div className="notice-content vertical-center-style">
|
||||
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
|
||||
<a className="pld01">删除</a>
|
||||
</div>
|
||||
<span>2021-08-29 11:59</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="private-letter-present">
|
||||
<div>
|
||||
<img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg"/>
|
||||
<TextArea className="private-letter-cont" rows="2" />
|
||||
</div>
|
||||
<div className="presentNotice">
|
||||
<Button className="private-letter-cont-mt25 but25" type="primary">发送</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pl-list">
|
||||
<div className="list-sort vertical-center-style">
|
||||
<Search
|
||||
className=""
|
||||
placeholder="搜索用户"
|
||||
enterButton={<i className="iconfont icon-sousuo"></i>}
|
||||
onSearch={value => console.log(value)}
|
||||
style={{ width: 265 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="list-scroll">
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-scroll-content vertical-center-style">
|
||||
<Badge count={5}><img src="https://testforgeplus.trustie.net//system/lets/letter_avatars/2/D/208_124_118/120.png" className="currentImg private-letter-img"/></Badge>
|
||||
<div className="private-letter-list-content-right">
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span>蒋宇航</span>
|
||||
<span className="private-letter-list-content-right-content-top-timeSpan">4分钟前</span>
|
||||
</div>
|
||||
<div className="ls-cont vertical-center-style">
|
||||
<span className="ls-content-span">最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。</span>
|
||||
<a onClick={deleteNotice}>删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default PrivateLetter;
|
|
@ -0,0 +1,192 @@
|
|||
.private-letter{
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
border: 1px solid rgba(151, 151, 151, 0.24);
|
||||
display: flex;
|
||||
|
||||
.currentImg{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
& .private-letter-img + .ant-badge-count{
|
||||
top: 2px;
|
||||
right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-content{
|
||||
flex: auto;
|
||||
.pl-name{
|
||||
height: 3rem;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
|
||||
&>a{
|
||||
position: relative;
|
||||
left: 26px;
|
||||
color:#999999;
|
||||
}
|
||||
span{
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
flex: auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.plcontent-list{
|
||||
height: 24rem;
|
||||
padding:20px;
|
||||
overflow-y: scroll;
|
||||
|
||||
.plclo{
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.notice-my{
|
||||
flex-direction: row-reverse;
|
||||
|
||||
& .pld01{
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .notice-content{
|
||||
padding:8px 10px;
|
||||
background: #F4F4F4;
|
||||
border-radius:5px;
|
||||
position: relative;
|
||||
max-width: 20rem;
|
||||
|
||||
.pld01{
|
||||
display: none;
|
||||
|
||||
}
|
||||
&:hover .pld01{
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -2em;
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
&:hover{
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .message-bubble{
|
||||
position:relative;
|
||||
width:0;
|
||||
height:0;
|
||||
font-size:0;
|
||||
border:solid 8px;
|
||||
border-color:#FFFFFF #FFFFFF #FFFFFF #F4F4F4;
|
||||
}
|
||||
|
||||
& .mb-other{
|
||||
border-color:#FFFFFF #F4F4F4 #FFFFFF #FFFFFF;
|
||||
}
|
||||
|
||||
& span{
|
||||
margin: 0 10px;
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
line-height: 17px;
|
||||
}
|
||||
}
|
||||
.private-letter-present{
|
||||
padding:20px 20px;
|
||||
& div{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex: auto;
|
||||
}
|
||||
.private-letter-cont-mt25{
|
||||
margin-top: 8px;
|
||||
padding:0 22px;
|
||||
}
|
||||
|
||||
.private-letter-cont, .private-letter-cont .ant-input-suffix{
|
||||
background-color: #fafafa !important;
|
||||
margin-left: 17px;
|
||||
}
|
||||
|
||||
.private-letter-cont{
|
||||
resize:none;
|
||||
&:hover,&:focus{
|
||||
background-color: #fafafa !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pl-list{
|
||||
width: 30%;
|
||||
border-left: 1px solid #EEEEEE;
|
||||
.list-sort{
|
||||
justify-content: center;
|
||||
height: 3rem;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
& .ant-btn-primary{
|
||||
width: 2.3rem;
|
||||
color: #466AFF;
|
||||
background: #eff2ff;
|
||||
border: 1px solid rgba(151, 151, 151, 0.24);
|
||||
}
|
||||
|
||||
& .ant-btn{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// & .ant-input:hover{
|
||||
// border: 1px solid red !important;
|
||||
// }
|
||||
}
|
||||
|
||||
.list-scroll{
|
||||
height: 32.4rem;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.list-scroll-content{
|
||||
padding: 10px 10px 15px 15px;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
// &:last-child{
|
||||
// border-bottom: 0px;
|
||||
// }
|
||||
&:hover{
|
||||
background: #F3F4F6;
|
||||
}
|
||||
& a{
|
||||
display: none;
|
||||
}
|
||||
&:hover a{
|
||||
display: block;
|
||||
color: #466AFF;
|
||||
}
|
||||
}
|
||||
|
||||
.ls-cont{
|
||||
width: 13rem;
|
||||
// flex: auto;
|
||||
margin-left: 15px;
|
||||
justify-content: space-between;
|
||||
& .ls-content-span{
|
||||
max-width: 11rem;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.vertical-center-style{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
img{
|
||||
width: 45x;
|
||||
height: 45px;
|
||||
}
|
|
@ -34,7 +34,7 @@ function New({ form , showNotification , history }) {
|
|||
return(
|
||||
<div>
|
||||
<div className="sshHead">
|
||||
<span><Link to={`/settings/SSH`} className="color-blue">SSH密钥</Link><i className="iconfont icon-youjiantou ml5 mr5 font-12"></i>添加SSH密钥</span>
|
||||
<span className="add-SSH-title"><Link to={`/settings/SSH`} className="blue-Purple">SSH密钥</Link><i className="iconfont icon-youjiantou ml5 mr5 font-12"></i>添加SSH密钥</span>
|
||||
</div>
|
||||
<Form className="sshForm">
|
||||
<Form.Item label="标题" validateStatus={msg && msg.status === 10001 ? "error":undefined} help={msg && msg.status === 10001 ? msg.message:undefined}>
|
||||
|
@ -51,7 +51,7 @@ function New({ form , showNotification , history }) {
|
|||
<TextArea placeholder="支持以'ssh-rsa','ssh-dss','ssh-ed25519','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521'开头" autoSize={{ minRows: 6, maxRows: 6 }}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Button type="primary" style={{width:"100px"}} onClick={submit}>确定</Button>
|
||||
<button style={{width:"100px"}} onClick={submit} className="but25">确定</button>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -55,8 +55,8 @@ function SSH(props) {
|
|||
<DeleteBox visible={visible} onCancel={()=>setVisible(false)} onSuccess={onSuccess}/>
|
||||
<SSHDetail visible={visibleDesc} onCancel={()=>setVisibleDesc(false)} desc={content}/>
|
||||
<div className="sshHead">
|
||||
<span>SSH密钥</span>
|
||||
<Button type="primary" size="large" onClick={()=>props.history.push('/settings/SSH/new')}>添加SSH密钥</Button>
|
||||
<span className="text-shadow07">SSH密钥</span>
|
||||
<button type="primary" size="large" onClick={()=>props.history.push('/settings/SSH/new')} className="but25">添加SSH密钥</button>
|
||||
</div>
|
||||
{
|
||||
list && list.length > 0 &&
|
||||
|
@ -64,7 +64,7 @@ function SSH(props) {
|
|||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<List.Item>
|
||||
<List.Item key={i.id}>
|
||||
<img src={miyao} alt=""/>
|
||||
<div>
|
||||
<p className="color-grey-3"><a className="task-hide" style={{display:"block",fontWeight:"500"}} onClick={()=>{setContent(i);setVisibleDesc(true)}}>{i.name}</a></p>
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
export const noticeSourceType = {
|
||||
// 易修
|
||||
IssueAssigned:"icon-yixiuicon1", // 有新指派给我的易修
|
||||
IssueAssignerExpire:"icon-yixiuicon1", // 我负责的易修截止日期到达最后一天
|
||||
IssueAtme:"icon-yixiuicon1", // 在易修中@我
|
||||
IssueChanged:"icon-yixiuicon1", // 我创建或负责的易修状态变更
|
||||
IssueCreatorExpire:"icon-yixiuicon1", // 我创建的易修截止日期到达最后一天
|
||||
IssueDelete:"icon-yixiuicon1", // 我创建或负责的易修删除
|
||||
IssueDeleted:"icon-yixiuicon1", // 我创建或负责的易修删除
|
||||
IssueJournal:"icon-yixiuicon1", // 我创建或负责的易修有新的评论
|
||||
//平台通知
|
||||
LoginIpTip:"icon-xitongtongzhiicon", //登录异常提示
|
||||
//个人状态类通知
|
||||
OrganizationJoined:"icon-xiaoxi2", // 账号被拉入组织
|
||||
OrganizationLeft:"icon-xiaoxi2", // 账号被移出组织
|
||||
OrganizationRole:"icon-xiaoxi2", // 账号组织权限变更
|
||||
ProjectJoined:"icon-xiaoxi2", // 账号被拉入项目
|
||||
ProjectLeft:"icon-xiaoxi2", // 账号被移出项目
|
||||
ProjectRole:"icon-xiaoxi2", // 账号仓库权限变更
|
||||
|
||||
//其他仓库通知
|
||||
ProjectDelete:"icon-daimakuicon1", // 我关注的仓库被删除
|
||||
ProjectFollowed:"icon-daimakuicon1", // 我管理的仓库被关注
|
||||
ProjectForked:"icon-daimakuicon1", // 我管理的仓库被复刻
|
||||
ProjectIssue:"icon-daimakuicon1", // 我管理/关注的仓库有新的易修
|
||||
ProjectSettingChanged:"icon-daimakuicon1", // 我管理的仓库项目设置被更改
|
||||
ProjectTransfer:"icon-daimakuicon1", // 我关注的仓库被转移
|
||||
ProjectVersion:"icon-daimakuicon1", // 我关注的仓库有新的发行版
|
||||
ProjectMemberJoined:"icon-daimakuicon1", // 我管理的仓库有成员加入
|
||||
ProjectMemberLeft:"icon-daimakuicon1", // 我管理的仓库有成员移出
|
||||
ProjectPraised:"icon-daimakuicon1", // 我管理的仓库被点赞
|
||||
//合并请求类通知
|
||||
ProjectPullRequest:"icon-hebingqingqiuicon", // 我管理/关注的仓库有新的合并请求
|
||||
PullRequestAssigned:"icon-hebingqingqiuicon", // 有新指派给我的合并请求
|
||||
PullRequestAtme:"icon-hebingqingqiuicon", // 在合并请求中@我
|
||||
PullRequestChanged:"icon-hebingqingqiuicon", // 我创建或负责的合并请求状态变更
|
||||
PullRequestJournal:"icon-hebingqingqiuicon", // 我创建或负责的合并请求有新的评论
|
||||
PullRequestClosed:"icon-hebingqingqiuicon", // 提交的合并请求被拒绝
|
||||
PullRequestMerged:"icon-hebingqingqiuicon", // 提交的合并请求被合并
|
||||
PullRequestClosed:"icon-hebingqingqiuicon", // 提交的合并请求被关闭
|
||||
//里程碑
|
||||
ProjectMilestone:"icon-lichengbeiicon", // 我管理的仓库有新的里程碑
|
||||
};
|
|
@ -0,0 +1,105 @@
|
|||
import { notification ,message} from 'antd';
|
||||
import axios from 'axios';
|
||||
import cookie from 'react-cookies';
|
||||
import Login from './Wiki/components/Login';
|
||||
|
||||
export const TokenKey = 'autologin_trustie';
|
||||
export default function javaFetch(productUrl,testUrl,developUrl ){
|
||||
let actionUrl='';
|
||||
if (window.location.href.indexOf('localhost') > -1) {
|
||||
actionUrl = developUrl;
|
||||
} else if(window.location.href.indexOf('testforgeplus')>-1){
|
||||
actionUrl = testUrl;
|
||||
axios.defaults.withCredentials = true;
|
||||
}else if (window.location.href.indexOf('forgeplus') > -1) {
|
||||
actionUrl = productUrl;
|
||||
axios.defaults.withCredentials = true;
|
||||
}
|
||||
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
baseURL: actionUrl,
|
||||
timeout: 10000, // 请求超时时间
|
||||
});
|
||||
|
||||
// request拦截器
|
||||
service.interceptors.request.use(config => {
|
||||
if (cookie.load(TokenKey)) {
|
||||
console.log(cookie.load(TokenKey));
|
||||
config.headers['Authorization'] = cookie.load(TokenKey); // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
if (window.location.port === "3007") {
|
||||
// 模拟token为登录用户
|
||||
const token = sessionStorage.token;
|
||||
if (config.url.indexOf('?') === -1) {
|
||||
config.url = `${config.url}?token=${token}`;
|
||||
} else {
|
||||
config.url = `${config.url}&token=${token}`;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}, error => {
|
||||
console.log(error); // for debug
|
||||
// Promise.reject(error);
|
||||
});
|
||||
// respone拦截器
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
const res = response||{};
|
||||
if (res.status === 400) {
|
||||
message.error(res.data.message || '操作失败');
|
||||
return Promise.reject('error');
|
||||
}
|
||||
if (res.status === 401) {
|
||||
message.error(res.data.message || '登录信息已过期');
|
||||
return Promise.reject('error');
|
||||
}
|
||||
if (res.status === 403) {
|
||||
message.error(res.data.message || '无权限!');
|
||||
return Promise.reject('error');
|
||||
}
|
||||
if (res.status === 40001) {
|
||||
notification.open({
|
||||
message: "提示",
|
||||
description: '账户或密码错误!',
|
||||
});
|
||||
return Promise.reject('error');
|
||||
}
|
||||
if (response.status !== 200 && res.status !== 200) {
|
||||
notification.open({
|
||||
message: "提示",
|
||||
description: res.message,
|
||||
});
|
||||
} else {
|
||||
return response.data;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
let res = error.response||{};
|
||||
if (res.status === 400) {
|
||||
message.error(res.data.message || '操作失败');
|
||||
return Promise.reject('error');
|
||||
}
|
||||
if (res.status === 401) {
|
||||
message.error(res.data.message || '登录信息已过期');
|
||||
Login();
|
||||
return Promise.reject('error');
|
||||
}
|
||||
if (res.status === 403) {
|
||||
message.error(res.data.message || '无权限!');
|
||||
return Promise.reject('error');
|
||||
}
|
||||
notification.open({
|
||||
message: "提示",
|
||||
description: error.message,
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
console.log(service);
|
||||
console.log(typeof service);
|
||||
|
||||
return {service,actionUrl};
|
||||
}
|
|
@ -125,7 +125,7 @@ export default Form.create()(
|
|||
</Form.Item>
|
||||
<AlignCenter>
|
||||
<span className="ant-form-item-label"></span>
|
||||
<Button type={"primary"} onClick={submit}>提交</Button>
|
||||
<Button className="but25" type={"primary"} onClick={submit}>提交</Button>
|
||||
{/* <Button type={"default"} onClick={()=>props.history.push(`/${current_user && current_user.login}`)} className="ml20">取消</Button> */}
|
||||
</AlignCenter>
|
||||
</Form>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import './Index.scss';
|
||||
import { Menu } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Base from './Base';
|
||||
import Password from './Password';
|
||||
import './Index.scss';
|
||||
import '../../SecuritySetting/notice/manager/Index.scss';
|
||||
|
||||
function Index(props){
|
||||
// const { username } = props && props.match && props.match.params;
|
||||
|
@ -25,11 +26,14 @@ function Index(props){
|
|||
|
||||
|
||||
return(
|
||||
<div>
|
||||
<Menu selectedKeys={[key]} mode={'horizontal'} className="infosRightMenu">
|
||||
<Menu.Item key="0"><Link to={`/settings/profile`}>基本资料</Link></Menu.Item>
|
||||
{/* <Menu.Item key="1"><Link to={`/${username}/password`}>密码管理</Link></Menu.Item> */}
|
||||
</Menu>
|
||||
<div className="notice01">
|
||||
<div className="sshHead">
|
||||
<Menu selectedKeys={[key]} mode={'horizontal'} className="infosRightMenu">
|
||||
<Menu.Item key="0"><Link to={`/settings/profile`}>基本资料</Link></Menu.Item>
|
||||
{/* <Menu.Item key="0"><Link to={`/settings/profile`}>基本资料</Link></Menu.Item> */}
|
||||
{/* <Menu.Item key="1"><Link to={`/${username}/password`}>密码管理</Link></Menu.Item> */}
|
||||
</Menu>
|
||||
</div>
|
||||
<div style={{padding:"20px"}}>
|
||||
{
|
||||
key === "0" ?
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
.ant-menu-item{
|
||||
a{
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
position: relative;
|
||||
bottom: -10px;
|
||||
}
|
||||
a:hover{
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -89,7 +89,7 @@ export default Form.create()(
|
|||
</Form.Item>
|
||||
<AlignCenter style={{marginTop:"20px"}}>
|
||||
<span className="ant-form-item-label"></span>
|
||||
<Button type={"primary"} onClick={submit}>提交</Button>
|
||||
<Button className="but25" type={"primary"} onClick={submit}>提交</Button>
|
||||
<Button type={"default"} onClick={()=>props.history.push(`/${username}`)} className="ml20">取消</Button>
|
||||
</AlignCenter>
|
||||
</Form>
|
||||
|
|
Loading…
Reference in New Issue