diff --git a/react-ui/config/routes.ts b/react-ui/config/routes.ts index 8838c75a..3caca59f 100644 --- a/react-ui/config/routes.ts +++ b/react-ui/config/routes.ts @@ -360,6 +360,16 @@ export default [ path: 'role-auth/user/:id', component: './System/Role/authUser', }, + { + name: '日志', + path: 'log', + routes: [ + { + path: '', + redirect: '/system/log/operlog', + }, + ], + }, ], }, { diff --git a/react-ui/src/pages/CodeConfig/List/index.tsx b/react-ui/src/pages/CodeConfig/List/index.tsx index b5aec9be..316f5925 100644 --- a/react-ui/src/pages/CodeConfig/List/index.tsx +++ b/react-ui/src/pages/CodeConfig/List/index.tsx @@ -84,7 +84,7 @@ function CodeConfigList() { }; // 修改 - const handleClick = (record: CodeConfigData) => { + const handleEdit = (record: CodeConfigData) => { const { close } = openAntdModal(AddCodeConfigModal, { opType: OperationType.Update, codeConfigData: record, @@ -147,7 +147,7 @@ function CodeConfigList() { item={item} key={item.id} onRemove={handleRemove} - onClick={handleClick} + onEdit={handleEdit} /> ))} diff --git a/react-ui/src/pages/CodeConfig/components/AddCodeConfigModal/index.tsx b/react-ui/src/pages/CodeConfig/components/AddCodeConfigModal/index.tsx index 975e4f2e..ce4deafd 100644 --- a/react-ui/src/pages/CodeConfig/components/AddCodeConfigModal/index.tsx +++ b/react-ui/src/pages/CodeConfig/components/AddCodeConfigModal/index.tsx @@ -3,8 +3,9 @@ import { AvailableRange } from '@/enums'; import { type CodeConfigData } from '@/pages/CodeConfig/List'; import { addCodeConfigReq, updateCodeConfigReq } from '@/services/codeConfig'; import { to } from '@/utils/promise'; -import { Form, Input, Radio, message, type ModalProps } from 'antd'; +import { Form, Input, Radio, message, type FormRule, type ModalProps } from 'antd'; import { omit } from 'lodash'; +import { useMemo } from 'react'; export enum VerifyMode { Password = 0, // 用户名密码 @@ -25,7 +26,32 @@ interface AddCodeConfigModalProps extends Omit { } function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeConfigModalProps) { - // 上传请求 + const [form] = Form.useForm(); + const isPublic = Form.useWatch('code_repo_vis', form) === AvailableRange.Public; + + const urlExample = useMemo( + () => + isPublic + ? 'https://gitlink.org.cn/ci4s/ci4sManagement-cloud.git' + : 'git@code.gitlink.org.cn:ci4s/ci4sManagement-cloud.git', + [isPublic], + ); + + // /^(git@[\w.-]+:[\w./-]+\.git)$/ + const urlRules: FormRule[] = useMemo( + () => + isPublic + ? [ + { + type: 'url', + message: '请输入正确的 Git 地址', + }, + ] + : ([] as FormRule[]), + [isPublic], + ); + + // 创建 const createCodeConfig = async (formData: FormData) => { const params: FormData & { id?: number } = { ...formData, @@ -78,14 +104,12 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo >
- {/* 禁止 Chrome 自动填充 */} - {/* - */} - + diff --git a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less index 42d054ab..09543c19 100644 --- a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less +++ b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less @@ -22,8 +22,15 @@ font-size: 16px; } + &__tag { + padding: 4px; + color: @primary-color; + font-size: 12px; + background-color: .addAlpha(@primary-color, 0.1) []; + border-radius: 4px; + } + &__url { - margin-bottom: 10px; color: @text-color-secondary; font-size: 14px; } diff --git a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx index 237719af..e20f76a8 100644 --- a/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx +++ b/react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx @@ -1,6 +1,7 @@ import clock from '@/assets/img/clock.png'; import creatByImg from '@/assets/img/creatBy.png'; import KFIcon from '@/components/KFIcon'; +import { AvailableRange } from '@/enums'; import { type CodeConfigData } from '@/pages/CodeConfig/List'; import { formatDate } from '@/utils/date'; import { Button, Flex, Typography } from 'antd'; @@ -8,13 +9,14 @@ import styles from './index.less'; type CodeConfigItemProps = { item: CodeConfigData; - onClick: (item: CodeConfigData) => void; - onRemove: (item: CodeConfigData) => void; + onClick?: (item: CodeConfigData) => void; + onEdit?: (item: CodeConfigData) => void; + onRemove?: (item: CodeConfigData) => void; }; -function CodeConfigItem({ item, onClick, onRemove }: CodeConfigItemProps) { +function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps) { return ( -
onClick(item)}> +
onClick?.(item)}> {item.code_repo_name} +
+ {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} +
+ +
-
{item.git_url}
+ + {item.git_url} + +
+ {item.git_branch} +
diff --git a/react-ui/src/pages/Dataset/components/ResourceItem/index.less b/react-ui/src/pages/Dataset/components/ResourceItem/index.less new file mode 100644 index 00000000..edd97f85 --- /dev/null +++ b/react-ui/src/pages/Dataset/components/ResourceItem/index.less @@ -0,0 +1,61 @@ +.resource-item { + position: relative; + width: calc(25% - 15px); + padding: 20px; + background: white; + border: 1px solid #eaeaea; + border-radius: 4px; + cursor: pointer; + + @media screen and (max-width: 1860px) { + & { + width: calc(33.33% - 13.33px); + } + } + + &__name { + position: relative; + display: inline-block; + height: 24px; + margin: 0 10px 0 0 !important; + color: @text-color; + font-size: 16px; + } + + &__description { + height: 44px; + margin-bottom: 20px; + color: @text-color-secondary; + font-size: 14px; + .multiLine(2); + } + &__time { + display: flex; + flex: 0 1 content; + align-items: center; + width: 100%; + color: #808080; + font-size: 13px; + } + + &:hover { + border-color: @primary-color; + box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1); + + .resource-item__name { + color: @primary-color; + } + } +} + +.resource-item__name { + &::after { + position: absolute; + top: 14px; + left: 0; + width: 100%; + height: 6px; + background: linear-gradient(to right, rgba(22, 100, 255, 0.3) 0, rgba(22, 100, 255, 0) 100%); + content: ''; + } +} diff --git a/react-ui/src/pages/Dataset/components/ResourceItem/index.tsx b/react-ui/src/pages/Dataset/components/ResourceItem/index.tsx new file mode 100644 index 00000000..3b517184 --- /dev/null +++ b/react-ui/src/pages/Dataset/components/ResourceItem/index.tsx @@ -0,0 +1,54 @@ +import clock from '@/assets/img/clock.png'; +import creatByImg from '@/assets/img/creatBy.png'; +import KFIcon from '@/components/KFIcon'; +import { formatDate } from '@/utils/date'; +import { Button, Flex, Typography } from 'antd'; +import { ResourceData } from '../../config'; +import styles from './index.less'; + +type ResourceItemProps = { + item: ResourceData; + isPublic: boolean; + onRemove: (item: ResourceData) => void; + onClick: (item: ResourceData) => void; +}; + +function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) { + return ( +
onClick(item)}> + + + {item.name} + + {!isPublic && ( + + )} + +
{item.description}
+ +
+ + {item.create_by} +
+
+ + 最近更新: {formatDate(item.update_time, 'YYYY-MM-DD')} +
+
+
+ ); +} + +export default ResourceItem; diff --git a/react-ui/src/pages/Monitor/JobLog/detail.tsx b/react-ui/src/pages/Monitor/JobLog/detail.tsx index d6b25913..10cac4e4 100644 --- a/react-ui/src/pages/Monitor/JobLog/detail.tsx +++ b/react-ui/src/pages/Monitor/JobLog/detail.tsx @@ -28,7 +28,7 @@ const JobLogDetailForm: React.FC = (props) => { return ( void; +}; + +function CodeConfigItem({ item, onClick }: CodeConfigItemProps) { + return ( +
onClick?.(item)}> + + + {item.code_repo_name} + +
+ {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} +
+
+ + {item.git_url} + +
+ {item.git_branch} +
+
+ ); +} + +export default CodeConfigItem; diff --git a/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less new file mode 100644 index 00000000..8206638a --- /dev/null +++ b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less @@ -0,0 +1,29 @@ +.code-selector { + width: 100%; + height: 100%; + + :global { + .ant-input-affix-wrapper .ant-input-prefix { + margin-inline-end: 12px; + } + + .ant-pagination { + text-align: right; + } + + .ant-input-group-addon { + display: none; + } + } + + &__content { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 20px; + width: 100%; + margin-top: 30px; + margin-bottom: 30px; + overflow-x: hidden; + overflow-y: auto; + } +} diff --git a/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx new file mode 100644 index 00000000..82d3fbf8 --- /dev/null +++ b/react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx @@ -0,0 +1,114 @@ +/* + * @Author: 赵伟 + * @Date: 2024-04-11 16:31:18 + * @Description: 选择代码 + */ + +import KFModal from '@/components/KFModal'; +import { type CodeConfigData } from '@/pages/CodeConfig/List'; +import { getCodeConfigListReq } from '@/services/codeConfig'; +import { to } from '@/utils/promise'; +import type { ModalProps, PaginationProps } from 'antd'; +import { Empty, Input, Pagination } from 'antd'; +import { useEffect, useState } from 'react'; +import CodeConfigItem from '../CodeConfigItem'; +import styles from './index.less'; + +export interface CodeSelectorModalProps extends Omit { + onOk?: (params: CodeConfigData | undefined) => void; +} + +function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { + const [dataList, setDataList] = useState([]); + const [total, setTotal] = useState(0); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 20, + }); + const [searchText, setSearchText] = useState(undefined); + const [inputText, setInputText] = useState(undefined); + + useEffect(() => { + getDataList(); + }, [pagination, searchText]); + + // 获取数据请求 + const getDataList = async () => { + const params = { + page: pagination.current! - 1, + size: pagination.pageSize, + code_repo_name: searchText !== '' ? searchText : undefined, + }; + const [res] = await to(getCodeConfigListReq(params)); + if (res && res.data && res.data.content) { + setDataList(res.data.content); + setTotal(res.data.totalElements); + } + }; + + // 搜索 + const handleSearch = (value: string) => { + setSearchText(value); + }; + + const handleClick = (item: CodeConfigData) => { + onOk?.(item); + }; + + // 分页切换 + const handlePageChange: PaginationProps['onChange'] = (page, pageSize) => { + setPagination({ + current: page, + pageSize: pageSize, + }); + }; + + return ( + +
+ setInputText(e.target.value)} + suffix={null} + value={inputText} + /> + {dataList?.length !== 0 ? ( + <> +
+ {dataList?.map((item) => ( + + ))} +
+ + + ) : ( +
+ +
+ )} +
+
+ ); +} + +export default CodeSelectorModal; diff --git a/react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx b/react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx index 362fcc0e..12ef2dfa 100644 --- a/react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx +++ b/react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx @@ -17,6 +17,7 @@ import { INode } from '@antv/g6'; import { Button, Drawer, Form, Input, MenuProps, Select } from 'antd'; import { NamePath } from 'antd/es/form/interface'; import { forwardRef, useImperativeHandle, useState } from 'react'; +import CodeSelectorModal from '../CodeSelectorModal'; import PropsLabel from '../PropsLabel'; import ResourceSelectorModal, { ResourceSelectorType, @@ -57,7 +58,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete formError: !!error, }; - // console.log('res', res); + console.log('res', res); onFormChange(res); } }; @@ -79,7 +80,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete out_parameters: JSON.parse(model.out_parameters), control_strategy: JSON.parse(model.control_strategy), }; - // console.log('model', nodeData); + console.log('model', nodeData); setStagingItem({ ...nodeData, }); @@ -115,6 +116,48 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete }, })); + // ref 类型选择 + const selectRefData = ( + formItemName: NamePath, + item: PipelineNodeModelParameter | Pick, + ) => { + if (item.item_type === 'code') { + selectCodeConfig(formItemName, item); + } else { + selectResource(formItemName, item); + } + }; + + // 选择代码配置 + const selectCodeConfig = ( + formItemName: NamePath, + item: PipelineNodeModelParameter | Pick, + ) => { + const { close } = openAntdModal(CodeSelectorModal, { + onOk: (res) => { + if (res) { + console.log('res', res); + const value = JSON.stringify({ + id: res.id, + name: res.code_repo_name, + code_path: res.git_url, + branch: res.git_branch, + username: res.git_user_name, + password: res.git_password, + ssh_private_key: res.ssh_key, + }); + form.setFieldValue(formItemName, { + ...item, + value, + showValue: res.code_repo_name, + fromSelect: true, + }); + } + close(); + }, + }); + }; + // 选择数据集、模型、镜像 const selectResource = ( formItemName: NamePath, @@ -146,8 +189,10 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete if (type === ResourceSelectorType.Mirror) { const { activeTab, id, version, path } = res; if (formItemName === 'image') { + // 单独的选择镜像 form.setFieldValue(formItemName, path); } else { + // 输入参数选择镜像 form.setFieldValue(formItemName, { ...item, value: path, @@ -160,12 +205,11 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete } } else { const { activeTab, id, name, version, path } = res; - const jsonObj = { + const value = JSON.stringify({ id, version, path, - }; - const value = JSON.stringify(jsonObj); + }); const showValue = `${name}:${version}`; form.setFieldValue(formItemName, { ...item, @@ -467,7 +511,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete size="small" type="link" icon={getSelectBtnIcon(item.value)} - onClick={() => selectResource(['in_parameters', item.key], item.value)} + onClick={() => selectRefData(['in_parameters', item.key], item.value)} className={styles['pipeline-drawer__ref-row__select-button']} > {item.value.label} diff --git a/react-ui/src/pages/System/Config/edit.tsx b/react-ui/src/pages/System/Config/edit.tsx index 97048e26..daa6e24e 100644 --- a/react-ui/src/pages/System/Config/edit.tsx +++ b/react-ui/src/pages/System/Config/edit.tsx @@ -22,8 +22,11 @@ export type ConfigFormProps = { const ConfigForm: React.FC = (props) => { const [form] = Form.useForm(); - const { configTypeOptions } = props; + const formLayout = { + labelCol: { span: 4 }, + wrapperCol: { span: 20 }, + }; useEffect(() => { form.resetFields(); @@ -32,7 +35,7 @@ const ConfigForm: React.FC = (props) => { configName: props.values.configName, configKey: props.values.configKey, configValue: props.values.configValue, - configType: props.values.configType, + configType: props.values.configType || Object.keys(configTypeOptions)[0], createBy: props.values.createBy, createTime: props.values.createTime, updateBy: props.values.updateBy, @@ -54,7 +57,7 @@ const ConfigForm: React.FC = (props) => { return ( = (props) => { submitter={false} layout="horizontal" onFinish={handleFinish} + {...formLayout} + size="large" + labelAlign="right" + autoComplete="off" > { handleRefreshCache(); }} > - + {' '} , ]} diff --git a/react-ui/src/pages/System/DictData/edit.tsx b/react-ui/src/pages/System/DictData/edit.tsx index 8f03931a..deef59d6 100644 --- a/react-ui/src/pages/System/DictData/edit.tsx +++ b/react-ui/src/pages/System/DictData/edit.tsx @@ -23,8 +23,11 @@ export type DataFormProps = { const DictDataForm: React.FC = (props) => { const [form] = Form.useForm(); - const { statusOptions } = props; + const formLayout = { + labelCol: { span: 4 }, + wrapperCol: { span: 20 }, + }; useEffect(() => { form.resetFields(); @@ -36,8 +39,8 @@ const DictDataForm: React.FC = (props) => { dictType: props.values.dictType, cssClass: props.values.cssClass, listClass: props.values.listClass, - isDefault: props.values.isDefault, - status: props.values.status, + isDefault: props.values.isDefault || 'N', + status: props.values.status || Object.keys(statusOptions)[0], createBy: props.values.createBy, createTime: props.values.createTime, updateBy: props.values.updateBy, @@ -59,7 +62,7 @@ const DictDataForm: React.FC = (props) => { return ( = (props) => { submitter={false} layout="horizontal" onFinish={handleFinish} + {...formLayout} + size="large" + labelAlign="right" + autoComplete="off" > = (props) => { id: 'system.dict.data.dict_sort', defaultMessage: '字典排序', })} - colProps={{ md: 12, xl: 12 }} + colProps={{ md: 12, xl: 24 }} placeholder="请输入字典排序" rules={[ { @@ -203,7 +210,6 @@ const DictDataForm: React.FC = (props) => { Y: '是', N: '否', }} - initialValue={'N'} colProps={{ md: 12, xl: 24 }} placeholder="请输入是否默认" rules={[ @@ -220,7 +226,6 @@ const DictDataForm: React.FC = (props) => { id: 'system.dict.data.status', defaultMessage: '状态', })} - initialValue={'0'} colProps={{ md: 12, xl: 24 }} placeholder="请输入状态" rules={[ diff --git a/react-ui/src/pages/System/Logininfor/edit.tsx b/react-ui/src/pages/System/Logininfor/edit.tsx index 6248b376..5f7aa0a2 100644 --- a/react-ui/src/pages/System/Logininfor/edit.tsx +++ b/react-ui/src/pages/System/Logininfor/edit.tsx @@ -22,8 +22,11 @@ export type LogininforFormProps = { const LogininforForm: React.FC = (props) => { const [form] = Form.useForm(); - const { statusOptions } = props; + const formLayout = { + labelCol: { span: 4 }, + wrapperCol: { span: 20 }, + }; useEffect(() => { form.resetFields(); @@ -54,7 +57,7 @@ const LogininforForm: React.FC = (props) => { return ( = (props) => { onOk={handleOk} onCancel={handleCancel} > - + = (props) => { const [form] = Form.useForm(); - const { noticeTypeOptions, statusOptions } = props; + const formLayout = { + labelCol: { span: 4 }, + wrapperCol: { span: 20 }, + }; useEffect(() => { form.resetFields(); @@ -34,7 +37,7 @@ const NoticeForm: React.FC = (props) => { noticeTitle: props.values.noticeTitle, noticeType: props.values.noticeType, noticeContent: props.values.noticeContent, - status: props.values.status, + status: props.values.status || Object.keys(statusOptions)[0], createBy: props.values.createBy, createTime: props.values.createTime, updateBy: props.values.updateBy, @@ -56,7 +59,7 @@ const NoticeForm: React.FC = (props) => { return ( = (props) => { submitter={false} layout="horizontal" onFinish={handleFinish} + {...formLayout} + size="large" + labelAlign="right" + autoComplete="off" > = (props) => { return ( { tableAlertRender={false} tableAlertOptionRender={false} toolBarRender={() => [ - , + // ,