diff --git a/dashboard/config/config.js b/dashboard/config/config.js index 9f0c725aa..ecf767d93 100644 --- a/dashboard/config/config.js +++ b/dashboard/config/config.js @@ -1,9 +1,9 @@ // https://umijs.org/config/ -import {defineConfig} from 'umi'; +import { defineConfig } from 'umi'; import defaultSettings from './defaultSettings'; import proxy from './proxy'; -const {REACT_APP_ENV} = process.env; +const { REACT_APP_ENV } = process.env; export default defineConfig({ hash: true, antd: {}, @@ -131,8 +131,8 @@ export default defineConfig({ icon: 'table', path: '/System/Env', component: './System/Env', - } - ] + }, + ], }, { name: 'Capability.Detail', diff --git a/dashboard/src/components/GlobalHeader/WorkSpaceDropDown.css b/dashboard/src/components/GlobalHeader/WorkSpaceDropDown.css new file mode 100644 index 000000000..fa0c32508 --- /dev/null +++ b/dashboard/src/components/GlobalHeader/WorkSpaceDropDown.css @@ -0,0 +1,96 @@ +.ant-pro-global-header-layout-side { + padding-right: 0; +} +.ant-dropdown-menu-item, +.ant-dropdown-menu-submenu-title { + clear: both; + margin: 0; + padding: 5px 20px; + color: deepskyblue; + font-weight: normal; + font-size: 14px; + line-height: 22px; + white-space: nowrap; + cursor: pointer; + transition: all 0.3s; + border-top: 1px solid #dee4e6; +} + +.ant-dropdown-menu-item:nth-child(1) { + border-top: 1px solid #fff; +} + +.ant-dropdown-menu-item-active { + background: rgb(0, 21, 41) !important; +} + +.ant-dropdown-menu-item-active .box1 { + color: white; +} + +.ant-dropdown-menu-item-active .box2 { + color: white; +} + +.box { + display: flex; + width: 100%; + flex-direction: column; +} + +.box1 { + font-size: 16px; + color: #5095d4; +} + +.box2 { + font-size: 12px; + color: #8d959b; +} + +.ant-dropdown-trigger { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + height: 100%; +} + +.drop-box { + display: flex; + flex-direction: row; + align-items: center; + justify-content: left; + height: 100%; + background: rgb(0, 21, 41); + padding: 0 20px; +} + +.btn-box { + display: flex; + flex-direction: column; + align-items: space-between; + overflow: hidden; + margin-right: 20px; + min-width: 120px; +} + +.btn-top { + display: flex; + height: 20px; + line-height: 20px; + font-size: 20px; + color: white; +} + +.btn-bottom { + display: flex; + height: 20px; + line-height: 20px; + font-size: 12px; + color: #8d959b; +} +.btn-outlined { + fontsize: 15px; + color: #fff; +} diff --git a/dashboard/src/components/GlobalHeader/WorkSpaceDropDown.jsx b/dashboard/src/components/GlobalHeader/WorkSpaceDropDown.jsx index 7f1e364ec..e8659219b 100644 --- a/dashboard/src/components/GlobalHeader/WorkSpaceDropDown.jsx +++ b/dashboard/src/components/GlobalHeader/WorkSpaceDropDown.jsx @@ -1,29 +1,32 @@ -import { Menu, Dropdown, Button, message } from 'antd'; +import { Menu, Dropdown, message } from 'antd'; import { DownOutlined } from '@ant-design/icons'; import React from 'react'; import { connect } from 'dva'; +import './WorkSpaceDropDown.css'; -@connect(() => ({})) +@connect((env) => ({ envs: env.envs })) export default class WorkSpaceDropDown extends React.Component { constructor(props) { super(props); this.state = { workSpaceName: '', envs: [], + namespace: '', }; } async componentDidMount() { const envs = await this.props.dispatch({ - type: 'envs/getEnvs', // applist对应models层的命名空间namespace + type: 'envs/getEnvs', }); if (envs) { - const { name = 'test' } = envs.find((a) => { - return a.current === '*'; + const { name, namespace } = envs.find((env) => { + return env.current === '*'; }); this.setState({ - envs, + envs: envs, workSpaceName: name, + namespace: namespace, }); this.props.dispatch({ type: 'globalData/currentEnv', @@ -48,6 +51,7 @@ export default class WorkSpaceDropDown extends React.Component { this.setState( { workSpaceName: e.key, + namespace: e.item.props.title, }, () => { // 值切换存储 @@ -59,25 +63,37 @@ export default class WorkSpaceDropDown extends React.Component { }); }, ); + await this.props.dispatch({ + type: 'envs/getEnvs', // applist对应models层的命名空间namespace + }); }; render() { + const { envs } = this.props; const menu = ( - {/* default - oam-system - linkerd - rio-system */} - {this.state.envs.map((item) => { - return {item.name}; - })} + {envs.envs && + envs.envs.map((item) => { + return ( + +
+
{item.name}
+
{item.namespace}
+
+
+ ); + })}
); return ( - +
+
+
{this.state.workSpaceName}
+
{this.state.namespace}
+
+ +
); } diff --git a/dashboard/src/components/GlobalHeader/workSpaceDropDown.jsx b/dashboard/src/components/GlobalHeader/workSpaceDropDown.jsx deleted file mode 100644 index 7f1e364ec..000000000 --- a/dashboard/src/components/GlobalHeader/workSpaceDropDown.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import { Menu, Dropdown, Button, message } from 'antd'; -import { DownOutlined } from '@ant-design/icons'; -import React from 'react'; -import { connect } from 'dva'; - -@connect(() => ({})) -export default class WorkSpaceDropDown extends React.Component { - constructor(props) { - super(props); - this.state = { - workSpaceName: '', - envs: [], - }; - } - - async componentDidMount() { - const envs = await this.props.dispatch({ - type: 'envs/getEnvs', // applist对应models层的命名空间namespace - }); - if (envs) { - const { name = 'test' } = envs.find((a) => { - return a.current === '*'; - }); - this.setState({ - envs, - workSpaceName: name, - }); - this.props.dispatch({ - type: 'globalData/currentEnv', - payload: { - currentEnv: name, - }, - }); - } - } - - handleMenuClick = async (e) => { - // 发送切换envs的接口 - const switchResult = await this.props.dispatch({ - type: 'envs/switchEnv', - payload: { - currentEnv: e.key, - }, - }); - if (switchResult) { - message.success(switchResult); - } - this.setState( - { - workSpaceName: e.key, - }, - () => { - // 值切换存储 - this.props.dispatch({ - type: 'globalData/currentEnv', - payload: { - currentEnv: e.key, - }, - }); - }, - ); - }; - - render() { - const menu = ( - - {/* default - oam-system - linkerd - rio-system */} - {this.state.envs.map((item) => { - return {item.name}; - })} - - ); - return ( - - - - ); - } -} diff --git a/dashboard/src/models/env.js b/dashboard/src/models/env.js index a75fc69d2..9753deac4 100644 --- a/dashboard/src/models/env.js +++ b/dashboard/src/models/env.js @@ -1,24 +1,49 @@ -import {getEnvs, switchEnv, initialEnvs} from '@/services/env'; +import { getEnvs, switchEnv, initialEnvs, deleteEnv } from '@/services/env'; const TestModel = { namespace: 'envs', state: { - // initailState: '8880' + envs: undefined, }, effects: { - * getEnvs({payload}, {call}) { + *getEnvs({ payload }, { call, put }) { const res = yield call(getEnvs, payload); + yield put({ + type: 'onGetEnvsSuccess', + payload: res, + }); return res; }, - * switchEnv({payload}, {call}) { + *switchEnv({ payload }, { call }) { const res = yield call(switchEnv, payload); return res; }, - * initialEnvs({payload}, {call}) { - const res = yield call(initialEnvs, payload) + *initialEnvs({ payload }, { call, put }) { + yield call(initialEnvs, payload); + const res = yield call(getEnvs, payload); + yield put({ + type: 'onGetEnvsSuccess', + payload: res, + }); return res; - } + }, + *deleteEnv({ payload }, { call, put }) { + yield call(deleteEnv, payload); + const res = yield call(getEnvs); + yield put({ + type: 'onGetEnvsSuccess', + payload: res, + }); + return res; + }, + }, + reducers: { + onGetEnvsSuccess(state, { payload }) { + return { + ...state, + envs: payload, + }; + }, }, - reducers: {}, }; export default TestModel; diff --git a/dashboard/src/pages/System/Env/index.jsx b/dashboard/src/pages/System/Env/index.jsx index 540b791d5..c146bffcd 100644 --- a/dashboard/src/pages/System/Env/index.jsx +++ b/dashboard/src/pages/System/Env/index.jsx @@ -1,30 +1,12 @@ -import React from 'react'; -import {PageContainer} from '@ant-design/pro-layout'; -import {Button, Table, Space, Modal, Form, Input} from 'antd'; -import {ExclamationCircleOutlined} from '@ant-design/icons'; -import {Link} from 'umi'; +import React, { useEffect, useState } from 'react'; +import { PageContainer } from '@ant-design/pro-layout'; +import { Button, Table, Space, Modal, Form, Input, Tooltip } from 'antd'; +import { ExclamationCircleOutlined } from '@ant-design/icons'; import './index.less'; -import {connect} from "dva"; +import { connect } from 'dva'; +import * as _ from 'lodash'; -const {confirm} = Modal; - -function showDeleteConfirm(record) { - confirm({ - title: `Are you sure delete env ${record.name}?`, - icon: , - width: 500, - okText: 'Yes', - okType: 'danger', - cancelText: 'No', - onOk() { - console.log('OK'); - } - }); -} - -function specifyNamespace(record) { - -} +const { confirm } = Modal; const layout = { labelCol: { @@ -35,141 +17,188 @@ const layout = { }, }; -const columns = [ - { - title: 'Env', - dataIndex: 'name', - key: 'name' - }, - { - title: 'Namespace', - dataIndex: 'namespace', - key: 'namespace', - }, - { - title: 'Operations', - dataIndex: 'Operations', - key: 'Operations', - render: (text, record) => { - return ( - - - - - ); - }, - }, -]; +const TableList = (props) => { + const { dispatch } = props; + const tableEnvs = _.get(props, 'getEnvs.envs', []); + const [form] = Form.useForm(); + const [visible, setVisible] = useState(false); + const [env, setEnv] = useState([]); -@connect(() => ({})) -class TableList extends React.PureComponent { - formRef = React.createRef(); + const showModal = () => { + setVisible(true); + }; - constructor(props) { - super(props); - this.state = { - visible: false, - envs: [] - }; - } + const handleOk = async () => { + const fieldsValue = await form.validateFields(); + await dispatch({ + type: 'envs/initialEnvs', + payload: { + params: fieldsValue, + }, + }); + setEnv(null); + form.resetFields(); + setVisible(false); + }; - showModal = () => { - this.setState({ - visible: true, + const handleCancel = () => { + setEnv(null); + setVisible(false); + form.resetFields(); + }; + + const deleteEnv = async (record) => { + await dispatch({ + type: 'envs/deleteEnv', + payload: { + envName: record.name, + }, }); }; - handleOk = async (values) => { - await this.formRef.current.validateFields(); - this.setState({ - visible: false, - }); - // const fieldsValue = await this.formRef.current.validateFields(); - // fieldsValue为通过验证的数据,现在可进行提交 - // console.log(fieldsValue) - }; - - handleTest = async () => { - await this.formRef.current.validateFields(); - this.setState({ - visible: false, + const showDeleteConfirm = (record) => { + confirm({ + title: `Are you sure delete env ${record.name}?`, + icon: , + width: 500, + okText: 'Yes', + okType: 'danger', + cancelText: 'No', + onOk() { + deleteEnv(record); + }, }); }; - handleCancel = () => { - this.setState({ - visible: false, + const specifyNamespace = (record) => { + form.setFieldsValue({ + name: record.name, + namespace: record.namespace, }); + setEnv(record); + setVisible(true); }; - getInitalData = async () => { - const envs = await this.props.dispatch({ + const getInitialData = async () => { + await dispatch({ type: 'envs/getEnvs', }); - this.setState({ - envs - }); - } + }; + useEffect(() => { + getInitialData(); + }, []); - componentDidMount() { - this.getInitalData(); - } - - render() { - return ( - -
+ const columns = [ + { + title: 'Env', + dataIndex: 'name', + key: 'name', + render: (text) => { + if (text.length > 20) { + return {text.substr(0, 20)}...; + } + return text; + }, + }, + { + title: 'Namespace', + dataIndex: 'namespace', + key: 'namespace', + render: (text) => { + if (text.length > 20) { + return {text.substr(0, 20)}...; + } + return text; + }, + }, + { + title: 'Current', + dataIndex: 'current', + align: 'center', + key: 'current', + render: (text) => { + return text === '*' ? 'active' : ''; + }, + }, + { + title: 'Operations', + dataIndex: 'Operations', + key: 'Operations', + render: (text, record) => { + return ( - + -
- - Test - , - , - ]} - > -
- - - - - - -
-
- record.name} - columns={columns} dataSource={this.state.envs}/> - - ); - } -} - -export default TableList; + ); + }, + }, + ]; + return ( + +
+ + + +
+ + {env && env.name ? 'Update' : 'Create'} + , + ]} + > +
+ + + + + + + +
+
record.name} columns={columns} dataSource={tableEnvs} /> + + ); +}; +export default connect((env) => { + return { + getEnvs: env.envs, + }; +})(TableList); diff --git a/dashboard/src/services/env.js b/dashboard/src/services/env.js index 7e091aced..7967b52e4 100644 --- a/dashboard/src/services/env.js +++ b/dashboard/src/services/env.js @@ -1,24 +1,34 @@ import request from '@/utils/request'; + /* * 初始化 env:Post /api/envs/ */ -export async function initialEnvs() { +export async function initialEnvs({ params }) { return request('/api/envs/', { method: 'post', + data: { + ...params, + }, + headers: { + 'Content-Type': 'application/json', + }, }); } + /* * env 列表:Get /api/envs/ */ export async function getEnvs() { return request('/api/envs/'); } + /* * 查询 env:Get /api/envs/:envName */ export async function searchEnvs({ envName }) { return request(`/api/envs/${envName}`); } + /* * 删除 env:Delete /api/envs/:envName */ @@ -27,6 +37,7 @@ export async function deleteEnv({ envName }) { method: 'delete', }); } + /* * 切换 env:Patch /api/envs/:envName */