add component AppSelector

pull/289/head
vapao 2021-03-07 18:50:57 +08:00
parent ba6c7d7c92
commit dc5028ee3a
8 changed files with 170 additions and 165 deletions

View File

@ -0,0 +1,103 @@
/**
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { observer } from 'mobx-react';
import { Modal, Button, Menu, Spin, Input, Tooltip } from 'antd';
import { OrderedListOutlined, BuildOutlined } from '@ant-design/icons';
import { includes, http } from 'libs';
import styles from './index.module.less';
import envStore from 'pages/config/environment/store';
import lds from 'lodash';
export default observer(function AppSelector(props) {
const [fetching, setFetching] = useState(false);
const [refs, setRefs] = useState({});
const [env_id, setEnvId] = useState();
const [search, setSearch] = useState();
const [deploys, setDeploys] = useState([]);
useEffect(() => {
setFetching(true);
http.get('/api/app/deploy/')
.then(res => setDeploys(res))
.finally(() => setFetching(false))
if (!envStore.records.length) {
envStore.fetchRecords().then(_initEnv)
} else {
_initEnv()
}
}, [])
function _initEnv() {
if (envStore.records.length) {
setEnvId(envStore.records[0].id)
}
}
function handleRef(el, id) {
if (el && !refs.hasOwnProperty(id)) {
setTimeout(() => {
refs[id] = el.scrollWidth > el.clientWidth;
setRefs({...refs})
}, 200)
}
}
let records = deploys.filter(x => x.env_id === Number(env_id));
if (search) records = records.filter(x => includes(x['app_name'], search));
if (props.filter) records = records.filter(x => props.filter(x));
return (
<Modal
visible={props.visible}
width={800}
maskClosable={false}
title="选择应用"
bodyStyle={{padding: 0}}
onCancel={props.onCancel}
footer={null}>
<div className={styles.appSelector}>
<div className={styles.left}>
<Spin spinning={envStore.isFetching}>
<Menu
mode="inline"
selectedKeys={[String(env_id)]}
style={{border: 'none'}}
onSelect={({selectedKeys}) => setEnvId(selectedKeys[0])}>
{envStore.records.map(item => <Menu.Item key={item.id}>{item.name}</Menu.Item>)}
</Menu>
</Spin>
</div>
<div className={styles.right}>
<Spin spinning={fetching}>
<div className={styles.title}>
<div>{lds.get(envStore.idMap, `${env_id}.name`)}</div>
<Input.Search
allowClear
style={{width: 200}}
placeholder="请输入快速搜应用"
onChange={e => setSearch(e.target.value)}/>
</div>
{records.map(item => (
<Tooltip key={item.id} title={refs[item.id] ? item['app_name'] : null}>
<Button type="primary" className={styles.appBlock} onClick={() => props.onSelect(item)}>
<div ref={el => handleRef(el, item.id)}
style={{width: 135, overflow: 'hidden', textOverflow: 'ellipsis'}}>
{item.extend === '1' ? <OrderedListOutlined/> : <BuildOutlined/>}
<span style={{marginLeft: 8}}>{item.app_name}</span>
</div>
</Button>
</Tooltip>
))}
{records.length === 0 &&
<div className={styles.tips}>该环境下还没有可发布或构建的应用哦快去<Link to="/deploy/app">应用管理</Link></div>}
</Spin>
</div>
</div>
</Modal>
)
})

View File

@ -14,6 +14,7 @@ import ACEditor from './ACEditor';
import Action from './Action';
import TableCard from './TableCard';
import Breadcrumb from './Breadcrumb';
import AppSelector from './AppSelector';
export {
StatisticsCard,
@ -27,4 +28,5 @@ export {
Action,
TableCard,
Breadcrumb,
AppSelector,
}

View File

@ -143,3 +143,44 @@
line-height: 50px;
}
}
.appSelector {
display: flex;
background-color: #fff;
padding: 16px 0;
min-height: 500px;
.left {
flex: 2;
border-right: 1px solid #e8e8e8;
}
.right {
flex: 7;
padding: 8px 40px;
}
.title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
color: rgba(0, 0, 0, .85);
font-weight: 500;
font-size: 20px;
line-height: 28px;
}
.appBlock {
margin-top: 20px;
width: 165px;
height: 60px;
font-size: 18px;
margin-right: 20px;
}
.tips {
margin-top: 32px;
color: #888;
}
}

View File

@ -36,6 +36,15 @@ export function hasHostPermission(id) {
return isSuper || hostPerms.includes(id)
}
export function includes(s, key) {
key = key.toLowerCase();
if (s) {
return s.toLowerCase().includes(key)
} else {
return false
}
}
// 清理输入的命令中包含的\r符号
export function cleanCommand(text) {
return text ? text.replace(/\r\n/g, '\n') : ''

View File

@ -1,120 +0,0 @@
/**
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import React from 'react';
import { Link } from 'react-router-dom';
import { observer } from 'mobx-react';
import { Modal, Button, Menu, Spin, Input, Tooltip } from 'antd';
import { OrderedListOutlined, BuildOutlined } from '@ant-design/icons';
import store from './store';
import styles from './index.module.css';
import envStore from 'pages/config/environment/store';
import lds from 'lodash';
@observer
class SelectApp extends React.Component {
constructor(props) {
super(props);
this.state = {
env_id: 0,
search: ''
}
}
componentDidMount() {
store.loadDeploys();
if (envStore.records.length === 0) {
envStore.fetchRecords().then(this._initEnv)
} else {
this._initEnv()
}
}
componentWillUnmount() {
store.refs = {}
}
_initEnv = () => {
if (envStore.records.length) {
this.setState({env_id: envStore.records[0].id})
}
};
handleClick = (deploy) => {
store.record = {deploy_id: deploy.id, app_host_ids: deploy.host_ids};
if (deploy.extend === '1') {
store.ext1Visible = true
} else {
store.ext2Visible = true
}
store.addVisible = false
};
handleRef = (el, id) => {
if (el && !store.refs.hasOwnProperty(id)) {
setTimeout(() => store.refs[id] = el.scrollWidth > el.clientWidth, 200)
}
};
render() {
const {env_id} = this.state;
let records = store.deploys.filter(x => x.env_id === Number(env_id));
if (this.state.search) {
records = records.filter(x => x['app_name'].toLowerCase().includes(this.state.search.toLowerCase()))
}
return (
<Modal
visible
width={800}
maskClosable={false}
title="选择应用"
bodyStyle={{padding: 0}}
onCancel={() => store.addVisible = false}
footer={null}>
<div className={styles.container}>
<div className={styles.left}>
<Spin spinning={envStore.isFetching}>
<Menu
mode="inline"
selectedKeys={[String(env_id)]}
style={{border: 'none'}}
onSelect={({selectedKeys}) => this.setState({env_id: selectedKeys[0]})}>
{envStore.records.map(item => <Menu.Item key={item.id}>{item.name}</Menu.Item>)}
</Menu>
</Spin>
</div>
<div className={styles.right}>
<Spin spinning={store.isLoading}>
<div className={styles.title}>
<div>{lds.get(envStore.idMap, `${env_id}.name`)}</div>
<Input.Search
allowClear
style={{width: 200}}
placeholder="请输入快速搜应用"
onChange={e => this.setState({search: e.target.value})}/>
</div>
{records.map(item => (
<Tooltip key={item.id} title={store.refs[item.id] ? item['app_name'] : null}>
<Button type="primary" className={styles.appBlock} onClick={() => this.handleClick(item)}>
<div ref={el => this.handleRef(el, item.id)}
style={{width: 135, overflow: 'hidden', textOverflow: 'ellipsis'}}>
{item.extend === '1' ? <OrderedListOutlined/> : <BuildOutlined/>}
<span style={{marginLeft: 8}}>{item.app_name}</span>
</div>
</Button>
</Tooltip>
))}
{records.length === 0 &&
<div className={styles.tips}>该环境下还没有可发布的应用哦快去<Link to="/deploy/app">应用管理</Link></div>}
</Spin>
</div>
</div>
</Modal>
)
}
}
export default SelectApp

View File

@ -7,8 +7,7 @@ import React from 'react';
import { observer } from 'mobx-react';
import { ExclamationCircleOutlined, DeleteOutlined } from '@ant-design/icons';
import { Form, Select, DatePicker, Modal, Input, message } from 'antd';
import { SearchForm, AuthDiv, AuthButton, Breadcrumb } from 'components';
import SelectApp from './SelectApp';
import { SearchForm, AuthDiv, AuthButton, Breadcrumb, AppSelector } from 'components';
import Ext1Form from './Ext1Form';
import Ext2Form from './Ext2Form';
import Approve from './Approve';
@ -102,7 +101,10 @@ class Index extends React.Component {
</SearchForm.Item>
</SearchForm>
<ComTable/>
{store.addVisible && <SelectApp/>}
<AppSelector
visible={store.addVisible}
onCancel={() => store.addVisible = false}
onSelect={store.confirmAdd}/>
{store.ext1Visible && <Ext1Form/>}
{store.ext2Visible && <Ext2Form/>}
{store.approveVisible && <Approve/>}

View File

@ -1,38 +0,0 @@
.container {
display: flex;
background-color: #fff;
padding: 16px 0;
min-height: 500px;
}
.left {
flex: 2;
border-right: 1px solid #e8e8e8;
}
.right {
flex: 7;
padding: 8px 40px;
}
.title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
color: rgba(0, 0, 0, .85);
font-weight: 500;
font-size: 20px;
line-height: 28px;
}
.appBlock {
margin-top: 20px;
width: 165px;
height: 60px;
font-size: 18px;
margin-right: 20px;
}
.tips {
margin-top: 32px;
color: #888;
}

View File

@ -8,12 +8,8 @@ import http from 'libs/http';
class Store {
@observable records = [];
@observable deploys = [];
@observable types = [];
@observable record = {};
@observable refs = {};
@observable counter = {};
@observable isLoading = false;
@observable isFetching = false;
@observable addVisible = false;
@observable ext1Visible = false;
@ -64,6 +60,16 @@ class Store {
}
};
confirmAdd = (deploy) => {
this.record = {deploy_id: deploy.id, app_host_ids: deploy.host_ids};
if (deploy.extend === '1') {
this.ext1Visible = true
} else {
this.ext2Visible = true
}
this.addVisible = false
};
showForm = (info) => {
this.record = info;
if (info['app_extend'] === '1') {