Commit c58f1cb8 authored by 吴永生#A02208's avatar 吴永生#A02208

Merge branch 'feat-20220530-project' of http://120.77.149.83/sunyihao/bkunyun into rele-nginx

parents dbc56504 e26e9f13
This diff is collapsed.
......@@ -5,6 +5,9 @@ const RESTAPI = {
// API_PRIVILEGE_LIST: `${BACKEND_API_URI_PREFIX}/routes/privilege/list`, //
API_PROJECT_LIST: `${BACKEND_API_URI_PREFIX}/cpp/project/list`, //获取产品下的项目列表
API_PROJECT_ADD: `${BACKEND_API_URI_PREFIX}/cpp/project/add`, //新增项目
API_PROJECT_UPDATE: `${BACKEND_API_URI_PREFIX}/cpp/project/update`, //新增项目
API_PROJECT_DELETE: `${BACKEND_API_URI_PREFIX}/cpp/project/delete`, //删除项目
API_PROJECT_GET: `${BACKEND_API_URI_PREFIX}/cpp/project/get`, //获取项目信息
API_CPCE_HPCZONE: `${BACKEND_API_URI_PREFIX}/cpp/cpce/hpczone`, //获取计算区列表
};
......
......@@ -3,6 +3,7 @@ import React, { useContext } from 'react';
import _ from 'lodash';
import localStorageKey from '@/utils/localStorageKey';
import { BACKEND_API_URI_PREFIX } from './api_prefix';
import qs from 'qs';
......@@ -115,15 +116,22 @@ export class Http {
return requestMethod.then((res) => res.data);
}
getConfig(config?: IAxiosRequestConfig) {
getConfig(config?: IAxiosRequestConfig, data?: any ) {
let Authorization: string =
`Bearer ` +
JSON.parse(localStorage.getItem(localStorageKey.TOKEN) || "{}")
?.access_token || "";
return {
headers:{'Content-Type': "application/x-www-form-urlencoded",Authorization, ...config?.headers, },...config
}
const newConfig = {
headers:{Authorization, ...config?.headers, },...config
}
if (
newConfig?.headers['Content-Type'] === "application/x-www-form-urlencoded"
) {
console.log(222)
newConfig.data = qs.stringify(data);
}
return newConfig
}
......@@ -135,18 +143,18 @@ export class Http {
}
patch<T>(url: string, data?: any, config?: IAxiosRequestConfig) {
return this.request<T>({ method: 'PATCH', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config) });
return this.request<T>({ method: 'PATCH', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config, data) });
}
put<T>(url: string, data?: any, config?: IAxiosRequestConfig) {
return this.request<T>({ method: 'PUT', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config) });
return this.request<T>({ method: 'PUT', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config, data) });
}
post<T>(url: string, data?: any, config?: IAxiosRequestConfig) {
if (config?.allowIntercept) {
return this.requestInterception<T>({ method: 'POST', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config) });
return this.requestInterception<T>({ method: 'POST', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config, data) });
}
return this.request<T>({ method: 'POST', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config) });
return this.request<T>({ method: 'POST', url: `${BACKEND_API_URI_PREFIX}${url}`, data, ...this.getConfig(config, data) });
}
del<T>(url: string, config?: IAxiosRequestConfig) {
......@@ -166,3 +174,9 @@ export function useHttp(raw?: boolean) {
}
export default rawHttp;
export interface IResponse<T> {
errorCode: number;
massage: string;
data: T;
}
......@@ -37,7 +37,6 @@ type addProjectParams = {
};
// 新增项目
const addProject = (params: addProjectParams) => {
console.log("params", params);
return request({
url: Api.API_PROJECT_ADD,
method: "post",
......@@ -53,4 +52,48 @@ const hpczone = () => {
});
};
export { current, menu, product, hpczone, addProject };
type getProjectParams = {
id: string;
};
// 获取项目信息
const getProject = (params: getProjectParams) => {
return request({
url: Api.API_PROJECT_GET,
method: "get",
params,
});
};
// 修改项目信息
const updateProject = (params: addProjectParams) => {
return request({
url: Api.API_PROJECT_UPDATE,
method: "put",
data: params,
});
};
type deleteProjectParams = {
id: string;
};
// 删除项目
const deleteProject = (params: deleteProjectParams) => {
return request({
url: Api.API_PROJECT_DELETE,
method: "delete",
params,
});
};
export {
current,
menu,
product,
hpczone,
addProject,
getProject,
updateProject,
deleteProject,
};
......@@ -21,3 +21,115 @@ main::-webkit-scrollbar-thumb {
border-radius: 4px;
height: 5px !important;
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
p,
blockquote,
dl,
dt,
dd,
ul,
ol,
li,
pre,
form,
fieldset,
legend,
button,
input,
textarea,
th,
td {
margin: 0;
padding: 0;
}
body,
button,
input,
select,
textarea {
font: 12px/1.5tahoma, arial, \5b8b\4f53;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 100%;
}
address,
cite,
dfn,
em,
var {
font-style: normal;
}
code,
kbd,
pre,
samp {
font-family: couriernew, courier, monospace;
}
small {
font-size: 12px;
}
ul,
ol {
list-style: none;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
sup {
vertical-align: text-top;
}
sub {
vertical-align: text-bottom;
}
legend {
color: #000;
}
fieldset,
img {
border: 0;
}
button,
input,
select,
textarea {
font-size: 100%;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
input::-webkit-input-placeholder {
color: #c2c6cc;
}
input::-moz-placeholder {
/* Mozilla Firefox 19+ */
color: #c2c6cc;
}
input:-moz-placeholder {
/* Mozilla Firefox 4 to 18 */
color: #c2c6cc;
}
input:-ms-input-placeholder {
/* Internet Explorer 10-11 */
color: #c2c6cc;
}
.infoList {
background-color: #fff;
}
.infoListLi {
display: flex;
justify-content: flex-start;
align-items: flex-start;
font-size: 14px;
line-height: 22px;
margin-bottom: 24px;
}
.infoListLiLabel {
width: 80px;
color: #8a9099;
margin-right: 24px;
}
.infoListLiValue {
color: #1e2633;
width: 460px;
}
import style from "./InformationDisplay.module.css";
import React from "react";
type InfoLi = {
label: string | number;
value: string | number;
};
type InfoList = Array<InfoLi>;
type InformationDisplayProps = {
infoList: InfoList;
};
const InformationDisplay = (props: InformationDisplayProps) => {
const { infoList } = props;
return (
<div className={style.infoList}>
{infoList.map((item) => {
return (
<div className={style.infoListLi} key={item.label}>
<div className={style.infoListLiLabel}>{item.label}</div>
<div className={style.infoListLiValue}>{item.value}</div>
</div>
);
})}
</div>
);
};
export default InformationDisplay;
import InformationDisplay from "./InformationDisplay";
export default InformationDisplay;
......@@ -32,9 +32,7 @@ const ButtonComponent = (props) => {
const defultClick = (event) => event && event.stoppropagation && event.stoppropagation()
// 更多按钮 点击 弹出菜单
const handleClick = (event) => {
console.log(event.currentTarget, 'lllll')
setAnchorEl(event.currentTarget)
}
// 关闭更多 menu 弹框
const handleClose = () => setAnchorEl(null);
......
......@@ -5,7 +5,7 @@ import { makeStyles } from "@mui/styles";
import cx from "classnames"
const useStyles = makeStyles({
MuiOutlinedInputInputLarge: { padding: "13.5px 15px", MozAppearance: 'textfield' },
MuiOutlinedInputInput: { padding: "6px 15px", "&::placeholder": { fontSize: "14px" }, MozAppearance: 'textfield' },
MuiOutlinedInputInput: { padding: "12px 15px", "&::placeholder": { fontSize: "14px" }, MozAppearance: 'textfield' },
MuiOutlinedInputInputSmall: { padding: "10px 15px", "&::placeholder": { fontSize: "13px" }, MozAppearance: 'textfield' },
MuiOutlinedInputInputXsmall: { padding: "8px 15px", "&::placeholder": { fontSize: "12px" }, MozAppearance: 'textfield' },
outlinedLarge: { transform: "translate(14px, 15.5px) scale(1)", fontSize: '14px', fontWeight: '400', color: '#707070' },
......
......@@ -165,7 +165,7 @@ export default function EnhancedTable(props) {
style={{ width: CellWidth, textAlign: item.numeric ? "right" : "left", paddingRight: item.sort && item.numeric ? "40px" : "", boxSizing: "border-box" }}
scope="row"
rowSpan={(row[item.id] === 'merge') ? (rows.length - (index)) : 1}
padding={item.disablePadding ? "none" : "default"}
padding={item.disablePadding ? "none" : "normal"}
classes={{
body: bodyTableCellStyle || classes.bodyTableCell,
}}
......
......@@ -18,7 +18,6 @@ import { useEffect } from "react";
export default function EnhancedTable(props) {
const classes = useStyles;
console.log(classes,11111);
const [order, setOrder] = React.useState("asc");
const [orderBy, setOrderBy] = React.useState("");
const { headCells, rows, footer = true, elevation1, tableStyle,tablecellstyle, tableContainerStyle, stickyheader, TableHeadClasses, onRowClick, defaultRow, minHeight='', borderBottom='', onDoubleClick,
......@@ -97,7 +96,7 @@ export default function EnhancedTable(props) {
<TableContainer style={{ ...tableContainerStyle }}>
<Table stickyHeader={stickyheader || false} className={classes.table} style={{ ...tableStyle }} aria-labelledby="tableTitle" size={size || "medium"} aria-label="cloudam table header" >
<EnhancedTableHeadComponent
classes={classes}
classes={classes()}
{...props}
numSelected={selected.length}
headTableCellCheckbox={headTableCellCheckbox}
......@@ -126,11 +125,7 @@ export default function EnhancedTable(props) {
return (
<TableRow
hover={ rowHover ? false : (row[disabledparam || "enabled"] ? true : false)}
onClick={(event) => {
if (!row[disabledparam]) return;
onRowClick && onRowClickDefault(row[param || "id"])
headCells.filter(k => k.id === "checkbox").length > 0 && handleClick(event, row[param || "id"])
}}
onDoubleClick={() => {
onDoubleClick && onDoubleClick(row)
}}
......@@ -143,27 +138,33 @@ export default function EnhancedTable(props) {
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row[param || "id"]}
key={row[param || "id"] || index}
selected={isItemSelected}
>
{
headCells.filter(k => k.id === "checkbox").length > 0 && <TableCell padding="checkbox">
headCells.filter(k => k.id === "checkbox").length > 0 && <TableCell
onClick={(event) => {
// if (!row[disabledparam]) return;
onRowClick && onRowClickDefault(row[param || "id"])
headCells.filter(k => k.id === "checkbox").length > 0 && handleClick(event, row[param || "id"])
}}
padding="checkbox">
<Checkbox color={"primary"} checked={isItemSelected} inputProps={{ "aria-labelledby": labelId }} />
</TableCell>
}
{
headCells.map((item, k) => {
headCells.filter(item=>item.id !== 'checkbox').map((item, k) => {
return (
<TableCell key={k} component="th" id={labelId + k}
// align={}
style={{ width:CellWidth, textAlign: item.numeric ? "right" : "left", paddingRight: item.sort && item.numeric ? "40px" : "",border:tablecellstyle }}
scope="row"
padding={item.disablePadding ? "none" : "default"}
padding={item.disablePadding ? "none" : "normal"}
classes={{
body: props.bodyTableCell || classes.bodyTableCell
}}
> {
item.render ? <>{item.render(row,index)}</> :
item.render ? <>{item.render(row[item.id],row,index)}</> :
row[item.id]
}
</TableCell>
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-01 16:53:15
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-02 10:19:08
* @LastEditTime: 2022-06-07 18:21:51
* @FilePath: /bkunyun/src/components/Material.Ui/Table/EnhancedTableHead.jsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -23,7 +23,11 @@ const EnhancedTableHead = (props) => {
onRequestSort(event, property);
};
return (
<TableHead classes={{ root: TableHeadClasses || classes.TableHeadClasses }}>
<TableHead classes={{ root: TableHeadClasses || classes.TableHeadClasses }} sx={{
th:{
backgroundColor: '#F7F8FA',
}
}}>
<TableRow style={{...RowStyle}}>
{
headCells.filter(k => k.id === "checkbox").length > 0 && <TableCell padding="checkbox">
......@@ -40,7 +44,7 @@ const EnhancedTableHead = (props) => {
<TableCell
key={headCell.id}
style={{ width: headCell.width ? headCell.width : "", textAlign: headCell.numeric ? "right" : "left", display: headCell.id !== "checkbox" ? "" : "none" }}
padding={headCell.disablePadding ? "none" : "default"}
padding={headCell.disablePadding ? "none" : "normal"}
sortDirection={orderBy === headCell.id ? order : false}
classes={{ head:( k && headTableCell) || classes.headTableCell }}
>
......
......@@ -248,7 +248,7 @@ export default function EnhancedTable(props) {
key={k} component="th" id={labelId + k}
style={{ width:item.width || '',maxWidth:item.width || '',textAlign: item.numeric ? "right" : "left", paddingRight: item.sort && item.numeric ? "40px" : "", boxSizing: "border-box", display: item.id !== "checkbox" ? "" : "none" }}
scope="row"
padding={item.disablePadding ? "none" : "default"}
padding={item.disablePadding ? "none" : "normal"}
classes={{ body: props.bodyTableCell || classes.bodyTableCell }}
> {row[item.id]} </TableCell>
)
......
......@@ -43,7 +43,6 @@ const useMyRouter = () => {
}
if (productInfo.res) {
console.log(productInfo, "1111111111111");
let list = productInfo.data?.data;
if (list.length === 0) {
currentProjectStore.setProjectList([]);
......
......@@ -10,11 +10,15 @@ import LoadingButton from "@mui/lab/LoadingButton";
import CloseIcon from "@mui/icons-material/Close";
import { useState } from "react";
import React, { useImperativeHandle } from "react";
// import LoadingButton from '@mui/lab/LoadingButton';
import { useImperativeHandle } from "react";
const MyDialog = (props: any) => {
const [open, setOpen] = useState(false);
const { title, handleSubmit, submitloading } = props;
const {
title,
handleSubmit,
submitloading,
submitStyle = { backgroundColor: "#1370FF" },
} = props;
const handleClickOpen = () => {
setOpen(true);
......@@ -62,7 +66,7 @@ const MyDialog = (props: any) => {
onClick={handleSubmit}
color="primary"
variant="contained"
style={{ backgroundColor: "#1370FF" }}
style={submitStyle}
>
确认
</LoadingButton>
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2021-12-04 15:46:25
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-05 18:06:47
* @LastEditTime: 2022-06-07 21:13:38
* @FilePath: /lionet-slb-pc/src/components/SearchView/components/Collapse.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -11,23 +11,24 @@ import Box from "@mui/material/Box";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import Select, { SelectProps } from "@mui/material/Select";
interface IOption {
export interface IOption {
label: string;
value: string;
disabled?: boolean;
}
interface IProps {
interface IProps
extends Omit<SelectProps, "value" | "options" | "onChange" | "title"> {
value?: IOption;
options: IOption[];
onChange?: (val: IOption) => void;
title?: string;
}
export default function BasicSelect(props: IProps) {
const { value, options, onChange, title } = props;
const { value, options, onChange, title, input } = props;
const handleChange = (event: SelectChangeEvent) => {
const handleChange = (event: any) => {
const newValue = options?.filter((item) => {
return item.value === event.target.value;
});
......@@ -39,14 +40,18 @@ export default function BasicSelect(props: IProps) {
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">
{title || "请选择"}
</InputLabel>
{input ? null : (
<InputLabel id="demo-simple-select-label">
{title || "请选择"}
</InputLabel>
)}
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={value?.value}
label={title || "请选择"}
size="small"
{...props}
value={value?.value}
onChange={handleChange}
>
{options.length
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-02 18:48:15
* @LastEditTime: 2022-06-07 20:31:40
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -45,7 +45,11 @@ const Tabs = (props: IProps) => {
</Box>
{tabList?.map((item) => {
return (
<TabPanel value={item.value} key={item.value}>
<TabPanel
sx={{ padding: "20px 0 0 0" }}
value={item.value}
key={item.value}
>
{item.component}
</TabPanel>
);
......
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
......@@ -25,7 +25,6 @@ class Menus {
}> = [];
initMenu = (list: projectList) => {
console.log('list: ', list);
this.productList = [];
this.utilityList = [];
for (let item of list) {
......
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-07 18:37:53
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-07 19:59:25
* @FilePath: /bkunyun/src/utils/util.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export const isProjectOwner = (name: string)=>{
let localName =''
try{
localName = JSON.parse(
localStorage.getItem("userInfo") || "{}"
)?.name
} catch {
console.error('获取localStorage出错')
}
return localName === name
}
\ No newline at end of file
......@@ -8,7 +8,32 @@
width: 10%;
min-width: 220px;
height: calc(100vh - 57px);
background-color: #f7f8fa;
}
.content {
flex: 1;
height: calc(100vh - 57px);
overflow: scroll;
}
.list {
/* background-color: red; */
}
.listItem {
padding: 8px 25px;
height: 38px;
box-sizing: border-box;
cursor: pointer;
color: #565c66;
font-size: 14px;
line-height: 22px;
}
.listItem:hover {
border-left: 3px solid #1370ff;
color: #1370ff;
background-color: #ebedf0;
}
.active {
border-left: 3px solid #1370ff;
color: #1370ff;
background-color: #ebedf0;
}
import {
Box,
List,
ListItem,
ListItemText,
ListItemButton,
} from "@mui/material";
// import CurrentProject from "./CurrentProject";
import { Box, List } from "@mui/material";
import CurrentProject from "../Project/components/CurrentProject";
import React from "react";
import { Outlet, useNavigate } from "react-router-dom";
import style from "./index.module.css";
import { observer } from "mobx-react-lite";
import { useStores } from "@/store/index";
import classnames from "classnames";
const MenuLayout = observer(() => {
const { permissionStore } = useStores();
let pathname = new URL(window.location.href).pathname;
const navigate = useNavigate();
......@@ -22,16 +17,19 @@ const MenuLayout = observer(() => {
<Box className={style.container}>
<Box className={style.aside}>
<CurrentProject />
<List>
<List className={style.list}>
{permissionStore.sidebarRouters.map((item, index) => {
return (
<ListItem key={"sidebar" + index}>
<ListItemButton
onClick={() => item.type === "page" && navigate(item.path)}
>
<ListItemText>{item.name}</ListItemText>
</ListItemButton>
</ListItem>
<li
key={"sidebar" + index}
className={classnames({
[style.listItem]: true,
[style.active]: item.path === pathname,
})}
onClick={() => item.type === "page" && navigate(item.path)}
>
{item.name}
</li>
);
})}
</List>
......
.loadingBox {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
height: 300px;
}
.projectInfoList {
background-color: #fff;
position: relative;
}
.projectInfoListLi {
margin-bottom: 24px;
}
.projectInfoListLiLabel {
color: #1e2633;
line-height: 22px;
font-size: 14px;
font-weight: 550;
margin-bottom: 12px;
}
.projectInfoName::after {
content: "*";
color: red;
}
.projectInfoListLiValue {
width: 560px;
height: 36px;
border: 1px solid #e6e8eb;
border-radius: 4px;
color: #565c66;
padding: 0 12px;
box-sizing: border-box;
outline: none;
}
.projectInfoListLiValue:focus {
border: 1px solid #136efa;
}
.projectInfoTextarea {
line-height: 22px;
height: 82px;
padding: 7px 12px;
}
.projectInfoSelect {
width: 560px;
padding: 0 12px;
}
.disable {
background: #f7f8fa;
}
.projectInfoListLiText {
margin-bottom: 16px;
color: #8a9099;
font-size: 12px;
line-height: 20px;
}
.deleteBox {
width: 380px;
}
.deleteText1 {
font-size: 14px;
line-height: 22px;
color: #ff4e4e;
margin-bottom: 8px;
}
.deleteText2 {
color: #1e2633;
font-size: 14px;
line-height: 22px;
margin-bottom: 8px;
}
.deleteText3 {
color: #1e2633;
font-size: 14px;
line-height: 22px;
margin-bottom: 20px;
}
.deleteProjectName {
color: #1370ff;
}
.deleteProjectInput {
width: 100%;
height: 36px;
border: 1px solid #e6e8eb;
border-radius: 4px;
color: #565c66;
padding: 0 12px;
box-sizing: border-box;
outline: none;
}
.deleteProjectInput:focus {
border: 1px solid #136efa;
}
......@@ -6,12 +6,316 @@
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { memo } from "react";
import React, { memo, useEffect, useMemo, useState } from "react";
import { Box } from "@mui/system";
import style from "./index.module.css";
import useMyRequest from "@/hooks/useMyRequest";
import {
getProject,
hpczone,
updateProject,
deleteProject,
} from "@/api/project_api";
import { useStores } from "@/store";
import { toJS } from "mobx";
import { observer } from "mobx-react-lite";
import InformationDisplay from "@/components/InformationDisplay";
import classnames from "classnames";
import { Button } from "@mui/material";
import LoadingButton from "@mui/lab/LoadingButton";
import { useMessage } from "@/components/MySnackbar";
import Loading from "@/views/Loading";
import MyDialog from "@/components/mui/MyDialog";
import { getProjectList } from "../../project";
const BaseInfo = () => {
return <Box>基础信息</Box>;
const reg = new RegExp("^[A-Za-z0-9\u4e00-\u9fa5]+$");
type zoneIdOption = {
id: string;
name: string;
};
const BaseInfo = observer(() => {
const message = useMessage();
const { currentProjectStore } = useStores();
const [projectInfo, setProjectInfo] = useState<any>({});
const [deleteProjectName, setDeleteProjectName] = useState("");
const currentUserName = JSON.parse(
localStorage.getItem("userInfo") || "{}"
).name;
const { run, loading } = useMyRequest(getProject, {
onSuccess: (result: any) => {
setProjectInfo(result.data);
},
});
useEffect(() => {
run({
id: currentProjectStore.currentProjectInfo.id as string,
});
}, [currentProjectStore.currentProjectInfo.id, run]);
const [zoneIdMap, setZoneIdMap] = useState<Map<string, string>>(new Map());
const [zoneIdOptions, setZoneIdOptions] = useState<Array<zoneIdOption>>([]);
// 获取计算区信息
const { run: getZone } = useMyRequest(hpczone, {
onSuccess: (result: any) => {
setZoneIdOptions(result);
let zoneMap: Map<string, string> = new Map();
result.forEach((item: zoneIdOption) => {
zoneMap.set(item.id, item.name);
});
setZoneIdMap(zoneMap);
},
});
useEffect(() => {
getZone();
}, [getZone]);
// 项目信息展示数据转换
const infoList = useMemo(() => {
return [
{
label: "项目名称:",
value: projectInfo.name,
},
{
label: "项目描述:",
value: projectInfo.desc,
},
{
label: "计算区:",
value: zoneIdMap.get(projectInfo.zoneId) || projectInfo.zoneId,
},
{
label: "创建人:",
value: projectInfo.owner,
},
{
label: "扣费账号:",
value: projectInfo.tenantUser,
},
];
}, [projectInfo, zoneIdMap]);
const nameChange = (e: any) => {
setProjectInfo({
...projectInfo,
name: e.target.value,
});
};
const descChange = (e: any) => {
setProjectInfo({
...projectInfo,
desc: e.target.value,
});
};
const { run: updateProjectRun, loading: updateLoading } = useMyRequest(
updateProject,
{
onSuccess: async (result: any) => {
message.success("修改成功");
const projectList = await getProjectList();
currentProjectStore.setProjectList(projectList);
currentProjectStore.changeProject(projectInfo);
},
}
);
// 修改项目
const handleClickUpdate = () => {
if (projectInfo.name) {
if (reg.test(projectInfo.name)) {
updateProjectRun({ ...projectInfo, product: "CADD" });
} else {
message.info(
"格式不正确,必须在30字符以内,仅限大小写字母、数字、中文"
);
}
} else {
message.info("项目名称不能为空");
}
};
const { run: deleteProjectRun, loading: deleteLoading } = useMyRequest(
deleteProject,
{
onSuccess: async (result: any) => {
message.success("删除成功");
DialogRef.current.handleClose();
const projectList = await getProjectList();
currentProjectStore.setProjectList(projectList);
currentProjectStore.changeProject(projectList[0]);
setProjectInfo(projectList[0]);
},
}
);
let DialogRef: any = React.createRef();
// 显示删除弹窗
const handleClickDelete = () => {
DialogRef.current.handleClickOpen();
};
const deleteProjectNameChange = (e: any) => {
setDeleteProjectName(e.target.value);
};
// 删除项目
const handleSubmitDelete = () => {
if (
deleteProjectName === toJS(currentProjectStore.currentProjectInfo.name)
) {
deleteProjectRun({ id: projectInfo.id });
} else {
message.error("项目名称不匹配");
}
};
if (loading) {
return (
<div className={style.loadingBox}>
<Loading />
</div>
);
} else if (currentUserName !== projectInfo.owner) {
return <InformationDisplay infoList={infoList} />;
} else {
return (
<div className={style.projectInfoList}>
<div className={style.projectInfoListLi}>
<div
className={classnames({
[style.projectInfoListLiLabel]: true,
[style.projectInfoName]: true,
})}
>
项目名称
</div>
<input
value={projectInfo.name}
className={style.projectInfoListLiValue}
onChange={nameChange}
maxLength={30}
placeholder="请输入项目名称"
></input>
</div>
<div className={style.projectInfoListLi}>
<div className={style.projectInfoListLiLabel}>项目描述</div>
<textarea
value={projectInfo.desc}
className={classnames({
[style.projectInfoListLiValue]: true,
[style.projectInfoTextarea]: true,
})}
onChange={descChange}
placeholder="项目描述限制100字以内"
maxLength={100}
></textarea>
</div>
<div className={style.projectInfoListLi}>
<div className={style.projectInfoListLiLabel}>计算区</div>
<select
value={projectInfo.zoneId}
disabled
className={classnames({
[style.projectInfoListLiValue]: true,
[style.projectInfoSelect]: true,
[style.disable]: true,
})}
>
{zoneIdOptions.map((option) => (
<option key={option.id} value={option.id}>
{option.name}
</option>
))}
</select>
</div>
<div className={style.projectInfoListLi}>
<div className={style.projectInfoListLiLabel}>创建人</div>
<input
value={projectInfo.owner}
disabled
className={classnames({
[style.projectInfoListLiValue]: true,
[style.disable]: true,
})}
></input>
</div>
<div className={style.projectInfoListLi}>
<div className={style.projectInfoListLiLabel}>扣费账号</div>
<input
value={projectInfo.tenantUser}
disabled
className={classnames({
[style.projectInfoListLiValue]: true,
[style.disable]: true,
})}
></input>
</div>
<div className={style.projectInfoListLi}>
<LoadingButton
variant="outlined"
className={style.updateButton}
onClick={handleClickUpdate}
loading={updateLoading}
>
保存修改
</LoadingButton>
</div>
<div className={style.projectInfoListLi}>
<div className={style.projectInfoListLiLabel}>删除项目</div>
<div className={style.projectInfoListLiText}>
删除项目将删除其存储的数据和所有相关资源,并且已删除的项目无法恢复!请谨慎操作!
</div>
<Button
variant="contained"
className={style.updateButton}
onClick={handleClickDelete}
style={{
backgroundColor: "#fff",
color: "#FF4E4E",
border: "1px solid #FF4E4E",
}}
>
删除项目
</Button>
</div>
<MyDialog
handleSubmit={handleSubmitDelete}
onRef={DialogRef}
title="删除项目"
submitloading={deleteLoading}
submitStyle={{ background: "#FF4E4E", color: "#fff" }}
>
<div className={style.deleteBox}>
<div className={style.deleteText1}>
您要删除本项目。已删除的项目将无法恢复!您真的确定吗?
</div>
<div className={style.deleteText2}>
此操作可能会导致数据丢失。为防止意外操作,我们要求您确认您的操作。
</div>
<div className={style.deleteText3}>
请输入“
<span className={style.deleteProjectName}>
{currentProjectStore.currentProjectInfo.name}
</span>
”以确认此操作。
</div>
<input
value={deleteProjectName}
className={style.deleteProjectInput}
onChange={deleteProjectNameChange}
maxLength={30}
placeholder="请输入项目名称"
></input>
</div>
</MyDialog>
</div>
);
}
});
export default memo(BaseInfo);
......@@ -2,39 +2,184 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-04 18:28:31
* @LastEditTime: 2022-06-07 21:50:55
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import Dialog from "@/components/mui/Dialog";
import { memo } from "react";
import { toJS } from "mobx";
import { observer } from "mobx-react-lite";
import _ from "lodash";
import Table from "@/components/Material.Ui/Table";
import Dialog from "@/components/mui/Dialog";
import { memo, useCallback, useEffect, useMemo, useState } from "react";
import { Box, OutlinedInput } from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import { IResponse, useHttp } from "@/api/http";
import { useStores } from "@/store";
import { useMessage } from "@/components/MySnackbar";
import Select, { IOption } from "@/components/mui/Select";
interface IProps {
setAddMemberDialog: (val: boolean) => void;
addMemberDialog: boolean;
getTableList: () => void;
}
const AddMember = (props: IProps) => {
const { addMemberDialog, setAddMemberDialog } = props;
const AddMember = observer((props: IProps) => {
const { addMemberDialog, setAddMemberDialog, getTableList } = props;
const http = useHttp();
const { currentProjectStore } = useStores();
const [tableData, setTableData] = useState([]);
const [projectMember, setProjectMember] = useState("");
const [filterTableData, setFilterTableData] = useState([]);
const [checkData, setCheckData] = useState<string[]>([]);
const Message = useMessage();
const selectOptions = useMemo(() => {
return [
{ value: "VIEWER", label: "查看者" },
{ value: "DEVELOPER", label: "研发者" },
{ value: "MANAGER", label: "管理者" },
];
}, []);
const changePermission = useCallback(
(val: IOption, index: number) => {
const tableDataIndex = tableData[index] as any;
const newVal = { ...tableDataIndex, projectRole: val.value };
const newTableData = _.cloneDeep(tableData);
newTableData.splice(index, 1, newVal);
setTableData(newTableData);
},
[tableData]
);
const columns = [
{ id: "checkbox", width: 50 },
{ id: "username", label: "项目成员", width: 160 },
{
id: "projectRole",
label: "项目权限",
width: 160,
render: (item: string, row: any, index: number) => {
const defaultValue = selectOptions.filter(
(every) => every.value === item
);
return (
<Select
input={<OutlinedInput />}
value={
defaultValue?.length
? defaultValue[0]
: { value: "VIEWER", label: "查看者" }
}
onChange={(val) => changePermission(val, index)}
options={selectOptions}
size="small"
/>
);
},
},
];
useEffect(() => {
if (!!projectMember) {
const newVal =
tableData.filter((item: any) => {
return item?.username?.includes(projectMember);
}) || [];
setFilterTableData(newVal || []);
} else {
setFilterTableData(tableData);
}
}, [projectMember, tableData]);
useEffect(() => {
if (!addMemberDialog) return;
const projectInfo = toJS(currentProjectStore?.currentProjectInfo);
http
.get<IResponse<any>>("/cpp/project/listusers", {
params: { id: projectInfo?.id || "" },
})
.then((res) => {
if (res.data.length) {
setTableData(res?.data);
}
});
}, [currentProjectStore?.currentProjectInfo, http, addMemberDialog]);
const onClose = () => {
setAddMemberDialog(false);
};
const onConfirm = () => {
setAddMemberDialog(false);
const projectInfo = toJS(currentProjectStore?.currentProjectInfo);
console.log(tableData, 1111);
const params = tableData.filter((item: any) => {
return checkData.includes(item?.username);
});
if (params.length) {
http
.put<IResponse<any>>(
`/cpp/project/updatemember?id=${projectInfo?.id || ""}`,
params
)
.then((res) => {
const { errorCode } = res;
if (errorCode === 0) {
Message.success("新增成功!");
getTableList();
setAddMemberDialog(false);
}
});
} else {
Message.warning("请先选择项目成员!");
}
};
return (
<>
<Dialog
open={addMemberDialog}
onClose={onClose}
onConfirm={onConfirm}
title="移出项目"
title="添加成员"
>
<div>确认移出该成员吗?</div>
<Box>
<Box sx={{ mb: 2 }}>
<OutlinedInput
onChange={(e: any) => {
_.debounce(() => {
setProjectMember(e.target.value);
}, 200)();
}}
placeholder="搜索项目成员"
size="small"
sx={{ width: "100%", height: 32 }}
endAdornment={<SearchIcon style={{ color: "#8A9099" }} />}
/>
</Box>
<div style={{ overflowY: "scroll", maxHeight: 400 }}>
<Table
checkboxData={(val: string[]) => setCheckData(val)}
param="username"
disabledparam={"enabled"}
rowHover={true}
stickyheader={true}
rows={filterTableData}
rowsPerPage="99"
headCells={columns}
footer={false}
tableStyle={{ minWidth: "auto" }}
borderBottom="none"
/>
</div>
</Box>
</Dialog>
</>
);
};
});
export default memo(AddMember);
......@@ -2,41 +2,108 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-05 18:15:22
* @LastEditTime: 2022-06-07 21:45:49
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// import Dialog from "@/components/Material.Ui/Dialog";
import Select from "@/components/mui/Select";
import { memo, useEffect, useMemo, useState } from "react";
import { observer } from "mobx-react-lite";
import { toJS } from "mobx";
import Select, { IOption } from "@/components/mui/Select";
import Dialog from "@/components/mui/Dialog";
import { memo } from "react";
import { IResponse, useHttp } from "@/api/http";
import { useStores } from "@/store/index";
import { IDialogInfo } from "../interface";
import { useMessage } from "@/components/MySnackbar";
interface IProps {
setPermissionDialog: (val: boolean) => void;
permissionDialog: boolean;
setPermissionDialog: (val: IDialogInfo) => void;
permissionDialog: IDialogInfo;
getTableList: () => void;
}
const ChangePermission = (props: IProps) => {
const { permissionDialog, setPermissionDialog } = props;
const ChangePermission = observer((props: IProps) => {
const { permissionDialog, setPermissionDialog, getTableList } = props;
const { currentProjectStore } = useStores();
const Message = useMessage();
const http = useHttp();
const [selectOptions, setSelectOptions] = useState<IOption[]>([]);
const [selectValue, setSelectValue] = useState<IOption | undefined>();
useEffect(() => {
if (permissionDialog?.isShow) {
http.get<IResponse<any>>("/cpp/project/listroles").then((res) => {
const arr = [];
const { data } = res;
for (const key in data) {
arr.push({
label: String(data[key]),
value: key,
});
}
setSelectOptions(arr);
});
}
}, [http, permissionDialog]);
const changePermission = (val: any) => {
setSelectValue(val);
};
const onClose = () => {
setPermissionDialog(false);
setPermissionDialog({ isShow: false, username: "" });
};
const onConfirm = () => {
setPermissionDialog(false);
const projectInfo = toJS(currentProjectStore?.currentProjectInfo);
http
.put<IResponse<any>>(
`/cpp/project/updateuserrole?id=${projectInfo?.id || ""}&username=${
permissionDialog.username
}&role=${selectValue?.value}`
)
.then((res) => {
const { errorCode } = res;
if (errorCode === 0) {
Message.success("更改成功!");
getTableList();
setPermissionDialog({ isShow: false, username: "" });
}
});
};
useEffect(() => {
const defaultValue = selectOptions.filter(
(every) => every.value === permissionDialog?.projectRole
);
if (defaultValue?.length) {
setSelectValue(defaultValue[0]);
} else {
setSelectValue({ value: "VIEWER", label: "查看者" });
}
}, [permissionDialog, selectOptions]);
return (
<>
<Dialog
open={permissionDialog}
open={permissionDialog?.isShow}
onClose={onClose}
onConfirm={onConfirm}
title="更改权限"
>
<Select title="项目权限" options={[]} />
<div style={{ marginTop: 12 }}>
<Select
title="项目权限"
value={selectValue}
onChange={changePermission}
options={selectOptions}
size="small"
/>
</div>
</Dialog>
</>
);
};
});
export default memo(ChangePermission);
......@@ -2,32 +2,61 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-04 17:54:50
* @LastEditTime: 2022-06-07 15:55:26
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// import Dialog from "@/components/Material.Ui/Dialog";
import { IResponse, useHttp } from "@/api/http";
import Dialog from "@/components/mui/Dialog";
import { useMessage } from "@/components/MySnackbar";
import { useStores } from "@/store";
import { toJS } from "mobx";
import { observer } from "mobx-react-lite";
import { memo } from "react";
import { IDialogInfo } from "../interface";
interface IProps {
setRemoveDialog: (val: boolean) => void;
removeDialog: boolean;
setRemoveDialog: (val: IDialogInfo) => void;
removeDialog: IDialogInfo;
getTableList: () => void;
}
const RemoveItem = (props: IProps) => {
const { removeDialog, setRemoveDialog } = props;
const RemoveItem = observer((props: IProps) => {
const { removeDialog, setRemoveDialog, getTableList } = props;
const http = useHttp();
const Message = useMessage();
const { currentProjectStore } = useStores();
const onClose = () => {
setRemoveDialog(false);
setRemoveDialog({ isShow: false, username: "" });
};
const onConfirm = () => {
setRemoveDialog(false);
const projectInfo = toJS(currentProjectStore?.currentProjectInfo);
console.log(111111, {
id: projectInfo?.id || "",
username: removeDialog.username,
});
http
.del<IResponse<any>>(
`/cpp/project/removemember?id=${projectInfo?.id || ""}&username=${
removeDialog.username || ""
}`
)
.then((res) => {
const { errorCode } = res;
if (errorCode === 0) {
Message.success("移出成功!");
getTableList();
setRemoveDialog({ isShow: false, username: "" });
}
});
};
return (
<>
<Dialog
open={removeDialog}
open={removeDialog?.isShow}
onClose={onClose}
onConfirm={onConfirm}
title="移出项目"
......@@ -36,6 +65,6 @@ const RemoveItem = (props: IProps) => {
</Dialog>
</>
);
};
});
export default memo(RemoveItem);
......@@ -2,6 +2,7 @@
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.removeItemBox {
......
......@@ -2,70 +2,125 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-04 18:33:02
* @LastEditTime: 2022-06-07 21:39:30
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { memo, useEffect, useState } from "react";
import { memo, useCallback, useEffect, useMemo, useState } from "react";
import _ from "lodash";
import OutlinedInput from "@mui/material/OutlinedInput";
import { Box } from "@mui/material";
import Button from "@mui/material/Button";
import SearchIcon from "@mui/icons-material/Search";
import Add from "@mui/icons-material/Add";
import Table from "@/components/Material.Ui/Table";
import { useHttp } from "@/api/http";
import { IResponse, useHttp } from "@/api/http";
import Input from "@/components/Material.Ui/Input";
import RemoveItem from "./components/RemoveItem";
import ChangePermission from "./components/ChangePermission";
import AddMember from "./components/AddMember";
import styles from "./index.module.css";
import { IDialogInfo } from "./interface";
import { toJS } from "mobx";
import { useStores } from "@/store";
import { isProjectOwner } from "@/utils/util";
const ProjectMembers = () => {
const http = useHttp();
const columns = [
{ id: "username", label: "成员名称", width: "20%" },
{ id: "projectRoleDesc", label: "项目权限", width: "20%" },
{ id: "phone", label: "联系方式", width: "20%" },
{
id: "operation",
label: "操作",
width: "20%",
render: (item: any) => {
console.log(item, "000000");
return (
<>
<span
style={{ color: "#1370FF", cursor: "pointer" }}
onClick={onPermissionBtn}
>
更改权限
</span>
<span className={styles.removeItemBox} onClick={onRemoveItemBtn}>
移出项目
</span>
</>
);
},
},
];
const { currentProjectStore } = useStores();
/** 删除成员 */
const [removeDialog, setRemoveDialog] = useState<boolean>(false);
const [removeDialog, setRemoveDialog] = useState<IDialogInfo>({
isShow: false,
username: "",
});
/** 更改权限 */
const [permissionDialog, setPermissionDialog] = useState<boolean>(false);
const [permissionDialog, setPermissionDialog] = useState<IDialogInfo>({
isShow: false,
username: "",
});
/** 添加成员 */
const [addMemberDialog, setAddMemberDialog] = useState<boolean>(false);
/** 表格数据 */
const [tableData, setTableData] = useState([]);
/** 项目名称 */
const [projectName, setProjectMember] = useState("");
/** 过滤后数据 */
const [filterTableData, setFilterTableData] = useState([]);
/** 项目所有者 */
const [projectOwner, setProjectOwner] = useState("");
useEffect(() => {
const columns = useMemo(() => {
const val: any = [
{ id: "username", label: "成员名称" },
{ id: "projectRoleDesc", label: "项目权限" },
{ id: "phone", label: "联系方式" },
];
if (isProjectOwner(projectOwner)) {
val.push({
id: "operation",
label: "操作",
width: 160,
render: (item: any, row: any) => {
return projectOwner === row?.username ? null : (
<>
<span
style={{ color: "#1370FF", cursor: "pointer" }}
onClick={() => {
onPermissionBtn(row);
}}
>
更改权限
</span>
<span
className={styles.removeItemBox}
onClick={() => {
onRemoveItemBtn(row.username);
}}
>
移出项目
</span>
</>
);
},
});
}
return val;
}, [projectOwner]);
/** 获取表格数据 */
const getTableList = useCallback(() => {
const projectInfo = toJS(currentProjectStore?.currentProjectInfo);
if (!projectInfo?.id) return;
http
.get("/cpp/project/list", {
params: { product: "cadd" },
.get<IResponse<any>>("/cpp/project/get", {
params: { id: projectInfo?.id || "" },
})
.then((res) => {
console.log(res);
const { data = {} } = res;
setProjectOwner(data.owner);
setTableData(data?.members || []);
});
}, [http]);
}, [currentProjectStore?.currentProjectInfo, http]);
useEffect(() => {
getTableList();
}, [getTableList]);
useEffect(() => {
if (!!projectName) {
const newVal =
tableData.filter((item: any) => {
return item?.username?.includes(projectName);
}) || [];
setFilterTableData(newVal || []);
} else {
setFilterTableData(tableData);
}
}, [projectName, tableData]);
/** 点击添加成员 */
const onAddMember = () => {
......@@ -73,44 +128,49 @@ const ProjectMembers = () => {
};
/** 点击删除成员 */
const onRemoveItemBtn = () => {
setRemoveDialog(true);
const onRemoveItemBtn = (userName: string) => {
setRemoveDialog({ isShow: true, username: userName });
};
/** 点击更改权限 */
const onPermissionBtn = () => {
setPermissionDialog(true);
const onPermissionBtn = (row: any) => {
setPermissionDialog({
isShow: true,
username: row?.username || "",
projectRole: row?.projectRole || "",
});
};
return (
<>
<Box className={styles.headerBox}>
<Input
<OutlinedInput
onChange={(e: any) => {
console.log(e);
_.debounce(() => {
setProjectMember(e.target.value);
}, 200)();
}}
size="xsmall"
placeholder="搜索项目成员"
endAdornment={<SearchIcon />}
/>
<Button
variant="contained"
onClick={onAddMember}
startIcon={<Add />}
size="small"
>
添加成员
</Button>
sx={{ width: 340, height: 32 }}
endAdornment={<SearchIcon style={{ color: "#8A9099" }} />}
/>
{isProjectOwner(projectOwner) ? (
<Button
style={{ backgroundColor: "#1370FF " }}
variant="contained"
onClick={onAddMember}
startIcon={<Add />}
size="small"
>
添加成员
</Button>
) : null}
</Box>
<Table
rowHover={true}
stickyheader={true}
rows={[
{
username: "wuyongs",
phone: 2323424,
},
]}
rows={filterTableData}
rowsPerPage={"99"}
headCells={columns}
nopadding={true}
......@@ -121,14 +181,17 @@ const ProjectMembers = () => {
<RemoveItem
removeDialog={removeDialog}
setRemoveDialog={setRemoveDialog}
getTableList={getTableList}
/>
<ChangePermission
permissionDialog={permissionDialog}
getTableList={getTableList}
setPermissionDialog={setPermissionDialog}
/>
<AddMember
addMemberDialog={addMemberDialog}
setAddMemberDialog={setAddMemberDialog}
getTableList={getTableList}
/>
</>
);
......
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-06 15:07:36
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-07 21:38:37
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/ProjectMembers/interface.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export interface IDialogInfo {
isShow: boolean;
username?: string;
projectRole?: string
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-05 18:29:59
* @LastEditTime: 2022-06-07 20:23:02
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -40,17 +40,19 @@ const ProjectSetting = observer(() => {
setValue(val);
};
if (currentProjectStore.currentProjectInfo.name) {
if (currentProjectStore.currentProjectInfo.name || true) {
return (
<Box style={{ padding: 24 }}>
<div>
<div style={{ padding: 24 }}>
<div style={{ display: "flex", alignItems: "center" }}>
<img src={projectImg} alt="项目logo" />
<span>项目名称A</span>
<span style={{ marginLeft: 12 }}>
{currentProjectStore.currentProjectInfo.name}
</span>
</div>
<Box sx={{ width: "100%", typography: "body1" }}>
<Tabs value={value} onChange={changeTabs} tabList={tabList} />
</Box>
</Box>
</div>
);
} else {
return <NoProject />;
......
......@@ -4,6 +4,9 @@ import MyDialog from "@/components/mui/MyDialog";
import React, { useState, useEffect, useImperativeHandle } from "react";
import useMyRequest from "@/hooks/useMyRequest";
import { hpczone, addProject } from "@/api/project_api";
import { useMessage } from "@/components/MySnackbar";
import { getProjectList } from "../../project";
import { useStores } from "@/store";
const reg = new RegExp("^[A-Za-z0-9\u4e00-\u9fa5]+$");
......@@ -13,6 +16,8 @@ type zoneIdOption = {
};
const AddProject = (props: any) => {
const { currentProjectStore } = useStores();
const message = useMessage();
let DialogRef: any = React.createRef();
useImperativeHandle(props.onRef, () => {
return {
......@@ -22,6 +27,7 @@ const AddProject = (props: any) => {
const [zoneId, setZoneId] = useState("");
const [zoneIdOptions, setZoneIdOptions] = useState<Array<zoneIdOption>>([]);
// 设置计算区
const { run } = useMyRequest(hpczone, {
onSuccess: (result: any) => {
setZoneIdOptions(result);
......@@ -33,10 +39,13 @@ const AddProject = (props: any) => {
onBefore: () => {
setSubmitloading(true);
},
onSuccess: (result: any) => {
onSuccess: async (result: any) => {
if (result.data) {
setSubmitloading(false);
DialogRef.current.handleClose();
message.success("新建项目成功");
const projectList = await getProjectList();
currentProjectStore.setProjectList(projectList);
}
},
onError: () => {
......@@ -46,7 +55,7 @@ const AddProject = (props: any) => {
useEffect(() => {
run();
}, []);
}, [run]);
const handleClickOpen = () => {
DialogRef.current.handleClickOpen();
......@@ -62,6 +71,7 @@ const AddProject = (props: any) => {
error: false,
help: "",
});
const checkName = (name: string) => {
if (name) {
if (name.length > 30) {
......@@ -87,6 +97,7 @@ const AddProject = (props: any) => {
});
}
};
const handleNameChange = (e: any) => {
const name = e.target.value;
setName(name);
......@@ -96,6 +107,7 @@ const AddProject = (props: any) => {
const handleZoneIdChange = (e: any) => {
setZoneId(e.target.value);
};
const handleDescChange = (e: any) => {
const desc = e.target.value;
setDesc(desc);
......@@ -126,6 +138,10 @@ const AddProject = (props: any) => {
}
};
const handleFromBox = (e: React.SyntheticEvent) => {
e.nativeEvent.stopImmediatePropagation();
};
return (
<MyDialog
handleSubmit={handleSubmit}
......@@ -133,7 +149,7 @@ const AddProject = (props: any) => {
title="新建项目"
submitloading={submitloading}
>
<div className={style.formBox}>
<div className={style.formBox} onClick={handleFromBox}>
<TextField
required
error={nameCheck.error}
......@@ -152,6 +168,7 @@ const AddProject = (props: any) => {
value={zoneId}
onChange={handleZoneIdChange}
variant="outlined"
onClick={handleFromBox}
>
{zoneIdOptions &&
zoneIdOptions.map((option) => (
......
......@@ -4,14 +4,25 @@ import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
import { Popper, Fade } from "@mui/material";
import { useStores } from "@/store/index";
import ProjectListPopper from "../ProjectListPopper";
import { useState } from "react";
import React, { useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
import AddProject from "../AddProject";
const CurrentProject = observer(() => {
const { currentProjectStore } = useStores();
let addProjectRef: any = React.createRef();
const [open, setOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
// 结合handleShowProjectList中的event.nativeEvent.stopImmediatePropagation();实现点击空白区域隐藏项目列表
useEffect(()=>{
document.addEventListener('click', (e) => {
setOpen(false)
})
}, [])
const handleShowProjectList = (event: React.MouseEvent<HTMLElement>) => {
event.nativeEvent.stopImmediatePropagation();
setAnchorEl(event.currentTarget);
setOpen((previousOpen) => !previousOpen);
};
......@@ -22,6 +33,11 @@ const CurrentProject = observer(() => {
setOpen(!open);
};
const openAddProject = () => {
addProjectRef.current.handleClickOpen()
setOpen(false)
}
return (
<div>
<div
......@@ -43,17 +59,20 @@ const CurrentProject = observer(() => {
style={{ fontSize: 12 }}
/>
</div>
<AddProject onRef={addProjectRef} />
<Popper
id={id}
open={open}
anchorEl={anchorEl}
placement="right-start"
transition
style={{ zIndex: 100 }}
>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<div>
<ProjectListPopper
handleClickOpen={openAddProject}
handleChangeCurrentProject={handleChangeCurrentProject}
/>
</div>
......
......@@ -4,19 +4,14 @@ import { InputBase, IconButton } from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import AddIcon from "@mui/icons-material/Add";
import { useStores } from "@/store/index";
import AddProject from "@/views/Project/components/AddProject";
import moment from "moment";
import React, { useMemo, useState } from "react";
import { toJS } from "mobx";
import { observer } from "mobx-react-lite";
const ProjectListPopper = observer((props: any) => {
const { handleChangeCurrentProject } = props;
let addProjectRef: any = React.createRef();
const { handleChangeCurrentProject, handleClickOpen } = props;
const { currentProjectStore } = useStores();
const handleClickOpen = () => {
addProjectRef.current.handleClickOpen();
};
const [name, setName] = useState("");
const nameChange = (e: any) => {
setName(e.target.value);
......@@ -28,12 +23,12 @@ const ProjectListPopper = observer((props: any) => {
});
}, [currentProjectStore.projectList, name]);
// const handleChangeCurrentProject = (project: any) => {
// currentProjectStore.changeProject(project);
// };
const handleProjectBox = (e: React.SyntheticEvent) => {
e.nativeEvent.stopImmediatePropagation();
}
return (
<div className={style.projectBox}>
<div className={style.projectBox} onClick={handleProjectBox}>
<div className={style.searchBox}>
<IconButton
type="submit"
......@@ -57,7 +52,6 @@ const ProjectListPopper = observer((props: any) => {
>
<AddIcon className={style.addIcon} />
</IconButton>
<AddProject onRef={addProjectRef} />
</div>
<div className={style.projectlist}>
{list.map((item: any) => {
......
import { product } from "@/api/project_api"
export const getProjectList = async () => {
const res = await product({product: "CADD",})
return res.data
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment