pull upstream pre_develop_dev

This commit is contained in:
谢思 2021-09-29 21:06:25 +08:00
commit 1750544c0d
58 changed files with 2381 additions and 188 deletions

13
package-lock.json generated
View File

@ -7286,8 +7286,7 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -7704,8 +7703,7 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -7761,7 +7759,6 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -7805,14 +7802,12 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
"optional": true
} }
} }
}, },

View File

@ -125,7 +125,11 @@
"scripts": { "scripts": {
"start": "node --max_old_space_size=15360 scripts/start.js", "start": "node --max_old_space_size=15360 scripts/start.js",
"build": "cross-env 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",
<<<<<<< HEAD
"test-build": "cross-env NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js", "test-build": "cross-env NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js",
=======
"test-build": "NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js",
>>>>>>> 9be808e37f9c52698b7861d74cd537e356aafb6d
"pre-build": "NODE_ENV=preBuild 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", "gen_stats": "NODE_ENV=production webpack --profile --config=./config/webpack.config.prod.js --json > stats.json",
"ana": "webpack-bundle-analyzer ./stats.json", "ana": "webpack-bundle-analyzer ./stats.json",

View File

@ -1,8 +1,14 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 2340181 */ font-family: "iconfont"; /* Project id 2340181 */
<<<<<<< HEAD
src: url('iconfont.woff2?t=1632881251448') format('woff2'), src: url('iconfont.woff2?t=1632881251448') format('woff2'),
url('iconfont.woff?t=1632881251448') format('woff'), url('iconfont.woff?t=1632881251448') format('woff'),
url('iconfont.ttf?t=1632881251448') format('truetype'); url('iconfont.ttf?t=1632881251448') format('truetype');
=======
src: url('iconfont.woff2?t=1631773579834') format('woff2'),
url('iconfont.woff?t=1631773579834') format('woff'),
url('iconfont.ttf?t=1631773579834') format('truetype');
>>>>>>> 9be808e37f9c52698b7861d74cd537e356aafb6d
} }
.iconfont { .iconfont {
@ -13,6 +19,7 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
<<<<<<< HEAD
.icon-wenjian7:before { .icon-wenjian7:before {
content: "\e8e0"; content: "\e8e0";
} }
@ -37,6 +44,8 @@
content: "\e8db"; content: "\e8db";
} }
=======
>>>>>>> 9be808e37f9c52698b7861d74cd537e356aafb6d
.icon-xiangmubiaoqian:before { .icon-xiangmubiaoqian:before {
content: "\e8da"; content: "\e8da";
} }

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,7 @@
"description": "", "description": "",
"glyphs": [ "glyphs": [
{ {
<<<<<<< HEAD
"icon_id": "24656750", "icon_id": "24656750",
"name": "文件", "name": "文件",
"font_class": "wenjian7", "font_class": "wenjian7",
@ -48,6 +49,8 @@
"unicode_decimal": 59611 "unicode_decimal": 59611
}, },
{ {
=======
>>>>>>> 9be808e37f9c52698b7861d74cd537e356aafb6d
"icon_id": "24378423", "icon_id": "24378423",
"name": "项目标签", "name": "项目标签",
"font_class": "xiangmubiaoqian", "font_class": "xiangmubiaoqian",

View File

@ -3319,9 +3319,9 @@
text = text.replace(emailReg, function ($1, $2, $3, $4) { text = text.replace(emailReg, function ($1, $2, $3, $4) {
return $1.replace(/@/g, "_#_&#64;_#_"); return $1.replace(/@/g, "_#_&#64;_#_");
}); });
// " + editormd.urls.atLinkBase + "" + $2 + "
text = text.replace(atLinkReg, function ($1, $2) { text = text.replace(atLinkReg, function ($1, $2) {
return "<a href=\"" + editormd.urls.atLinkBase + "" + $2 + "\" title=\"&#64;" + $2 + "\" class=\"at-link\">" + $1 + "</a>"; return "<span title=\"&#64;" + $2 + "\" class=\"at-link\"> " + $1 + " </span>";
}).replace(/_#_&#64;_#_/g, "@"); }).replace(/_#_&#64;_#_/g, "@");
} }

View File

@ -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>
)
}

View File

@ -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;
}
}

View File

@ -27,7 +27,11 @@ function Releases({owner,projectsId,releaseVersions , baseOperate , projectType}
}) })
: :
<div className="mt8"> <div className="mt8">
<<<<<<< HEAD
您暂未发布任何版本{baseOperate && projectType !==2 && <Link className="color-blue ml20" to={{pathname:`/${owner}/${projectsId}/releases/new`,state:{stable:true}}}>创建新版本</Link>} 您暂未发布任何版本{baseOperate && projectType !==2 && <Link className="color-blue ml20" to={{pathname:`/${owner}/${projectsId}/releases/new`,state:{stable:true}}}>创建新版本</Link>}
=======
您暂未发布任何版本{baseOperate && projectType !==2 && <Link className="color-blue ml20" to={`/${owner}/${projectsId}/releases/new`}>创建新版本</Link>}
>>>>>>> 9be808e37f9c52698b7861d74cd537e356aafb6d
</div> </div>
} }

View File

@ -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

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import AccountProfile from "../../modules/user/AccountProfile"; import AccountProfile from "../../modules/user/AccountProfile";
import { getImageUrl } from 'educoder' import { getImageUrl } from 'educoder'
import axios from 'axios'; 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 { Link } from 'react-router-dom';
import LoginDialog from '../../modules/login/LoginDialog'; import LoginDialog from '../../modules/login/LoginDialog';
@ -13,6 +13,7 @@ import '../../modules/tpm/TPMIndex.css';
import CheckProfile from '../Component/ProfileModal/Profile'; import CheckProfile from '../Component/ProfileModal/Profile';
import './header.scss'; import './header.scss';
import NoticeContent from './NoticeContent';
const $ = window.$ const $ = window.$
// TODO 这部分脚本从公共脚本中直接调用 // TODO 这部分脚本从公共脚本中直接调用
const { Search } = Input; const { Search } = Input;
@ -47,6 +48,7 @@ class NewHeader extends Component {
settings: null, settings: null,
visiblemyss: false, visiblemyss: false,
openSearch:false, openSearch:false,
visible:false, //浮动消息框展示控制
} }
} }
componentDidMount() { componentDidMount() {
@ -92,9 +94,6 @@ class NewHeader extends Component {
this.setState({ this.setState({
user: newProps.user user: newProps.user
}) })
if (newProps.Headertop !== undefined) {
old_url = newProps.Headertop.old_url
}
} }
educoderlogin = () => { educoderlogin = () => {
@ -255,6 +254,7 @@ class NewHeader extends Component {
</div> </div>
) )
} }
renderMenu=(personal)=>{ renderMenu=(personal)=>{
const { current_user } = this.props; const { current_user } = this.props;
return( return(
@ -275,8 +275,12 @@ class NewHeader extends Component {
) )
} }
handleVisibleChange = visible => {
this.setState({ visible });
};
render() { render() {
const { match} = this.props; const { match ,resetUserInfo ,showNotification} = this.props;
let current_user = this.props.user; let current_user = this.props.user;
let { let {
AccountProfiletype, AccountProfiletype,
@ -285,6 +289,7 @@ class NewHeader extends Component {
headtypesonClickbool, headtypesonClickbool,
headtypess, headtypess,
settings, settings,
visible,
} = this.state; } = this.state;
/*用户名称 用户头像url*/ /*用户名称 用户头像url*/
let activeIndex = false; let activeIndex = false;
@ -395,7 +400,7 @@ class NewHeader extends Component {
{ {
settings.navbar && settings.navbar.map((item, key) => { settings.navbar && settings.navbar.map((item, key) => {
var new_link = item.link; 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 var is_hidden = item.hidden
if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) { if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) {
if (user_login) { if (user_login) {
@ -426,25 +431,30 @@ class NewHeader extends Component {
} }
</div> </div>
<div className="head-right"> <div className="head-right">
{/* {search_url ? this.SearchInput(openSearch,search_url):""} */}
{ search_url && <HeadSearch {...this.props}/>} { search_url && <HeadSearch {...this.props}/>}
{ {
current_user && (current_user.main_site || current_user.login) && (settings && settings.add && settings.add.length>0)? 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"> <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>:"" </Dropdown>:""
} }
{this.props.user && this.props.user.login && notice_url ? {current_user && current_user.login ?
<div className="ml30 edu-menu-panel"> <Popover
{user && user.login && overlayClassName="notice-popover"
<a href={`${notice_url}`} style={{ position: 'relative' }}> placement={`bottomRight`}
<i className="iconfont icon-xiaoxilingdang color-grey-6"></i> content={<NoticeContent visible={visible} current_user={current_user} showNotification={showNotification} resetUserInfo={resetUserInfo}/>}
<span className="newslight" style={{ display: this.props.Headertop === undefined ? "none" : this.props.Headertop.new_message === true ? "block" : "none" }}> visible={visible}
</span> onVisibleChange={this.handleVisibleChange}
</a> destroyTooltipOnHide
} >
</div>:"" <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> </div>
{!user || (user && !user.login) ? {!user || (user && !user.login) ?

View File

@ -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, //01
// 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); }} //ajaxfunction
// 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); }} //ajaxfunction
// 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>&nbsp;&nbsp;&nbsp;" + 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;

View File

@ -24,8 +24,9 @@
width: 34px; width: 34px;
height: 34px; height: 34px;
border-radius: 50%; border-radius: 50%;
margin-left: 30px; margin-left: 15px;
} }
.currentMenu{ .currentMenu{
width: 120px; width: 120px;
text-align: center; text-align: center;
@ -127,3 +128,138 @@
text-align: right; 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;
}

View File

@ -0,0 +1,70 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { Spin } from 'antd';
import { truncateCommitId } from '../common/util';
import Nodata from '../Nodata';
import { Link } from 'react-router-dom'
export default (( props, { projectDetail }) => {
const [isSpin, setSpin] = useState(true);
const [data, setData] = useState(undefined);
const { projectsId , owner } = props.match.params;
useEffect(() => {
if (projectsId) {
const url = `/${owner}/${projectsId}/tags.json`;
axios.get(url).then((result) => {
if (result) {
setSpin(false);
setData(result.data);
}
}).catch(error => {
console.log(error);
})
}
}, [owner, projectsId]);
return (
<div className="main" style={{padding:"0px",border:"none"}}>
<Spin spinning={isSpin}>
<div style={{minHeight:"400px"}}>
{
data && data.length > 0 &&
<div className="div_table">
<ul className="ul_thead">
<li>
<span className="flex1">标记名</span>
<span>提交信息</span>
<span className="ul_tbody_forth">下载</span>
</li>
</ul>
<ul className="ul_tbody">
{
data.map((item, key) => {
return (
<li>
<span className="flex1">
<i className="iconfont icon-biaoqian3 font-16 mr5 color-grey-8"></i>
<span className="font-16">{item.name}</span>
</span>
<span className="ul_tbody_third">
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.id}`)}`} className="commitKey" style={{ "marginLeft": 0 }}>{truncateCommitId(`${item.id}`)}</Link>
</span>
<span className="ul_tbody_forth">
<a href={item.tarball_url} style={{ color: "#4CC1DA" }} className="mr30"><i className="iconfont icon-TAR font-18 mr5"></i>TAR</a>
<a href={item.zipball_url} style={{ color: "#28BD6C" }}><i className="iconfont icon-ZIP font-18 mr5"></i>ZIP</a>
</span>
</li>
)
})
}
</ul>
</div>
}
{ data && data.length === 0 && <Nodata _html={`暂无标签!`}/> }
</div>
</Spin>
</div>
)
})

View File

@ -255,7 +255,11 @@ class Detail extends Component {
open_devops: flag open_devops: flag
}) })
} }
canvasChannel = () => { /**
*
* @param {*} deleteFlag 同步镜像需要提示成功且未成功的情况下不需要删除项目
*/
canvasChannel = (deleteFlag) => {
const name = window.location.hostname === "localhost" ? "testforgeplus.trustie.net" : window.location.hostname; const name = window.location.hostname === "localhost" ? "testforgeplus.trustie.net" : window.location.hostname;
const actioncable = require("actioncable"); const actioncable = require("actioncable");
var project = this.state.project; var project = this.state.project;
@ -272,10 +276,15 @@ class Detail extends Component {
console.log(`###### ---received data--- ######`); console.log(`###### ---received data--- ######`);
console.log(data); console.log(data);
if (data) { if (data) {
if(deleteFlag){
this.props.showNotification("镜像同步成功!");
window.location.reload();
}else{
if (data.project && data.project.mirror_status === 2) { if (data.project && data.project.mirror_status === 2) {
this.deleteProjectBack(); this.deleteProjectBack();
} }
this.getDetail(); this.getDetail();
}
this.setState({ this.setState({
firstSync: false, firstSync: false,
secondSync: false secondSync: false
@ -428,8 +437,10 @@ class Detail extends Component {
const url = `/${owner}/${projectsId}/sync_mirror.json`; const url = `/${owner}/${projectsId}/sync_mirror.json`;
axios.post(url).then(result => { axios.post(url).then(result => {
if (result && result.data && result.data.status === 0) { if (result && result.data && result.data.status === 0) {
this.props.showNotification("镜像同步成功!"); this.setState({
this.getProject(2); secondSync:true
})
this.canvasChannel(true);
} else { } else {
this.props.showNotification("镜像同步失败!"); this.props.showNotification("镜像同步失败!");
} }
@ -670,17 +681,22 @@ class Detail extends Component {
(props) => (<OrderNew {...this.props} {...props} {...this.state} {...common} />) (props) => (<OrderNew {...this.props} {...props} {...this.state} {...common} />)
} }
></Route> ></Route>
{/* 修改详情 */} {/* 修改详情 updatedetail*/}
<Route path="/:owner/:projectsId/issues/:orderId/updatedetail" <Route path="/:owner/:projectsId/issues/:orderId/:operateName"
render={ render={
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} />) (props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} />)
} }
></Route> ></Route>
{/* 复制详情 */} {/* 复制详情 copyetail*/}
<Route path="/:owner/:projectsId/issues/:orderId/copyetail" {/* <Route path="/:owner/:projectsId/issues/:orderId/copyetail"
render={ render={
(props) => (<OrdercopyDetail {...this.props} {...props} {...this.state} {...common} />) (props) => (<OrdercopyDetail {...this.props} {...props} {...this.state} {...common} />)
} }
></Route> */}
<Route path="/:owner/:projectsId/issues/:orderId/:operateName"
render={
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} />)
}
></Route> ></Route>
{/* 任务详情 */} {/* 任务详情 */}
<Route path="/:owner/:projectsId/issues/:orderId" <Route path="/:owner/:projectsId/issues/:orderId"

View File

@ -426,14 +426,15 @@
border-right: none; border-right: none;
} }
.gitAddressClone > input{ .gitAddressClone > input{
border:none;
outline: none; outline: none;
padding: 0px 8px; padding: 0px 8px;
height: 40px; height: 38px;
line-height: 40px; line-height: 38px;
border-radius: 0px; border: none!important;
border: 1px solid #eee; border-right: 1px solid #eee!important;
border-radius: 4px 0px 0px 4px;
flex: 1; flex: 1;
max-width: 249px;
} }
.wrap-commit-table .ant-table-small > .ant-table-content > .ant-table-body{ .wrap-commit-table .ant-table-small > .ant-table-content > .ant-table-body{
margin:0px; margin:0px;

View File

@ -424,7 +424,7 @@ class MessageCount extends Component {
</span> </span>
</span> </span>
<span className="ml25"> <span className="ml25">
<span className="color-grey-8"></span> <span className="color-grey-8"></span>
<span className="color-grey-3"> <span className="color-grey-3">
{data.issue.issue_tags && {data.issue.issue_tags &&
data.issue.issue_tags.length > 0 data.issue.issue_tags.length > 0

View File

@ -110,6 +110,7 @@ class NewMerge extends Component {
const { author , identifier } =oldProject; const { author , identifier } =oldProject;
url += `/${mergeBranch}...${author && author.login}/${identifier}:${localBranch}.json`; url += `/${mergeBranch}...${author && author.login}/${identifier}:${localBranch}.json`;
} }
this.setState({isSpin: true});
axios.get(url).then(result=>{ axios.get(url).then(result=>{
if(result){ if(result){
if (result.data.status === 0) { if (result.data.status === 0) {
@ -128,7 +129,9 @@ class NewMerge extends Component {
comparesData:result.data comparesData:result.data
}) })
} }
}).catch(error=>{}) }).catch(error=>{
this.setState({isSpin: false});
})
} }
} }

View File

@ -43,7 +43,7 @@ class merge extends Component {
//设置选择高亮 //设置选择高亮
openselect: 1, openselect: 1,
closeselect: undefined, closeselect: undefined,
issue_tag_ids: "标", issue_tag_ids: "标",
fixed_version_ids: "里程碑", fixed_version_ids: "里程碑",
assigned_to_ids: "审查人员", assigned_to_ids: "审查人员",
paix: "排序", paix: "排序",
@ -198,7 +198,7 @@ class merge extends Component {
}); });
this.setState({ this.setState({
status_type: type, status_type: type,
issue_tag_ids: "标", issue_tag_ids: "标",
fixed_version_ids: "里程碑", fixed_version_ids: "里程碑",
assigned_to_ids: "审查人员", assigned_to_ids: "审查人员",
paix: "排序", paix: "排序",
@ -321,7 +321,7 @@ class merge extends Component {
className="topWrapperSelect" className="topWrapperSelect"
overlay={this.renderMenu( overlay={this.renderMenu(
issue_chosen && issue_chosen.issue_tag, issue_chosen && issue_chosen.issue_tag,
"标", "标",
"issue_tag_id" "issue_tag_id"
)} )}
trigger={["click"]} trigger={["click"]}

View File

@ -300,7 +300,8 @@ class MergeForm extends Component {
{getFieldDecorator("assigned_to_id", { {getFieldDecorator("assigned_to_id", {
initialValue: assigned_to_id, initialValue: assigned_to_id,
})( })(
<Select placeholder="审查人员" showSearch> <Select placeholder="未选择审查人员" showSearch>
<Option key={0} value={""}>未选择审查人员</Option>
{this.renderSelect(members)} {this.renderSelect(members)}
</Select> </Select>
)} )}
@ -311,12 +312,11 @@ class MergeForm extends Component {
})( })(
<Select <Select
placeholder={ placeholder={
issue_versions && issue_versions.length > 0 issue_versions && issue_versions.length > 0? "未选择里程碑": "请添加里程碑"
? "未选择里程碑"
: "请添加里程碑"
} }
showSearch showSearch
> >
<Option key={0} value={""}>{issue_versions && issue_versions.length > 0? "未选择里程碑": "请添加里程碑"}</Option>
{this.renderSelect(issue_versions)} {this.renderSelect(issue_versions)}
</Select> </Select>
)} )}
@ -327,12 +327,11 @@ class MergeForm extends Component {
})( })(
<Select <Select
placeholder={ placeholder={
issue_tags && issue_tags.length > 0 issue_tags && issue_tags.length > 0 ? "未选择标记" : "请在仓库设置里添加标记"
? "未选择标签"
: "请在仓库设置里添加标签"
} }
showSearch showSearch
> >
<Option key={0} value={""}>{issue_tags && issue_tags.length > 0 ? "未选择标记" : "请在仓库设置里添加标记"}</Option>
{this.renderSelect(issue_tags)} {this.renderSelect(issue_tags)}
</Select> </Select>
)} )}

View File

@ -219,7 +219,7 @@ class Index extends Component {
user_id:owners_id user_id:owners_id
}).then((result) => { }).then((result) => {
if (result && result.data.id) { if (result && result.data.id) {
projectsType && projectsType !== "mirror" && this.props.showNotification(`托管项目创建成功!`); projectsType && projectsType !== "mirror" && this.props.showNotification(`项目创建成功!`);
this.props.history.push(`/${result.data.login}/${result.data.identifier}`); this.props.history.push(`/${result.data.login}/${result.data.identifier}`);
} }
this.setState({ this.setState({
@ -351,7 +351,7 @@ class Index extends Component {
required: true, message: '请填写镜像版本库地址' required: true, message: '请填写镜像版本库地址'
}], }],
})( })(
<Input placeholder="输入需要同步到本项目的镜像版本库地址" onChange={this.ChangeAddr} /> <Input placeholder="请输入需要导入到本项目的仓库地址" onChange={this.ChangeAddr} />
)} )}
</Form.Item> </Form.Item>
<p className="formTip color-orange">示例https://github.com/facebook/reack.git</p> <p className="formTip color-orange">示例https://github.com/facebook/reack.git</p>
@ -386,7 +386,7 @@ class Index extends Component {
{getFieldDecorator('password', { {getFieldDecorator('password', {
rules: [], rules: [],
})( })(
<Input.Password placeholder="请输入对应平台的登录用户名" autocomplete='new-password' style={{width:"240px"}}/> <Input.Password placeholder="请输入对应平台的登录密码" autocomplete='new-password' style={{width:"240px"}}/>
)} )}
</Form.Item> </Form.Item>
</div> </div>

View File

@ -1,7 +1,7 @@
import React , { Component } from 'react'; import React , { Component } from 'react';
import nodata from './Images/nodata.png'; import nodata from './Images/nodata.png';
import './css/index.scss';
class Nodata extends Component{ class Nodata extends Component{
render(){ render(){
const { _html , small } = this.props; const { _html , small } = this.props;

View File

@ -10,10 +10,6 @@ const Apply = Loadable({
loader: () => import("./Apply"), loader: () => import("./Apply"),
loading: Loading, loading: Loading,
}); });
const Notify = Loadable({
loader: () => import("./Notify"),
loading: Loading,
});
const UndoEvent = Loadable({ const UndoEvent = Loadable({
loader: () => import("./UndoEvent"), loader: () => import("./UndoEvent"),
loading: Loading, loading: Loading,
@ -23,7 +19,7 @@ function Index(props){
const pathname = props.history.location.pathname; const pathname = props.history.location.pathname;
const user = props.user; const user = props.user;
const [ menu , setMenu ] = useState("notify"); const [ menu , setMenu ] = useState("undo");
const [ messagesCount , setMessagesCount ] = useState(0); const [ messagesCount , setMessagesCount ] = useState(0);
const [ transferCount , setTransferCount ] = useState(0); const [ transferCount , setTransferCount ] = useState(0);
const [ applyCount , setApplyCount ] = useState(0); const [ applyCount , setApplyCount ] = useState(0);
@ -48,10 +44,6 @@ function Index(props){
useEffect(()=>{ useEffect(()=>{
if(pathname && username){ if(pathname && username){
if(pathname === `/${username}/notice`){ if(pathname === `/${username}/notice`){
setMenu("notify");
changeNum(user.undo_messages);
}
if(pathname === `/${username}/notice/undo`){
setMenu("undo"); setMenu("undo");
} }
if(pathname === `/${username}/notice/apply`){ if(pathname === `/${username}/notice/apply`){
@ -83,14 +75,8 @@ function Index(props){
return ( return (
<div> <div>
<ul className="noticeMenu"> <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":""}> <li className={menu === "undo" ? "active":""}>
<Link to={`/${username}/notice/undo`}> <Link to={`/${username}/notice`}>
<span>接收仓库</span> <span>接收仓库</span>
{transferCount ? <span className="unNum">{transferCount}</span>:""} {transferCount ? <span className="unNum">{transferCount}</span>:""}
</Link> </Link>
@ -109,16 +95,10 @@ function Index(props){
return <Apply {...props} {...p} deleteEvent={deleteEvent}/>; return <Apply {...props} {...p} deleteEvent={deleteEvent}/>;
}} }}
></Route> ></Route>
<Route
path="/:username/notice/undo"
render={(p) => {
return <UndoEvent {...props} {...p} deleteEvent={deleteEvent}/>;
}}
></Route>
<Route <Route
path="/:username/notice" path="/:username/notice"
render={(p) => { render={(p) => {
return <Notify {...props} {...p} deleteEvent={deleteEvent}/>; return <UndoEvent {...props} {...p} deleteEvent={deleteEvent}/>;
}} }}
></Route> ></Route>
</Switch> </Switch>

View File

@ -1,11 +1,13 @@
import React, { Component } from "react"; import React from "react";
import OrderItem from './order_form' import OrderItem from './order_form'
class CopyDetail extends Component {
render() {
function CopyDetail(props){
const operate = props.match.params.operate;
console.log(operate);
return( return(
<OrderItem form_type="copy" {...this.props}></OrderItem> <OrderItem form_type="copy" {...props}></OrderItem>
) )
} }
}
export default CopyDetail; export default CopyDetail;

View File

@ -315,7 +315,7 @@ class Detail extends Component {
</span> </span>
</p> </p>
<p className="grid-item-left pb15"> <p className="grid-item-left pb15">
<span className="issue_detail_info">:</span> <span className="issue_detail_info">:</span>
<span> <span>
{data && data.issue_tags ? ( {data && data.issue_tags ? (
<span className="grid-item"> <span className="grid-item">

View File

@ -40,7 +40,7 @@ class MilepostDetail extends Component {
issue_type: undefined, issue_type: undefined,
status_type: '1', status_type: '1',
//设置选择高亮 //设置选择高亮
issue_tag_ids: '标', issue_tag_ids: '标',
tracker_ids: '类型', tracker_ids: '类型',
author_ids: '发布人', author_ids: '发布人',
assigned_to_ids: '负责人', assigned_to_ids: '负责人',
@ -194,7 +194,7 @@ class MilepostDetail extends Component {
done_ratio : undefined, done_ratio : undefined,
status_id: undefined, status_id: undefined,
assigned_to_id: undefined, assigned_to_id: undefined,
issue_tag_ids: '标', issue_tag_ids: '标',
tracker_ids: '类型', tracker_ids: '类型',
author_ids: '发布人', author_ids: '发布人',
assigned_to_ids: '负责人', assigned_to_ids: '负责人',
@ -254,7 +254,7 @@ class MilepostDetail extends Component {
</ul> </ul>
<ul className="topWrapper_select"> <ul className="topWrapper_select">
<li> <li>
<Dropdown className="topWrapperSelect" overlay={this.renderMenu(issue_chosen && issue_chosen.issue_tag, '标', 'issue_tag_id')} trigger={['click']} placement="bottomCenter"> <Dropdown className="topWrapperSelect" overlay={this.renderMenu(issue_chosen && issue_chosen.issue_tag, '标', 'issue_tag_id')} trigger={['click']} placement="bottomCenter">
<span>{this.state.issue_tag_ids}<Icon type="caret-down" className="ml5" /></span> <span>{this.state.issue_tag_ids}<Icon type="caret-down" className="ml5" /></span>
</Dropdown> </Dropdown>
</li> </li>

View File

@ -7,7 +7,7 @@ class Nav extends Component{
const { projectsId , owner } = this.props.match.params; const { projectsId , owner } = this.props.match.params;
return( return(
<p className="topWrapper_nav"> <p className="topWrapper_nav">
<NavLink activeClassName="active" className="issue-type-button" to={`/${owner}/${projectsId}/issues/tags`}></NavLink> <NavLink activeClassName="active" className="issue-type-button" to={`/${owner}/${projectsId}/issues/tags`}></NavLink>
<NavLink activeClassName="active" className="issue-type-button" to={`/${owner}/${projectsId}/milestones`}>里程碑</NavLink> <NavLink activeClassName="active" className="issue-type-button" to={`/${owner}/${projectsId}/milestones`}>里程碑</NavLink>
</p> </p>
) )

View File

@ -324,11 +324,11 @@ class Tags extends Component {
data && data.issue_tags && data.issue_tags.length > 0 ? data && data.issue_tags && data.issue_tags.length > 0 ?
<div> <div>
<div className="topWrapper"> <div className="topWrapper">
<span>{data && data.issue_tags_count}个标</span> <span>{data && data.issue_tags_count}个标</span>
<ul className="topWrapper_select"> <ul className="topWrapper_select">
<li> <li>
<Dropdown className="topWrapperSelect" overlay={this.menu()} trigger={['click']} placement="bottomCenter"> <Dropdown className="topWrapperSelect" overlay={this.menu()} trigger={['click']} placement="bottomCenter">
<span><Icon type="caret-down" className="ml5" /></span> <span><Icon type="caret-down" className="ml5" /></span>
</Dropdown> </Dropdown>
</li> </li>
</ul> </ul>

View File

@ -1,11 +1,11 @@
import React, { Component } from "react"; import React from "react";
import OrderForm from './order_form' import OrderForm from './order_form'
class UpdateDetail extends Component {
render() { function CopyDetail(props){
const operateName = props.match.params.operateName;
return( return(
<OrderForm form_type="edit" {...this.props}></OrderForm> <OrderForm form_type={operateName === "copyetail" ? "copy":"edit"} {...props}></OrderForm>
) )
} }
} export default CopyDetail;
export default UpdateDetail;

View File

@ -39,7 +39,7 @@ class order extends Component {
search_count: undefined, search_count: undefined,
issue_type: undefined, issue_type: undefined,
status_type: "1", // 默认显示开启中的 status_type: "1", // 默认显示开启中的
issue_tag_ids: "标", issue_tag_ids: "标",
tracker_ids: "类型", tracker_ids: "类型",
author_ids: "发布人", author_ids: "发布人",
assigned_to_ids: "负责人", assigned_to_ids: "负责人",
@ -58,7 +58,7 @@ class order extends Component {
select_params: { select_params: {
assigned_to_id: undefined, // 负责人 assigned_to_id: undefined, // 负责人
author_id: undefined, // 发布人 author_id: undefined, // 发布人
issue_tag_id: undefined, // 标 issue_tag_id: undefined, // 标
tracker_id: undefined, //类型 tracker_id: undefined, //类型
done_ratio: undefined, // 完成度 done_ratio: undefined, // 完成度
status_id: undefined, // 优先级 status_id: undefined, // 优先级
@ -257,7 +257,7 @@ class order extends Component {
author_id: undefined, author_id: undefined,
assigned_to_id: undefined, assigned_to_id: undefined,
status_type: type, status_type: type,
issue_tag_ids: "标", issue_tag_ids: "标",
tracker_ids: "类型", tracker_ids: "类型",
author_ids: "发布人", author_ids: "发布人",
assigned_to_ids: "负责人", assigned_to_ids: "负责人",
@ -677,7 +677,7 @@ class order extends Component {
className="topWrapperSelect" className="topWrapperSelect"
overlay={this.renderMenu( overlay={this.renderMenu(
issue_chosen && issue_chosen.issue_tag, issue_chosen && issue_chosen.issue_tag,
"标", "标",
"issue_tag_id" "issue_tag_id"
)} )}
trigger={["click"]} trigger={["click"]}

View File

@ -153,7 +153,7 @@ class order_form extends Component {
values.issue_tag_ids = [values.issue_tag_ids]; values.issue_tag_ids = [values.issue_tag_ids];
} }
const { description, start_date, due_date, issue_type } = this.state; const { description, start_date, due_date, issue_type } = this.state;
if (form_type === "new") { if (form_type !== "edit") {
const url = `/${owner}/${projectsId}/issues.json`; const url = `/${owner}/${projectsId}/issues.json`;
axios.post(url, { axios.post(url, {
...values, ...values,
@ -442,13 +442,13 @@ class order_form extends Component {
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
<Form.Item label="标"> <Form.Item label="标">
{getFieldDecorator("issue_tag_ids", {rules: []})( {getFieldDecorator("issue_tag_ids", {rules: []})(
<Select> <Select>
<Option value={""}> <Option value={""}>
{issue_chosen && issue_chosen.issue_tag.length > 0 {issue_chosen && issue_chosen.issue_tag.length > 0
? "未选择标" ? "未选择标"
: "请在仓库设置里添加标"} : "请在仓库设置里添加标"}
</Option> </Option>
{this.renderSelect(issue_chosen && issue_chosen.issue_tag)} {this.renderSelect(issue_chosen && issue_chosen.issue_tag)}
</Select> </Select>

View File

@ -14,6 +14,16 @@ import { Link } from 'react-router-dom';
import './Index.scss'; 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({ const SSHNew = Loadable({
loader: () => import("./sub/New"), loader: () => import("./sub/New"),
loading: Loading, loading: Loading,
@ -26,6 +36,12 @@ const SSHIndex = Loadable({
loader: () => import("./sub/SSH"), loader: () => import("./sub/SSH"),
loading: Loading, loading: Loading,
}); });
const PrivateLetter = Loadable({
loader: () => import("./notice/privateLetter/Index"),
loading: Loading,
});
function Index(props){ function Index(props){
const { current_user } = props; const { current_user } = props;
const { pathname } = props.location; const { pathname } = props.location;
@ -39,18 +55,35 @@ function Index(props){
<img src={getImageUrl(`/${current_user && current_user.image_url}`)} alt=""/> <img src={getImageUrl(`/${current_user && current_user.image_url}`)} alt=""/>
<span>{current_user && current_user.username}</span> <span>{current_user && current_user.username}</span>
</div> </div>
<ul className="securityUl"> <ul className="securityUl ul-border-buttom">
<li>个人信息</li> <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>
<ul className="securityUl"> <ul className="securityUl">
<li>安全设置</li> <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> </ul>
</div> </div>
<LongWidth> <LongWidth>
<Gap> <Gap>
<Switch> <Switch>
<Route
path="/settings/notice"
render={(p) => (
<MyNoticeIndex {...props} {...p}/>
)}
></Route>
<Route
path="/settings/notice/config"
render={(p) => (
<NoticeManager {...props} {...p}/>
)}
></Route>
<Route <Route
path="/settings/SSH/new" path="/settings/SSH/new"
render={(p) => ( render={(p) => (
@ -69,12 +102,17 @@ function Index(props){
<SSHIndex {...props} {...p}/> <SSHIndex {...props} {...p}/>
)} )}
></Route> ></Route>
<Route
path="/settings/notice/privateLetter"
render={(p)=>(
<PrivateLetter{...props} {...p}/>
)}
></Route>
</Switch> </Switch>
</Gap> </Gap>
</LongWidth> </LongWidth>
</Box> </Box>
</div> </div>
</div> </div>
) )
} }

View File

@ -8,42 +8,44 @@
width: 198px; width: 198px;
border: 1px solid rgba(153, 153, 153, 0.22); border: 1px solid rgba(153, 153, 153, 0.22);
border-radius: 4px; border-radius: 4px;
min-height: 400px; // min-height: 400px;
margin-bottom: 30px; margin-bottom: 30px;
.userDetail{ .userDetail{
background: rgba(153, 153, 153, 0.05); background: rgba(153, 153, 153, 0.05);
border-radius: 4px 4px 0px 0px; border-radius: 4px 4px 0px 0px;
padding:20px 25px; padding:20px 25px;
display: flex; text-align: center;
align-items: center; height: 105px;
justify-content: flex-start;
img{ img{
height: 48px; height: 48px;
width: 48px; width: 48px;
border-radius: 50%; border-radius: 50%;
margin-right: 12px;
} }
span{ span{
font-size: 16px; font-size: 16px;
color: #333; color: #333;
max-width: 90px;
display: block; display: block;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
font-weight: 500;
} }
} }
.securityUl{ .securityUl{
padding:20px 16px; padding-left: 17px;
color: #333; color: #333;
margin-bottom: 0px; margin-bottom: 0px;
padding-bottom: 0px; padding-bottom: 12px;
padding-top: 5px;
li{ li{
font-size: 14px;
margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
height: 27px; height: 27px;
line-height: 27px; line-height: 27px;
position: relative; position: relative;
cursor: pointer; cursor: default;
a{ a{
color: #666; color: #666;
&:hover{ &:hover{
@ -52,32 +54,57 @@
} }
&.active a{ &.active a{
color: #333; color: #333;
.text-shodow-bold{
font-weight: 400;
text-shadow: 0.5px 0 #333;
} }
&:first-child{
font-size: 16px;
} }
&.active::before{ &.active::before{
position: absolute; position: absolute;
left: -16px; left: -18px;
top:0px; top:6px;
height: 100%; height: 15px;
width: 2px; width: 3px;
border-radius: 2px;
content: ""; 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{ .sshHead{
display: flex; display: flex;
align-items: center; align-items: center;
padding:15px 20px; padding:6px 0px;
margin-bottom: 15px;
justify-content: space-between; justify-content: space-between;
border-bottom: 1px solid #EEEEEE; border-bottom: 1px solid #EEEEEE;
&>span{ &>.text-shadow07{
font-size: 18px; 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{ .ant-list-item{
padding:20px; padding:20px;
border-bottom: 1px solid #eee!important; border-bottom: 1px solid #eee!important;
@ -109,7 +136,7 @@
.questionLink{ .questionLink{
padding:15px 20px; padding:15px 20px;
a{ a{
color: #4B7AFF; color: #466AFF;
&:hover{ &:hover{
text-decoration: underline; text-decoration: underline;
} }
@ -148,3 +175,22 @@
} }
} }
} }
.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

View File

@ -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,
});
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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>
&nbsp;&nbsp;&nbsp;已选择&nbsp;<span className="numberSpan">{selectedNum}</span>&nbsp;
</div>
<div>
<button onClick={cancelBatchDelete}>取消</button>&nbsp;&nbsp;&nbsp;&nbsp;
<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>}&nbsp;&nbsp;&nbsp;
{!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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -34,7 +34,7 @@ function New({ form , showNotification , history }) {
return( return(
<div> <div>
<div className="sshHead"> <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> </div>
<Form className="sshForm"> <Form className="sshForm">
<Form.Item label="标题" validateStatus={msg && msg.status === 10001 ? "error":undefined} help={msg && msg.status === 10001 ? msg.message:undefined}> <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 }}/> <TextArea placeholder="支持以'ssh-rsa','ssh-dss','ssh-ed25519','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521'开头" autoSize={{ minRows: 6, maxRows: 6 }}/>
)} )}
</Form.Item> </Form.Item>
<Button type="primary" style={{width:"100px"}} onClick={submit}>确定</Button> <button style={{width:"100px"}} onClick={submit} className="but25">确定</button>
</Form> </Form>
</div> </div>
) )

View File

@ -55,8 +55,8 @@ function SSH(props) {
<DeleteBox visible={visible} onCancel={()=>setVisible(false)} onSuccess={onSuccess}/> <DeleteBox visible={visible} onCancel={()=>setVisible(false)} onSuccess={onSuccess}/>
<SSHDetail visible={visibleDesc} onCancel={()=>setVisibleDesc(false)} desc={content}/> <SSHDetail visible={visibleDesc} onCancel={()=>setVisibleDesc(false)} desc={content}/>
<div className="sshHead"> <div className="sshHead">
<span>SSH密钥</span> <span className="text-shadow07">SSH密钥</span>
<Button type="primary" size="large" onClick={()=>props.history.push('/settings/SSH/new')}>添加SSH密钥</Button> <button type="primary" size="large" onClick={()=>props.history.push('/settings/SSH/new')} className="but25">添加SSH密钥</button>
</div> </div>
{ {
list && list.length > 0 && list && list.length > 0 &&
@ -64,7 +64,7 @@ function SSH(props) {
{ {
list.map((i,k)=>{ list.map((i,k)=>{
return( return(
<List.Item> <List.Item key={i.id}>
<img src={miyao} alt=""/> <img src={miyao} alt=""/>
<div> <div>
<p className="color-grey-3"><a className="task-hide" style={{display:"block",fontWeight:"500"}} onClick={()=>{setContent(i);setVisibleDesc(true)}}>{i.name}</a></p> <p className="color-grey-3"><a className="task-hide" style={{display:"block",fontWeight:"500"}} onClick={()=>{setContent(i);setVisibleDesc(true)}}>{i.name}</a></p>

View File

@ -102,8 +102,8 @@ class Index extends Component {
> >
<p> <p>
<Link to={`/${owner}/${projectsId}/settings/labels`} className="w-100"> <Link to={`/${owner}/${projectsId}/settings/labels`} className="w-100">
<i className="iconfont icon-biaoqian3 font-18 mr10 color-grey-6"></i> <i className="iconfont icon-xiangmubiaoqian font-18 mr10 color-grey-6"></i>
项目标 项目标
</Link> </Link>
</p> </p>
</li> </li>

View File

@ -295,23 +295,23 @@ class Setting extends Component {
</Form.Item> </Form.Item>
<Form.Item label="项目类别"> <Form.Item label="项目类别">
{getFieldDecorator("project_category_id", { {getFieldDecorator("project_category_id", {
rules: [ rules: [],
{ })(
required: true, <Select>
message: "请选择大类别", <Option key={0} value={""}>未选择项目类别</Option>
}, {CategoryList}
], </Select>
})(<Select>{CategoryList}</Select>)} )}
</Form.Item> </Form.Item>
<Form.Item label="项目语言"> <Form.Item label="项目语言">
{getFieldDecorator("project_language_id", { {getFieldDecorator("project_language_id", {
rules: [ rules: [],
{ })(
required: true, <Select>
message: "请选择项目语言", <Option key={0} value={""}>未选择项目语言</Option>
}, {LanguageList}
], </Select>
})(<Select>{LanguageList}</Select>)} )}
</Form.Item> </Form.Item>
<Form.Item label="项目导航"> <Form.Item label="项目导航">
{getFieldDecorator("project_units", { {getFieldDecorator("project_units", {

View File

@ -258,11 +258,11 @@ function New({ form , match , showNotification , history }) {
</span> */} </span> */}
<span> <span>
<Checkbox value="create">创建</Checkbox> <Checkbox value="create">创建</Checkbox>
<span>创建分支或标</span> <span>创建分支或标</span>
</span> </span>
<span> <span>
<Checkbox value="delete">删除</Checkbox> <Checkbox value="delete">删除</Checkbox>
<span>删除分支或标</span> <span>删除分支或标</span>
</span> </span>
</div> </div>
<p className="color-grey-3 mt10 mb10">合并请求事件</p> <p className="color-grey-3 mt10 mb10">合并请求事件</p>

View File

@ -348,7 +348,7 @@ class NewTags extends Component {
</a> </a>
<Popconfirm <Popconfirm
placement="bottom" placement="bottom"
title={"删除标签会将其从所有引用中删除。继续?"} title={"删除标记会将其从所有引用中删除,是否继续?"}
okText="是" okText="是"
cancelText="否" cancelText="否"
onConfirm={() => this.deletetag(item.id)} onConfirm={() => this.deletetag(item.id)}
@ -383,7 +383,7 @@ class NewTags extends Component {
return ( return (
<div> <div>
<div className="topWrapper" style={{borderBottom:"1px solid #eee"}}> <div className="topWrapper" style={{borderBottom:"1px solid #eee"}}>
<span>{data && data.issue_tags_count}个标</span> <span>{data && data.issue_tags_count}个标</span>
<ul className="topWrapper_select"> <ul className="topWrapper_select">
<li> <li>
<Dropdown <Dropdown
@ -393,7 +393,7 @@ class NewTags extends Component {
placement="bottomCenter" placement="bottomCenter"
> >
<span> <span>
标签 标记
<Icon type="caret-down" className="ml5" /> <Icon type="caret-down" className="ml5" />
</span> </span>
</Dropdown> </Dropdown>
@ -423,11 +423,11 @@ class NewTags extends Component {
className="flex-a-center baseForm bbr" className="flex-a-center baseForm bbr"
style={{ "justify-content": "space-between" }} style={{ "justify-content": "space-between" }}
> >
<span className="font-18 text-black">项目标</span> <span className="font-18 text-black">项目标</span>
{data && data.user_admin_or_member ? ( {data && data.user_admin_or_member ? (
<Button type="primary" ghost onClick={this.newshow}> <Button type="primary" ghost onClick={this.newshow}>
<Icon type="plus"></Icon> <Icon type="plus"></Icon>
创建标 创建标
</Button> </Button>
) : ( ) : (
"" ""
@ -442,7 +442,7 @@ class NewTags extends Component {
rules: [ rules: [
{ {
required: true, required: true,
message: "请填写标名字", message: "请填写标名字",
}, },
], ],
})(<Input placeholder="名称10字以内" maxLength="10" />)} })(<Input placeholder="名称10字以内" maxLength="10" />)}
@ -481,7 +481,7 @@ class NewTags extends Component {
onClick={this.createtagpost} onClick={this.createtagpost}
className="fr" className="fr"
> >
创建标 创建标
</Button> </Button>
{/* <a onClick={this.createtagpost} className="topWrapper_btn fr" >创建标签</a> */} {/* <a onClick={this.createtagpost} className="topWrapper_btn fr" >创建标签</a> */}
<a onClick={this.newclose} className="a_btn cancel_btn fr"> <a onClick={this.newclose} className="a_btn cancel_btn fr">
@ -497,7 +497,7 @@ class NewTags extends Component {
</div> </div>
</div> </div>
<Modal <Modal
title="编辑标" title="编辑标"
onCancel={this.handleCancel} onCancel={this.handleCancel}
visible={this.state.isShow} visible={this.state.isShow}
onOk={this.handleok} onOk={this.handleok}

View File

@ -21,7 +21,7 @@ function AddTag({form , visible , onCancel ,onOk}){
}; };
return( return(
<Modal <Modal
title={"新增标"} title={"新增标"}
closable={false} closable={false}
visible={visible} visible={visible}
onCancel={onCancel} onCancel={onCancel}
@ -32,11 +32,11 @@ function AddTag({form , visible , onCancel ,onOk}){
centered centered
> >
<Form {...layout}> <Form {...layout}>
<Form.Item label="标名"> <Form.Item label="标名">
{getFieldDecorator("tagName",{ {getFieldDecorator("tagName",{
rules:[{required:true,message:"请输入标名"}] rules:[{required:true,message:"请输入标名"}]
})( })(
<Input placeholder="请输入标名" width="200px" autoComplete="off" /> <Input placeholder="请输入标名" width="200px" autoComplete="off" />
)} )}
</Form.Item> </Form.Item>
</Form> </Form>

View File

@ -141,7 +141,7 @@ function Index(props){
params:{id,tagName:tag} params:{id,tagName:tag}
}).then(result=>{ }).then(result=>{
if(result && result.data){ if(result && result.data){
props.showNotification("标删除成功"); props.showNotification("标删除成功");
setIsSpin(true); setIsSpin(true);
getData(); getData();
} }

View File

@ -48,7 +48,7 @@ export default ((props)=>{
<div className="teamDetail" style={{paddingTop:"0px"}}> <div className="teamDetail" style={{paddingTop:"0px"}}>
<div> <div>
<i className="iconfont icon-zuobiao mr5"></i> <i className="iconfont icon-zuobiao mr5"></i>
<Link to={`/${OIdentifier}`}>{OIdentifier}</Link> <Link to={`/${OIdentifier}`}>{detail && detail.organization && detail.organization.nickname}</Link>
<i className="iconfont icon-youjiantou ml3 mr3 font-12 color-grey-9"></i> <i className="iconfont icon-youjiantou ml3 mr3 font-12 color-grey-9"></i>
<span className="color-grey-9">{detail ? detail.name : "新建团队"}</span> <span className="color-grey-9">{detail ? detail.name : "新建团队"}</span>
</div> </div>

View File

@ -198,7 +198,7 @@ class comments extends Component {
</span> </span>
<span> <span>
{item.value && item.value.length > 0 ? ( {item.value && item.value.length > 0 ? (
item.detail === "标签" ? ( item.detail === "标记"? (
<span <span
className="issue-tag-show" className="issue-tag-show"
style={{ background: item.value[0].color }} style={{ background: item.value[0].color }}

View File

@ -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", // 我管理的仓库有新的里程碑
};

105
src/forge/javaFetch.js Normal file
View File

@ -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};
}

View File

@ -125,7 +125,7 @@ export default Form.create()(
</Form.Item> </Form.Item>
<AlignCenter> <AlignCenter>
<span className="ant-form-item-label"></span> <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> */} {/* <Button type={"default"} onClick={()=>props.history.push(`/${current_user && current_user.login}`)} className="ml20">取消</Button> */}
</AlignCenter> </AlignCenter>
</Form> </Form>

View File

@ -1,9 +1,10 @@
import React , { useEffect , useState } from 'react'; import React , { useEffect , useState } from 'react';
import './Index.scss';
import { Menu } from 'antd'; import { Menu } from 'antd';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Base from './Base'; import Base from './Base';
import Password from './Password'; import Password from './Password';
import './Index.scss';
import '../../SecuritySetting/notice/manager/Index.scss';
function Index(props){ function Index(props){
// const { username } = props && props.match && props.match.params; // const { username } = props && props.match && props.match.params;
@ -25,11 +26,14 @@ function Index(props){
return( return(
<div> <div className="notice01">
<div className="sshHead">
<Menu selectedKeys={[key]} mode={'horizontal'} className="infosRightMenu"> <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="0"><Link to={`/settings/profile`}>基本资料</Link></Menu.Item> */}
{/* <Menu.Item key="1"><Link to={`/${username}/password`}>密码管理</Link></Menu.Item> */} {/* <Menu.Item key="1"><Link to={`/${username}/password`}>密码管理</Link></Menu.Item> */}
</Menu> </Menu>
</div>
<div style={{padding:"20px"}}> <div style={{padding:"20px"}}>
{ {
key === "0" ? key === "0" ?

View File

@ -19,6 +19,12 @@
.ant-menu-item{ .ant-menu-item{
a{ a{
font-size: 16px; font-size: 16px;
color: #333333;
position: relative;
bottom: -10px;
}
a:hover{
color: #333333;
} }
} }
} }

View File

@ -89,7 +89,7 @@ export default Form.create()(
</Form.Item> </Form.Item>
<AlignCenter style={{marginTop:"20px"}}> <AlignCenter style={{marginTop:"20px"}}>
<span className="ant-form-item-label"></span> <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> <Button type={"default"} onClick={()=>props.history.push(`/${username}`)} className="ml20">取消</Button>
</AlignCenter> </AlignCenter>
</Form> </Form>