[ISSUE #11441] Add console-ui Dark mode (#11445)

* fix NPE

* fix NPE

* add console-ui dark mode

* merge

* merge
This commit is contained in:
zhanghong 2023-12-05 09:33:33 +08:00 committed by GitHub
parent 33cb6e26c7
commit de739e9b9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 563 additions and 42 deletions

View File

@ -75,6 +75,7 @@
"qs": "^6.8.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-icons": "^4.12.0",
"react-redux": "^7.1.3",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",

View File

@ -16,10 +16,10 @@
import React from 'react';
import { withRouter } from 'react-router-dom';
import { Icon, Message } from '@alifd/next';
import { Message } from '@alifd/next';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FaCopy } from 'react-icons/fa';
@connect(state => ({ ...state.locale }))
@withRouter
@ -47,10 +47,9 @@ copyText(locale, value) {
<div className={className} onClick={() => (showIcon ? '' : this.copyText(locale, value))} style={style}>
{textNode || value}
{showIcon && (
<Icon
<FaCopy
title={title || '复制'}
className="copy-icon"
size="small"
type="copy"
onClick={() => this.copyText(locale, value)}
/>

View File

@ -168,15 +168,11 @@ class NameSpaceList extends React.Component {
rendernamespace(namespaceList) {
const { nownamespace } = this.state; // 获得当前namespace
const namespacesBtn = namespaceList.map((obj, index) => {
const style =
obj.namespace === nownamespace
? { color: '#209BFA', paddingRight: 10, border: 'none', fontSize: 14 }
: { color: '#666', paddingRight: 10, border: 'none', fontSize: 14 };
return (
<div key={index} style={{ cursor: 'pointer' }}>
{index === 0 ? '' : <span style={{ marginRight: 8, color: '#999' }}>|</span>}
<span
style={style}
className={obj.namespace === nownamespace ? 'naming-focus' : 'naming-simple'}
onClick={this.changeNameSpace.bind(
this,
obj.namespace,

View File

@ -13,3 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.naming-focus {
color: #209BFA;
padding-right: 10px;
border: none;
font-size: 14px;
}
.naming-simple {
color: #666;
padding-right: 10px;
border: none;
font-size: 14px;
}

View File

@ -40,6 +40,7 @@ class PageTitle extends React.Component {
<span style={{ display: 'flex', alignItems: 'center', marginLeft: 16 }}>
{locale.NameSpace.namespace}
<Copy
className="naming-copy"
style={{
marginLeft: 16,
height: 32,
@ -59,7 +60,10 @@ class PageTitle extends React.Component {
render() {
const { title, namespaceId, namespaceName, desc, nameSpace, locale } = this.props;
return (
<div style={{ display: 'flex', alignItems: 'center', marginTop: 8, marginBottom: 8 }}>
<div
className="page-title"
style={{ display: 'flex', alignItems: 'center', marginTop: 8, marginBottom: 8 }}
>
<span style={{ fontSize: 28, height: 40, fontWeight: 500 }}>{title}</span>
<span style={{ marginLeft: 4 }}>
{namespaceId && namespaceId !== 'undefined'

View File

@ -45,4 +45,6 @@ export const GLOBAL_PAGE_SIZE_LIST = [10, 20, 30, 50, 100];
export const LOGINPAGE_ENABLED = 'docsite_loginpage';
export const THEME = 'setting_theme';
export const SERVER_GUIDE = 'SERVER_GUIDE';

View File

@ -29,7 +29,7 @@ import { ConfigProvider, Loading } from '@alifd/next';
import './lib';
import Layout from './layouts/MainLayout';
import { LANGUAGE_KEY, REDUX_DEVTOOLS } from './constants';
import { LANGUAGE_KEY, REDUX_DEVTOOLS, THEME } from './constants';
import Login from './pages/Login';
import Namespace from './pages/NameSpace';
@ -50,10 +50,12 @@ import UserManagement from './pages/AuthorityControl/UserManagement';
import PermissionsManagement from './pages/AuthorityControl/PermissionsManagement';
import RolesManagement from './pages/AuthorityControl/RolesManagement';
import Welcome from './pages/Welcome/Welcome';
import SettingCenter from './pages/SettingCenter';
import reducers from './reducers';
import { changeLanguage } from './reducers/locale';
import { getState } from './reducers/base';
import changeTheme from './theme';
import './index.scss';
import PropTypes from 'prop-types';
@ -94,9 +96,10 @@ const MENU = [
{ path: '/userManagement', component: UserManagement },
{ path: '/rolesManagement', component: RolesManagement },
{ path: '/permissionsManagement', component: PermissionsManagement },
{ path: '/settingCenter', component: SettingCenter },
];
@connect(state => ({ ...state.locale, ...state.base }), { changeLanguage, getState })
@connect(state => ({ ...state.locale, ...state.base }), { changeLanguage, getState, changeTheme })
class App extends React.Component {
static propTypes = {
locale: PropTypes.object,
@ -104,6 +107,7 @@ class App extends React.Component {
getState: PropTypes.func,
loginPageEnabled: PropTypes.string,
consoleUiEnable: PropTypes.string,
changeTheme: PropTypes.func,
};
constructor(props) {
@ -118,7 +122,9 @@ class App extends React.Component {
componentDidMount() {
this.props.getState();
const language = localStorage.getItem(LANGUAGE_KEY);
const theme = localStorage.getItem(THEME);
this.props.changeLanguage(language);
this.props.changeTheme(theme);
}
get router() {

View File

@ -14,6 +14,7 @@
* limitations under the License.
*/
@import "theme/index.scss";
html,
body,

View File

@ -1492,7 +1492,7 @@ h6 {
.main-container {
height: calc(100vh - 66px);
background-color: #fff !important;
background-color: #fff;
.right-panel {
background-color: #fff;

View File

@ -68,7 +68,7 @@ const authorityControlMenu = {
};
const namespaceMenu = {
key: 'namespace',
url: '/namespace'
url: '/namespace',
};
const clusterMenu = {
key: 'clusterManagementVirtual',
@ -79,7 +79,10 @@ const clusterMenu = {
},
],
};
const settingMenu = {
key: 'settingCenter',
url: '/settingCenter',
};
export default function(model) {
const { token = '{}' } = localStorage;
const { globalAdmin } = isJsonString(token) ? JSON.parse(token) || {} : {};
@ -96,5 +99,6 @@ export default function(model) {
}
result.push(namespaceMenu);
result.push(clusterMenu);
result.push(settingMenu);
return result.filter(item => item);
}

View File

@ -64,6 +64,7 @@ const I18N_CONF = {
roleManagement: 'Role Management',
privilegeManagement: 'Privilege Management',
consoleClosed: 'Console Closed',
settingCenter: 'Setting Center',
},
Password: {
passwordNotConsistent: 'The passwords are not consistent',
@ -674,6 +675,14 @@ const I18N_CONF = {
Components: {
copySuccessfully: 'Success copied!',
},
SettingCenter: {
settingTitle: 'Setting Center',
settingTheme: 'Themes',
settingLight: 'light',
settingDark: 'dark',
settingLocale: 'Language',
settingSubmit: 'Apply',
},
};
export default I18N_CONF;

View File

@ -62,6 +62,7 @@ const I18N_CONF = {
roleManagement: '角色管理',
privilegeManagement: '权限管理',
consoleClosed: '控制台已关闭',
settingCenter: '设置中心',
},
Password: {
passwordNotConsistent: '两次输入密码不一致',
@ -669,6 +670,14 @@ const I18N_CONF = {
Components: {
copySuccessfully: '复制成功',
},
SettingCenter: {
settingTitle: '设置中心',
settingTheme: '样式主题',
settingLight: '明亮',
settingDark: '深色',
settingLocale: '系统语言',
settingSubmit: '应用',
},
};
export default I18N_CONF;

View File

@ -1366,6 +1366,7 @@ class ConfigurationManagement extends React.Component {
autoWidth={false}
label={locale.exportBtn}
popupStyle={{ minWidth: 150 }}
iconSize="xs"
>
{[
{

View File

@ -15,14 +15,12 @@
*/
.service-management {
padding-top: 12px;
.page-title {
height: 30px;
width: 100%;
line-height: 30px;
margin: 0 0 20px;
padding: 0 0 0 10px;
border-left: 3px solid #09c;
color: #ccc;
}
.title-item {
font-size: 14px;

View File

@ -29,14 +29,12 @@
*/
.subscriber-list {
padding-top: 12px;
.page-title {
height: 30px;
width: 100%;
line-height: 30px;
margin: 0 0 20px;
padding: 0 0 0 10px;
border-left: 3px solid #09c;
color: #ccc;
}
.title-item {
font-size: 14px;

View File

@ -0,0 +1,110 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import './index.scss';
import { Button, ConfigProvider, Radio } from '@alifd/next';
import PageTitle from '../../components/PageTitle';
import { changeLanguage } from '@/reducers/locale';
import changeTheme from '../../theme';
import { connect } from 'react-redux';
import { LANGUAGE_KEY, THEME } from '../../constants';
const { Group: RadioGroup } = Radio;
@connect(state => ({ ...state.locale }), { changeLanguage, changeTheme })
@ConfigProvider.config
class SettingCenter extends React.Component {
static displayName = 'SettingCenter';
static propTypes = {
locale: PropTypes.object,
changeLanguage: PropTypes.func,
changeTheme: PropTypes.func,
};
constructor(props) {
super(props);
const defaultTheme = localStorage.getItem(THEME);
this.state = {
theme: defaultTheme === 'dark' ? 'dark' : 'light',
language: localStorage.getItem(LANGUAGE_KEY),
};
}
newTheme(value) {
this.setState({
theme: value,
});
}
newLanguage(value) {
this.setState({
language: value,
});
}
submit() {
const { changeLanguage, changeTheme } = this.props;
const currentLanguage = this.state.language;
const currentTheme = this.state.theme;
changeLanguage(currentLanguage);
changeTheme(currentTheme);
}
render() {
const { locale = {} } = this.props;
const themeList = [
{ value: 'light', label: locale.settingLight },
{ value: 'dark', label: locale.settingDark },
];
const languageList = [
{ value: 'en-US', label: 'English' },
{ value: 'zh-CN', label: '中文' },
];
return (
<>
<PageTitle title={locale.settingTitle} />
<div className="setting-box">
<div className="text-box">
<div className="setting-checkbox">
<div className="setting-span">{locale.settingTheme}</div>
<RadioGroup
dataSource={themeList}
value={this.state.theme}
onChange={this.newTheme.bind(this)}
/>
</div>
<div className="setting-checkbox">
<div className="setting-span">{locale.settingLocale}</div>
<RadioGroup
dataSource={languageList}
value={this.state.language}
onChange={this.newLanguage.bind(this)}
/>
</div>
</div>
<Button type="primary" onClick={this.submit.bind(this)}>
{locale.settingSubmit}
</Button>
</div>
</>
);
}
}
export default SettingCenter;

View File

@ -0,0 +1,19 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import SettingCenter from './SettingCenter';
export default SettingCenter;

View File

@ -0,0 +1,43 @@
/*!
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.setting-box {
margin-top: 15px;
padding: 20px;
background-color: #fff;
border: 1px solid #f6f7fb;
border-radius: 0;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.text-box {
height: 140px;
display: flex;
}
.setting-checkbox {
flex: 1;
height: 100px;
box-sizing: border-box;
}
.setting-span {
font-size: 18px;
font-weight: normal;
margin-bottom: 15px;
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { THEME } from '../constants';
const changeTheme = theme => dispatch => {
if (theme === 'dark') {
document.body.classList.add('dark');
} else {
document.body.classList.remove('dark');
}
localStorage.setItem(THEME, theme);
};
export default changeTheme;

View File

@ -0,0 +1,250 @@
/*!
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
$dark-container-bgc: #161616;
$dark-menu-bac: #2D2D2D;
$dark-main-bgc: #303030;
$dark-color-deep: #ffffff;
$dark-color-light-grep: #C9C9CC;
$dark-color-grep: #929299;
$dark-bg-hover: #209BFA;
$dark-bg-border: #3E3E3E;
$dark-aside-color: #424346;
$dark-aside-border: #1C1D1F;
$dark-table-header-bg: #4A4A4A;
$dark-pull-bac: #373737;
$dark-input-bak: #303030;
$dark-input-border: #414141;
$dark-input-color: #585858;
//contain
.dark {
filter: grayscale(.2);
//侧边栏背景
.next-shell-navigation.next-shell-mini.next-shell-aside, .next-menu-item:not(.next-disabled).next-focused {
background-color: $dark-menu-bac !important;
color: $dark-color-deep !important;
border-color: $dark-bg-border;
}
//菜单颜色
.next-menu-item {
color: $dark-color-deep !important;
}
//二级菜单
.next-nav-embeddable.next-primary .next-menu-sub-menu .next-menu-item, .next-nav-embeddable.next-primary .next-nav-item.next-menu-item, .next-nav-embeddable.next-secondary .next-menu-sub-menu .next-menu-item, .next-nav-embeddable.next-secondary .next-nav-item.next-menu-item, .next-nav-embeddable.next-normal .next-menu-sub-menu .next-menu-item, .next-nav-embeddable.next-normal .next-nav-item.next-menu-item {
background-color: $dark-container-bgc !important;
color: $dark-color-grep!important;
}
//一级菜单hover
.next-menu-item:not(.next-disabled):hover {
background-color: $dark-aside-color!important;
}
//菜单hover和focus
.next-nav.next-normal .next-menu-sub-menu .next-menu-item.next-focused, .next-nav.next-normal .next-menu-sub-menu .next-menu-item:hover {
background-color: $dark-aside-color !important;
color: $dark-bg-hover !important;
}
//菜单选中
.next-menu.next-normal .next-menu-item.next-selected, .next-menu-item.next-selected {
background-color: $dark-aside-color!important;
color: $dark-bg-hover !important;
}
//container背景
.next-shell-brand .next-shell-main {
background-color: $dark-container-bgc !important;
}
//container背景
.main-container .right-panel {
background-color: $dark-container-bgc !important;
}
//title颜色
.page-title {
color: $dark-color-deep;
}
.nav-title, .nav-mode {
border-color: $dark-aside-border
}
}
//namespacewrapper
.dark {
//命名空间列表
.namespacewrapper {
background-color: $dark-main-bgc !important;
.naming-simple {
color: $dark-color-light-grep;
}
}
//表格
.next-table {
border-color: $dark-bg-border !important;
table {
background-color: $dark-main-bgc !important;
}
}
.query_result_wrapper {
color: $dark-color-deep;
}
.next-table-header th {
background-color: $dark-table-header-bg !important;
color: $dark-color-deep;
}
//每行
.next-table-row {
background-color: $dark-main-bgc !important;
color: $dark-color-light-grep;
}
.next-table-sort .next-icon {
color: $dark-color-deep;
}
.next-table td, .next-table th {
border-color: $dark-bg-border!important;
}
//table tr hover
.next-table-row.hovered {
background: $dark-table-header-bg !important;
color: $dark-color-deep;
}
.next-table-cell-wrapper a {
color: $dark-bg-hover;
}
//表单
form {
label {
color: $dark-color-light-grep !important;
}
}
//命名空间复制
.naming-copy {
background: $dark-main-bgc !important;
color: $dark-color-deep !important;
}
//下拉框
.next-menu.next-ver {
background-color: $dark-menu-bac;
border-color: $dark-aside-color;
}
.next-overlay-wrapper .opened {
.next-menu-item:not(.next-disabled):hover {
background-color: white;
}
}
}
//dialog
.dark {
.next-message.next-large.next-title-content .next-message-title, .next-dialog-header {
color: $dark-color-deep;
}
.next-overlay-wrapper .next-overlay-inner {
border-color: $dark-aside-border;
background-color: $dark-container-bgc;
}
}
//input
.dark {
.next-input.next-medium, .next-input.next-input-textarea {
background-color: $dark-input-bak!important;
border-color: $dark-input-border!important;
caret-color: $dark-color-light-grep!important;
input, textarea, em {
color: $dark-color-deep !important;
}
input::placeholder {
color: $dark-color-grep;
}
}
.next-input.next-small, .next-input.next-input-textarea {
background-color: $dark-input-bak!important;
border-color: $dark-input-border!important;
caret-color: $dark-color-light-grep!important;
input, textarea, em {
color: $dark-color-deep !important;
}
input::placeholder {
color: $dark-color-grep;
}
}
}
//设置中心
.dark {
.setting-box {
background-color: $dark-main-bgc !important;
color: $dark-color-deep!important;
border-color: $dark-bg-border;
}
.next-radio-group .next-radio-label {
color: $dark-color-light-grep !important;
}
}
//服务管理
.dark {
.main-container {
background-color: $dark-container-bgc;
}
.next-shell-brand .next-shell-main {
color: $dark-color-light-grep;
h1 {
color: $dark-color-deep;
}
}
}
.dark {
.service-detail .cluster-card, .next-card-head, .next-card-body {
background-color: $dark-main-bgc;
}
.next-card-title, .next-form-item.next-small .next-form-item-label {
color: $dark-color-deep !important;
}
.next-card-subtitle {
color: $dark-color-light-grep;
}
.next-tag-group .next-tag-medium {
color: $dark-color-light-grep !important;
}
}
//权限管理
.dark {
.next-form-item p {
color: $dark-color-light-grep;
}
}
//节点管理
.dark {
.next-collapse-panel-title {
background-color: $dark-container-bgc;
color: $dark-color-light-grep;
}
.next-collapse .next-collapse-panel-icon {
color: $dark-color-light-grep;
}
.next-collapse-panel-title:hover, .next-collapse-panel-title:hover .next-collapse-panel-icon {
background-color: $dark-aside-border;
color: $dark-color-deep;
}
.next-collapse {
border-color: $dark-aside-border;
}
.next-collapse-panel-expanded > .next-collapse-panel-content {
background-color: $dark-menu-bac;
ul li pre {
background-color: $dark-main-bgc;
border-color: $dark-aside-border;
color: $dark-color-light-grep;
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,7 @@
<link rel="stylesheet" type="text/css" href="console-ui/public/css/icon.css">
<link rel="stylesheet" type="text/css" href="console-ui/public/css/font-awesome.css">
<!-- 第三方css结束 -->
<link href="./css/main.css?dee224cff7cea555a830" rel="stylesheet"></head>
<link href="./css/main.css?31bc8618f12b5f782632" rel="stylesheet"></head>
<body>
<div id="root" style="overflow:hidden"></div>
@ -56,6 +56,6 @@
<script src="console-ui/public/js/merge.js"></script>
<script src="console-ui/public/js/loader.js"></script>
<!-- 第三方js结束 -->
<script type="text/javascript" src="./js/main.js?dee224cff7cea555a830"></script></body>
<script type="text/javascript" src="./js/main.js?31bc8618f12b5f782632"></script></body>
</html>

File diff suppressed because one or more lines are too long