Browse Source

feat: 代码配置选择回显选中的

dev-zw-temp
zhaowei 7 months ago
parent
commit
2fdbf62b2b
9 changed files with 217 additions and 34 deletions
  1. +22
    -3
      react-ui/src/components/CodeConfigItem/index.less
  2. +38
    -11
      react-ui/src/components/CodeConfigItem/index.tsx
  3. +39
    -3
      react-ui/src/components/CodeSelect/index.tsx
  4. +1
    -0
      react-ui/src/components/CodeSelectorModal/index.less
  5. +76
    -15
      react-ui/src/components/CodeSelectorModal/index.tsx
  6. +1
    -0
      react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less
  7. +28
    -2
      react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx
  8. +9
    -0
      react-ui/src/services/codeConfig/index.js
  9. +3
    -0
      react-ui/src/types.ts

+ 22
- 3
react-ui/src/components/CodeConfigItem/index.less View File

@@ -1,11 +1,22 @@
.code-config-item {
position: relative;
width: calc(25% - 7.5px);
width: calc(33.33% - 7px);
padding: 15px;
background-color: .addAlpha(@primary-color, 0.04) [];
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;

&__checkbox {
flex: 1;
min-width: 0;

:global {
.ant-checkbox + span {
flex: 1;
min-width: 0;
}
}
}

&__name {
margin-right: 8px;
@@ -38,6 +49,8 @@
margin-bottom: 10px !important;
color: @text-color-secondary;
font-size: 13px;
cursor: pointer;
word-break: break-all;
}

&__branch {
@@ -46,11 +59,17 @@
}

&:hover {
background-color: .addAlpha(@primary-color, 0.08) [];
}

&--active {
border-color: @primary-color;
box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1);
}

&:hover &__name {
&--active &__name {
color: @primary-color;
}


}

+ 38
- 11
react-ui/src/components/CodeConfigItem/index.tsx View File

@@ -1,25 +1,51 @@
import { type CodeConfigData } from '@/pages/CodeConfig/List';
import { Flex, Typography } from 'antd';
import { getGitUrl } from '@/utils';
import { Checkbox, Flex, Typography } from 'antd';
import { type CheckboxChangeEvent } from 'antd/es/checkbox';
import classNames from 'classnames';
import { useState } from 'react';
import styles from './index.less';

type CodeConfigItemProps = {
item: CodeConfigData;
onClick?: (item: CodeConfigData) => void;
checked: boolean;
onChange?: (item: CodeConfigData, checked: boolean) => void;
};

function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
function CodeConfigItem({ item, checked, onChange }: CodeConfigItemProps) {
const [isEllipsis, setIsEllipsis] = useState(false);

const openProject = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.stopPropagation();
const { git_url, git_branch } = item;
const url = getGitUrl(git_url, git_branch);
window.open(url, '_blank');
};

const handleChange = (e: CheckboxChangeEvent) => {
onChange?.(item, e.target.checked);
};

return (
<div className={styles['code-config-item']} onClick={() => onClick?.(item)}>
<div
id={`code-config-item-${item.id}`}
className={classNames(styles['code-config-item'], {
[styles['code-config-item--active']]: checked,
})}
>
<Flex justify="space-between" align="center" style={{ marginBottom: '15px' }}>
<Typography.Paragraph
className={styles['code-config-item__name']}
ellipsis={{ tooltip: item.code_repo_name }}
<Checkbox
className={styles['code-config-item__checkbox']}
checked={checked}
onChange={handleChange}
>
{item.code_repo_name}
</Typography.Paragraph>
<Typography.Paragraph
className={styles['code-config-item__name']}
ellipsis={{ tooltip: item.code_repo_name }}
>
{item.code_repo_name}
</Typography.Paragraph>
</Checkbox>
<div
className={classNames(
styles['code-config-item__tag'],
@@ -35,9 +61,10 @@ function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
className={styles['code-config-item__url']}
ellipsis={{
rows: 2,
tooltip: isEllipsis ? item.git_url : false, // 仅当省略时显示 tooltip
onEllipsis: (ellipsis) => setIsEllipsis(ellipsis),
tooltip: isEllipsis ? item.git_url : false,
onEllipsis: (ellipsis) => setIsEllipsis(ellipsis), // 必须这样,不然不能省略
}}
onClick={openProject}
>
{item.git_url}
</Typography.Paragraph>


+ 39
- 3
react-ui/src/components/CodeSelect/index.tsx View File

@@ -18,7 +18,19 @@ export {
type ParameterInputValue,
} from '../ParameterInput';

type CodeSelectProps = ParameterInputProps;
export type CodeSelectProps = ParameterInputProps;

// 服务的需要的代码配置数据格式
export type ServerCodeData = {
id: number;
name: string;
code_path: string;
branch: string;
username: string;
password: string;
ssh_private_key: string;
is_public: boolean;
};

/** 代码配置选择表单组件 */
function CodeSelect({
@@ -32,11 +44,34 @@ function CodeSelect({
}: CodeSelectProps) {
// 选择代码配置
const selectResource = () => {
const codeData = value as ServerCodeData;
const defaultSelected =
value && typeof value === 'object'
? {
id: codeData.id,
code_repo_name: codeData.name,
git_url: codeData.code_path,
git_branch: codeData.branch,
git_user_name: codeData.username,
git_password: codeData.password,
ssh_key: codeData.ssh_private_key,
is_public: codeData.is_public,
}
: undefined;
const { close } = openAntdModal(CodeSelectorModal, {
defaultSelected: defaultSelected,
onOk: (res) => {
if (res) {
const { id, code_repo_name, git_url, git_branch, git_user_name, git_password, ssh_key } =
res;
const {
id,
code_repo_name,
git_url,
git_branch,
git_user_name,
git_password,
ssh_key,
is_public,
} = res;
const jsonObj = {
id,
name: code_repo_name,
@@ -45,6 +80,7 @@ function CodeSelect({
username: git_user_name,
password: git_password,
ssh_private_key: ssh_key,
is_public,
};
const jsonObjStr = JSON.stringify(jsonObj);
onChange?.({


+ 1
- 0
react-ui/src/components/CodeSelectorModal/index.less View File

@@ -17,6 +17,7 @@
margin-bottom: 30px;
overflow-x: hidden;
overflow-y: auto;
padding-bottom: 10px;
}

&__empty {


+ 76
- 15
react-ui/src/components/CodeSelectorModal/index.tsx View File

@@ -7,7 +7,8 @@
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { type CodeConfigData } from '@/pages/CodeConfig/List';
import { getCodeConfigListReq } from '@/services/codeConfig';
import { getCodeConfigListReq, getCodeConfigPageNumReq } from '@/services/codeConfig';
import { CustomPartial } from '@/types';
import { to } from '@/utils/promise';
import type { ModalProps, PaginationProps } from 'antd';
import { Empty, Input, Pagination } from 'antd';
@@ -17,24 +18,68 @@ import './index.less';

export { type CodeConfigData };

export type SelectCodeData = CustomPartial<
CodeConfigData,
| 'id'
| 'code_repo_name'
| 'git_url'
| 'git_branch'
| 'git_user_name'
| 'git_password'
| 'ssh_key'
| 'is_public'
>;

export interface CodeSelectorModalProps extends Omit<ModalProps, 'onOk'> {
onOk?: (params: CodeConfigData | undefined) => void;
defaultSelected?: SelectCodeData;
onOk?: (params: SelectCodeData | undefined) => void;
}

/** 选择代码配置的弹窗,推荐使用函数的方式打开 */
function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
function CodeSelectorModal({ defaultSelected, onOk, ...rest }: CodeSelectorModalProps) {
const defaultPageSize = 10;
const [dataList, setDataList] = useState<CodeConfigData[]>([]);
const [total, setTotal] = useState(0);
const [pagination, setPagination] = useState<PaginationProps>({
current: 1,
pageSize: 20,
});
const [searchText, setSearchText] = useState<string | undefined>(undefined);
const [inputText, setInputText] = useState<string | undefined>(undefined);
const [selected, setSelected] = useState(defaultSelected);
const [isScrolled, setIsScrolled] = useState(false);
const [pagination, setPagination] = useState<PaginationProps>({
current: defaultSelected?.id ? 0 : 1, // 为 0 时,不请求,等待接口返回选中的代码配置在第几页
pageSize: defaultPageSize,
});

useEffect(() => {
const getCodeConfigPageNum = async (id: number, size: number) => {
const [res] = await to(
getCodeConfigPageNumReq(id, {
size,
}),
);
if (res) {
setPagination({
current: typeof res.data === 'number' ? Math.max(0, res.data) + 1 : 1,
pageSize: defaultPageSize,
});
} else {
setPagination({
current: 1,
pageSize: defaultPageSize,
});
}
};
if (defaultSelected?.id) {
getCodeConfigPageNum(defaultSelected?.id, defaultPageSize);
}
}, [defaultSelected?.id]);

useEffect(() => {
// 获取数据请求
const getDataList = async () => {
// 为 0 时,不请求,等待接口返回选中的代码配置在第几页
if (pagination.current === 0) {
return;
}
const params = {
page: pagination.current! - 1,
size: pagination.pageSize,
@@ -50,6 +95,16 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
getDataList();
}, [pagination, searchText]);

useEffect(() => {
if (dataList.length > 0 && !isScrolled && defaultSelected?.id) {
const selectedItem = document.getElementById(`code-config-item-${defaultSelected?.id}`);
if (selectedItem) {
selectedItem.scrollIntoView();
}
setIsScrolled(true);
}
}, [isScrolled, dataList, defaultSelected?.id]);

// 搜索
const handleSearch = (value: string) => {
setSearchText(value);
@@ -59,8 +114,12 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
}));
};

const handleClick = (item: CodeConfigData) => {
onOk?.(item);
const handleChange = (item: CodeConfigData, checked: boolean) => {
if (checked) {
setSelected(item);
} else {
setSelected(undefined);
}
};

// 分页切换
@@ -77,7 +136,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
title="选择代码配置"
image={require('@/assets/img/modal-code-config.png')}
width={920}
footer={null}
onOk={() => onOk?.(selected)}
destroyOnClose
>
<div className="kf-code-selector-modal">
@@ -93,19 +152,21 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
prefix={
<KFIcon type="icon-sousuo" color="rgba(22,100,255,0.4" style={{ marginLeft: '10px' }} />
}
// prefix={
// <Icon icon="local:magnifying-glass" style={{ marginLeft: '10px', marginTop: '2px' }} />
// }
/>
{dataList?.length !== 0 ? (
<>
<div className="kf-code-selector-modal__content">
{dataList?.map((item) => (
<CodeConfigItem item={item} key={item.id} onClick={handleClick} />
<CodeConfigItem
item={item}
key={item.id}
checked={item.id === selected?.id}
onChange={handleChange}
/>
))}
</div>
<Pagination
align="center"
align="end"
total={total}
showSizeChanger
defaultPageSize={20}


+ 1
- 0
react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less View File

@@ -89,6 +89,7 @@
margin-bottom: 15px !important;
color: @text-color;
font-size: 14px;
word-break: break-all;
}

&__branch {


+ 28
- 2
react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx View File

@@ -1,3 +1,4 @@
import { type ServerCodeData } from '@/components/CodeSelect';
import CodeSelectorModal from '@/components/CodeSelectorModal';
import KFIcon from '@/components/KFIcon';
import ParameterInput, { requiredValidator } from '@/components/ParameterInput';
@@ -15,6 +16,7 @@ import {
PipelineNodeModelParameter,
PipelineNodeModelSerialize,
} from '@/types';
import { parseJsonText } from '@/utils';
import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { INode } from '@antv/g6';
@@ -155,11 +157,34 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
formItemName: NamePath,
item: PipelineNodeModelParameter | Pick<PipelineNodeModelParameter, 'item_type'>,
) => {
const jsonValue = form.getFieldValue(formItemName)?.value;
const fieldValue = parseJsonText(jsonValue) as ServerCodeData;
const defaultSelected = fieldValue
? {
id: fieldValue.id,
code_repo_name: fieldValue.name,
git_url: fieldValue.code_path,
git_branch: fieldValue.branch,
git_user_name: fieldValue.username,
git_password: fieldValue.password,
ssh_key: fieldValue.ssh_private_key,
is_public: fieldValue.is_public,
}
: undefined;
const { close } = openAntdModal(CodeSelectorModal, {
defaultSelected,
onOk: (res) => {
if (res) {
const { id, code_repo_name, git_url, git_branch, git_user_name, git_password, ssh_key } =
res;
const {
id,
code_repo_name,
git_url,
git_branch,
git_user_name,
git_password,
ssh_key,
is_public,
} = res;
const value = JSON.stringify({
id,
name: code_repo_name,
@@ -168,6 +193,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
username: git_user_name,
password: git_password,
ssh_private_key: ssh_key,
is_public,
});
form.setFieldValue(formItemName, {
...item,


+ 9
- 0
react-ui/src/services/codeConfig/index.js View File

@@ -37,3 +37,12 @@ export function getCodeConfigDetailReq(id) {
method: 'GET',
});
}

// 获取代码配置项在第几页
export function getCodeConfigPageNumReq(id, params) {
return request(`/api/mmp/codeConfig/getPageNum/${id}`, {
method: 'GET',
params
});
}


+ 3
- 0
react-ui/src/types.ts View File

@@ -151,3 +151,6 @@ export type UploadFileRes = {
fileSize: number;
url: string;
};

// 定义一个类型,取一个类型的有些字段必须的,其它的是可选
export type CustomPartial<T, K extends keyof T> = Pick<T, K> & Partial<Omit<T, K>>;

Loading…
Cancel
Save