| @@ -360,6 +360,16 @@ export default [ | |||||
| path: 'role-auth/user/:id', | path: 'role-auth/user/:id', | ||||
| component: './System/Role/authUser', | component: './System/Role/authUser', | ||||
| }, | }, | ||||
| { | |||||
| name: '日志', | |||||
| path: 'log', | |||||
| routes: [ | |||||
| { | |||||
| path: '', | |||||
| redirect: '/system/log/operlog', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -84,7 +84,7 @@ function CodeConfigList() { | |||||
| }; | }; | ||||
| // 修改 | // 修改 | ||||
| const handleClick = (record: CodeConfigData) => { | |||||
| const handleEdit = (record: CodeConfigData) => { | |||||
| const { close } = openAntdModal(AddCodeConfigModal, { | const { close } = openAntdModal(AddCodeConfigModal, { | ||||
| opType: OperationType.Update, | opType: OperationType.Update, | ||||
| codeConfigData: record, | codeConfigData: record, | ||||
| @@ -147,7 +147,7 @@ function CodeConfigList() { | |||||
| item={item} | item={item} | ||||
| key={item.id} | key={item.id} | ||||
| onRemove={handleRemove} | onRemove={handleRemove} | ||||
| onClick={handleClick} | |||||
| onEdit={handleEdit} | |||||
| /> | /> | ||||
| ))} | ))} | ||||
| </div> | </div> | ||||
| @@ -3,8 +3,9 @@ import { AvailableRange } from '@/enums'; | |||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | import { type CodeConfigData } from '@/pages/CodeConfig/List'; | ||||
| import { addCodeConfigReq, updateCodeConfigReq } from '@/services/codeConfig'; | import { addCodeConfigReq, updateCodeConfigReq } from '@/services/codeConfig'; | ||||
| import { to } from '@/utils/promise'; | 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 { omit } from 'lodash'; | ||||
| import { useMemo } from 'react'; | |||||
| export enum VerifyMode { | export enum VerifyMode { | ||||
| Password = 0, // 用户名密码 | Password = 0, // 用户名密码 | ||||
| @@ -25,7 +26,32 @@ interface AddCodeConfigModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| } | } | ||||
| function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeConfigModalProps) { | 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 createCodeConfig = async (formData: FormData) => { | ||||
| const params: FormData & { id?: number } = { | const params: FormData & { id?: number } = { | ||||
| ...formData, | ...formData, | ||||
| @@ -78,14 +104,12 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| > | > | ||||
| <Form | <Form | ||||
| name="form" | name="form" | ||||
| form={form} | |||||
| layout="vertical" | layout="vertical" | ||||
| onFinish={onFinish} | onFinish={onFinish} | ||||
| initialValues={initialValues} | initialValues={initialValues} | ||||
| autoComplete="off" | autoComplete="off" | ||||
| > | > | ||||
| {/* 禁止 Chrome 自动填充 */} | |||||
| {/* <Input type="text" style={{ display: 'none' }} /> | |||||
| <Input type="password" style={{ display: 'none' }} /> */} | |||||
| <Form.Item | <Form.Item | ||||
| label="代码仓库名称" | label="代码仓库名称" | ||||
| name="code_repo_name" | name="code_repo_name" | ||||
| @@ -122,13 +146,15 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| required: true, | required: true, | ||||
| message: '请输入 Git 地址', | message: '请输入 Git 地址', | ||||
| }, | }, | ||||
| { | |||||
| type: 'url', | |||||
| message: '请输入正确的 Git 地址', | |||||
| }, | |||||
| ...urlRules, | |||||
| ]} | ]} | ||||
| > | > | ||||
| <Input placeholder="请输入 Git 地址" showCount allowClear maxLength={256} /> | |||||
| <Input | |||||
| placeholder={`请输入 Git 地址,如: ${urlExample}`} | |||||
| showCount | |||||
| allowClear | |||||
| maxLength={256} | |||||
| /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| label="代码分支/Tag" | label="代码分支/Tag" | ||||
| @@ -224,8 +250,8 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo | |||||
| <Input.TextArea | <Input.TextArea | ||||
| placeholder="请输入 SSH Key" | placeholder="请输入 SSH Key" | ||||
| showCount | showCount | ||||
| maxLength={1024} | |||||
| autoSize={{ minRows: 3, maxRows: 6 }} | |||||
| maxLength={4096} | |||||
| autoSize={{ minRows: 4, maxRows: 8 }} | |||||
| allowClear | allowClear | ||||
| /> | /> | ||||
| </Form.Item> | </Form.Item> | ||||
| @@ -22,8 +22,15 @@ | |||||
| font-size: 16px; | font-size: 16px; | ||||
| } | } | ||||
| &__tag { | |||||
| padding: 4px; | |||||
| color: @primary-color; | |||||
| font-size: 12px; | |||||
| background-color: .addAlpha(@primary-color, 0.1) []; | |||||
| border-radius: 4px; | |||||
| } | |||||
| &__url { | &__url { | ||||
| margin-bottom: 10px; | |||||
| color: @text-color-secondary; | color: @text-color-secondary; | ||||
| font-size: 14px; | font-size: 14px; | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| import clock from '@/assets/img/clock.png'; | import clock from '@/assets/img/clock.png'; | ||||
| import creatByImg from '@/assets/img/creatBy.png'; | import creatByImg from '@/assets/img/creatBy.png'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { AvailableRange } from '@/enums'; | |||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | import { type CodeConfigData } from '@/pages/CodeConfig/List'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { Button, Flex, Typography } from 'antd'; | import { Button, Flex, Typography } from 'antd'; | ||||
| @@ -8,13 +9,14 @@ import styles from './index.less'; | |||||
| type CodeConfigItemProps = { | type CodeConfigItemProps = { | ||||
| item: CodeConfigData; | 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 ( | return ( | ||||
| <div className={styles['code-config-item']} onClick={() => onClick(item)}> | |||||
| <div className={styles['code-config-item']} onClick={() => onClick?.(item)}> | |||||
| <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | ||||
| <Typography.Paragraph | <Typography.Paragraph | ||||
| className={styles['code-config-item__name']} | className={styles['code-config-item__name']} | ||||
| @@ -22,18 +24,43 @@ function CodeConfigItem({ item, onClick, onRemove }: CodeConfigItemProps) { | |||||
| > | > | ||||
| {item.code_repo_name} | {item.code_repo_name} | ||||
| </Typography.Paragraph> | </Typography.Paragraph> | ||||
| <div className={styles['code-config-item__tag']}> | |||||
| {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | |||||
| </div> | |||||
| <Button | |||||
| type="text" | |||||
| shape="circle" | |||||
| style={{ marginLeft: 'auto', marginRight: '4px' }} | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| onEdit?.(item); | |||||
| }} | |||||
| > | |||||
| <KFIcon type="icon-bianji" font={17} /> | |||||
| </Button> | |||||
| <Button | <Button | ||||
| type="text" | type="text" | ||||
| shape="circle" | shape="circle" | ||||
| style={{ marginRight: '-4px' }} | |||||
| onClick={(e) => { | onClick={(e) => { | ||||
| e.stopPropagation(); | e.stopPropagation(); | ||||
| onRemove(item); | |||||
| onRemove?.(item); | |||||
| }} | }} | ||||
| > | > | ||||
| <KFIcon type="icon-shanchu" font={17} /> | <KFIcon type="icon-shanchu" font={17} /> | ||||
| </Button> | </Button> | ||||
| </Flex> | </Flex> | ||||
| <div className={styles['code-config-item__description']}>{item.git_url}</div> | |||||
| <Typography.Paragraph | |||||
| className={styles['code-config-item__url']} | |||||
| ellipsis={{ tooltip: item.git_url }} | |||||
| style={{ marginBottom: '8px' }} | |||||
| > | |||||
| {item.git_url} | |||||
| </Typography.Paragraph> | |||||
| <div className={styles['code-config-item__url']} style={{ marginBottom: '20px' }}> | |||||
| {item.git_branch} | |||||
| </div> | |||||
| <Flex justify="space-between"> | <Flex justify="space-between"> | ||||
| <div className={styles['code-config-item__time']}> | <div className={styles['code-config-item__time']}> | ||||
| <img style={{ width: '17px', marginRight: '6px' }} src={creatByImg} alt="" /> | <img style={{ width: '17px', marginRight: '6px' }} src={creatByImg} alt="" /> | ||||
| @@ -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: ''; | |||||
| } | |||||
| } | |||||
| @@ -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 ( | |||||
| <div className={styles['resource-item']} onClick={() => onClick(item)}> | |||||
| <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | |||||
| <Typography.Paragraph | |||||
| className={styles['resource-item__name']} | |||||
| ellipsis={{ tooltip: item.name }} | |||||
| > | |||||
| {item.name} | |||||
| </Typography.Paragraph> | |||||
| {!isPublic && ( | |||||
| <Button | |||||
| type="text" | |||||
| shape="circle" | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| onRemove(item); | |||||
| }} | |||||
| > | |||||
| <KFIcon type="icon-shanchu" font={17} /> | |||||
| </Button> | |||||
| )} | |||||
| </Flex> | |||||
| <div className={styles['resource-item__description']}>{item.description}</div> | |||||
| <Flex justify="space-between"> | |||||
| <div className={styles['resource-item__time']}> | |||||
| <img style={{ width: '17px', marginRight: '6px' }} src={creatByImg} alt="" /> | |||||
| <span>{item.create_by}</span> | |||||
| </div> | |||||
| <div className={styles['resource-item__time']}> | |||||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} alt="" /> | |||||
| <span>最近更新: {formatDate(item.update_time, 'YYYY-MM-DD')}</span> | |||||
| </div> | |||||
| </Flex> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ResourceItem; | |||||
| @@ -28,7 +28,7 @@ const JobLogDetailForm: React.FC<JobLogFormProps> = (props) => { | |||||
| return ( | return ( | ||||
| <KFModal | <KFModal | ||||
| width={640} | |||||
| width={680} | |||||
| title={intl.formatMessage({ | title={intl.formatMessage({ | ||||
| id: 'monitor.job.log.title', | id: 'monitor.job.log.title', | ||||
| defaultMessage: '定时任务调度日志', | defaultMessage: '定时任务调度日志', | ||||
| @@ -86,6 +86,9 @@ export function canInput(parameter: PipelineNodeModelParameter) { | |||||
| const { type, item_type } = parameter; | const { type, item_type } = parameter; | ||||
| return !( | return !( | ||||
| type === 'ref' && | type === 'ref' && | ||||
| (item_type === 'dataset' || item_type === 'model' || item_type === 'image') | |||||
| (item_type === 'dataset' || | |||||
| item_type === 'model' || | |||||
| item_type === 'image' || | |||||
| item_type === 'code') | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -0,0 +1,68 @@ | |||||
| .code-config-item { | |||||
| position: relative; | |||||
| padding: 20px; | |||||
| background: white; | |||||
| border: 1px solid #eaeaea; | |||||
| border-radius: 4px; | |||||
| cursor: pointer; | |||||
| &__name { | |||||
| position: relative; | |||||
| display: inline-block; | |||||
| height: 24px; | |||||
| margin: 0 10px 0 0 !important; | |||||
| color: @text-color; | |||||
| font-size: 16px; | |||||
| } | |||||
| &__tag { | |||||
| padding: 4px; | |||||
| color: @primary-color; | |||||
| font-size: 12px; | |||||
| background-color: .addAlpha(@primary-color, 0.1) []; | |||||
| border-radius: 4px; | |||||
| } | |||||
| &__url { | |||||
| color: @text-color-secondary; | |||||
| font-size: 14px; | |||||
| } | |||||
| &__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: ''; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,39 @@ | |||||
| import { AvailableRange } from '@/enums'; | |||||
| import { type CodeConfigData } from '@/pages/CodeConfig/List'; | |||||
| import { Flex, Typography } from 'antd'; | |||||
| import styles from './index.less'; | |||||
| type CodeConfigItemProps = { | |||||
| item: CodeConfigData; | |||||
| onClick?: (item: CodeConfigData) => void; | |||||
| }; | |||||
| function CodeConfigItem({ item, onClick }: CodeConfigItemProps) { | |||||
| return ( | |||||
| <div className={styles['code-config-item']} onClick={() => onClick?.(item)}> | |||||
| <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | |||||
| <Typography.Paragraph | |||||
| className={styles['code-config-item__name']} | |||||
| ellipsis={{ tooltip: item.code_repo_name }} | |||||
| > | |||||
| {item.code_repo_name} | |||||
| </Typography.Paragraph> | |||||
| <div className={styles['code-config-item__tag']}> | |||||
| {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'} | |||||
| </div> | |||||
| </Flex> | |||||
| <Typography.Paragraph | |||||
| className={styles['code-config-item__url']} | |||||
| ellipsis={{ tooltip: item.git_url }} | |||||
| style={{ marginBottom: '8px' }} | |||||
| > | |||||
| {item.git_url} | |||||
| </Typography.Paragraph> | |||||
| <div className={styles['code-config-item__url']} style={{ marginBottom: '20px' }}> | |||||
| {item.git_branch} | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default CodeConfigItem; | |||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| @@ -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<ModalProps, 'onOk'> { | |||||
| onOk?: (params: CodeConfigData | undefined) => void; | |||||
| } | |||||
| function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||||
| 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); | |||||
| 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 ( | |||||
| <KFModal | |||||
| {...rest} | |||||
| title="选择代码配置" | |||||
| image={require('@/assets/img/edit-experiment.png')} | |||||
| width={920} | |||||
| footer={null} | |||||
| destroyOnClose | |||||
| > | |||||
| <div className={styles['code-selector']}> | |||||
| <Input.Search | |||||
| placeholder="按代码仓库名称筛选" | |||||
| allowClear | |||||
| onSearch={handleSearch} | |||||
| style={{ | |||||
| width: '100%', | |||||
| }} | |||||
| onChange={(e) => setInputText(e.target.value)} | |||||
| suffix={null} | |||||
| value={inputText} | |||||
| /> | |||||
| {dataList?.length !== 0 ? ( | |||||
| <> | |||||
| <div className={styles['code-selector__content']}> | |||||
| {dataList?.map((item) => ( | |||||
| <CodeConfigItem item={item} key={item.id} onClick={handleClick} /> | |||||
| ))} | |||||
| </div> | |||||
| <Pagination | |||||
| total={total} | |||||
| showSizeChanger | |||||
| defaultPageSize={20} | |||||
| pageSizeOptions={[20, 40, 60, 80, 100]} | |||||
| showQuickJumper | |||||
| onChange={handlePageChange} | |||||
| {...pagination} | |||||
| /> | |||||
| </> | |||||
| ) : ( | |||||
| <div className={styles['code-selector__empty']}> | |||||
| <Empty></Empty> | |||||
| </div> | |||||
| )} | |||||
| </div> | |||||
| </KFModal> | |||||
| ); | |||||
| } | |||||
| export default CodeSelectorModal; | |||||
| @@ -17,6 +17,7 @@ import { INode } from '@antv/g6'; | |||||
| import { Button, Drawer, Form, Input, MenuProps, Select } from 'antd'; | import { Button, Drawer, Form, Input, MenuProps, Select } from 'antd'; | ||||
| import { NamePath } from 'antd/es/form/interface'; | import { NamePath } from 'antd/es/form/interface'; | ||||
| import { forwardRef, useImperativeHandle, useState } from 'react'; | import { forwardRef, useImperativeHandle, useState } from 'react'; | ||||
| import CodeSelectorModal from '../CodeSelectorModal'; | |||||
| import PropsLabel from '../PropsLabel'; | import PropsLabel from '../PropsLabel'; | ||||
| import ResourceSelectorModal, { | import ResourceSelectorModal, { | ||||
| ResourceSelectorType, | ResourceSelectorType, | ||||
| @@ -57,7 +58,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| formError: !!error, | formError: !!error, | ||||
| }; | }; | ||||
| // console.log('res', res); | |||||
| console.log('res', res); | |||||
| onFormChange(res); | onFormChange(res); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -79,7 +80,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| out_parameters: JSON.parse(model.out_parameters), | out_parameters: JSON.parse(model.out_parameters), | ||||
| control_strategy: JSON.parse(model.control_strategy), | control_strategy: JSON.parse(model.control_strategy), | ||||
| }; | }; | ||||
| // console.log('model', nodeData); | |||||
| console.log('model', nodeData); | |||||
| setStagingItem({ | setStagingItem({ | ||||
| ...nodeData, | ...nodeData, | ||||
| }); | }); | ||||
| @@ -115,6 +116,48 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| }, | }, | ||||
| })); | })); | ||||
| // ref 类型选择 | |||||
| const selectRefData = ( | |||||
| formItemName: NamePath, | |||||
| item: PipelineNodeModelParameter | Pick<PipelineNodeModelParameter, 'item_type'>, | |||||
| ) => { | |||||
| if (item.item_type === 'code') { | |||||
| selectCodeConfig(formItemName, item); | |||||
| } else { | |||||
| selectResource(formItemName, item); | |||||
| } | |||||
| }; | |||||
| // 选择代码配置 | |||||
| const selectCodeConfig = ( | |||||
| formItemName: NamePath, | |||||
| item: PipelineNodeModelParameter | Pick<PipelineNodeModelParameter, 'item_type'>, | |||||
| ) => { | |||||
| 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 = ( | const selectResource = ( | ||||
| formItemName: NamePath, | formItemName: NamePath, | ||||
| @@ -146,8 +189,10 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| if (type === ResourceSelectorType.Mirror) { | if (type === ResourceSelectorType.Mirror) { | ||||
| const { activeTab, id, version, path } = res; | const { activeTab, id, version, path } = res; | ||||
| if (formItemName === 'image') { | if (formItemName === 'image') { | ||||
| // 单独的选择镜像 | |||||
| form.setFieldValue(formItemName, path); | form.setFieldValue(formItemName, path); | ||||
| } else { | } else { | ||||
| // 输入参数选择镜像 | |||||
| form.setFieldValue(formItemName, { | form.setFieldValue(formItemName, { | ||||
| ...item, | ...item, | ||||
| value: path, | value: path, | ||||
| @@ -160,12 +205,11 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| } | } | ||||
| } else { | } else { | ||||
| const { activeTab, id, name, version, path } = res; | const { activeTab, id, name, version, path } = res; | ||||
| const jsonObj = { | |||||
| const value = JSON.stringify({ | |||||
| id, | id, | ||||
| version, | version, | ||||
| path, | path, | ||||
| }; | |||||
| const value = JSON.stringify(jsonObj); | |||||
| }); | |||||
| const showValue = `${name}:${version}`; | const showValue = `${name}:${version}`; | ||||
| form.setFieldValue(formItemName, { | form.setFieldValue(formItemName, { | ||||
| ...item, | ...item, | ||||
| @@ -467,7 +511,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| size="small" | size="small" | ||||
| type="link" | type="link" | ||||
| icon={getSelectBtnIcon(item.value)} | 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']} | className={styles['pipeline-drawer__ref-row__select-button']} | ||||
| > | > | ||||
| {item.value.label} | {item.value.label} | ||||
| @@ -22,8 +22,11 @@ export type ConfigFormProps = { | |||||
| const ConfigForm: React.FC<ConfigFormProps> = (props) => { | const ConfigForm: React.FC<ConfigFormProps> = (props) => { | ||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const { configTypeOptions } = props; | const { configTypeOptions } = props; | ||||
| const formLayout = { | |||||
| labelCol: { span: 4 }, | |||||
| wrapperCol: { span: 20 }, | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| form.resetFields(); | form.resetFields(); | ||||
| @@ -32,7 +35,7 @@ const ConfigForm: React.FC<ConfigFormProps> = (props) => { | |||||
| configName: props.values.configName, | configName: props.values.configName, | ||||
| configKey: props.values.configKey, | configKey: props.values.configKey, | ||||
| configValue: props.values.configValue, | configValue: props.values.configValue, | ||||
| configType: props.values.configType, | |||||
| configType: props.values.configType || Object.keys(configTypeOptions)[0], | |||||
| createBy: props.values.createBy, | createBy: props.values.createBy, | ||||
| createTime: props.values.createTime, | createTime: props.values.createTime, | ||||
| updateBy: props.values.updateBy, | updateBy: props.values.updateBy, | ||||
| @@ -54,7 +57,7 @@ const ConfigForm: React.FC<ConfigFormProps> = (props) => { | |||||
| return ( | return ( | ||||
| <KFModal | <KFModal | ||||
| width={640} | |||||
| width={680} | |||||
| title={intl.formatMessage({ | title={intl.formatMessage({ | ||||
| id: 'system.config.title', | id: 'system.config.title', | ||||
| defaultMessage: '编辑参数配置', | defaultMessage: '编辑参数配置', | ||||
| @@ -71,6 +74,10 @@ const ConfigForm: React.FC<ConfigFormProps> = (props) => { | |||||
| submitter={false} | submitter={false} | ||||
| layout="horizontal" | layout="horizontal" | ||||
| onFinish={handleFinish} | onFinish={handleFinish} | ||||
| {...formLayout} | |||||
| size="large" | |||||
| labelAlign="right" | |||||
| autoComplete="off" | |||||
| > | > | ||||
| <ProFormDigit | <ProFormDigit | ||||
| name="configId" | name="configId" | ||||
| @@ -331,7 +331,7 @@ const ConfigTableList: React.FC = () => { | |||||
| handleRefreshCache(); | handleRefreshCache(); | ||||
| }} | }} | ||||
| > | > | ||||
| <ReloadOutlined /> | |||||
| <ReloadOutlined />{' '} | |||||
| <FormattedMessage id="system.config.refreshCache" defaultMessage="刷新缓存" /> | <FormattedMessage id="system.config.refreshCache" defaultMessage="刷新缓存" /> | ||||
| </Button>, | </Button>, | ||||
| ]} | ]} | ||||
| @@ -23,8 +23,11 @@ export type DataFormProps = { | |||||
| const DictDataForm: React.FC<DataFormProps> = (props) => { | const DictDataForm: React.FC<DataFormProps> = (props) => { | ||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const { statusOptions } = props; | const { statusOptions } = props; | ||||
| const formLayout = { | |||||
| labelCol: { span: 4 }, | |||||
| wrapperCol: { span: 20 }, | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| form.resetFields(); | form.resetFields(); | ||||
| @@ -36,8 +39,8 @@ const DictDataForm: React.FC<DataFormProps> = (props) => { | |||||
| dictType: props.values.dictType, | dictType: props.values.dictType, | ||||
| cssClass: props.values.cssClass, | cssClass: props.values.cssClass, | ||||
| listClass: props.values.listClass, | 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, | createBy: props.values.createBy, | ||||
| createTime: props.values.createTime, | createTime: props.values.createTime, | ||||
| updateBy: props.values.updateBy, | updateBy: props.values.updateBy, | ||||
| @@ -59,7 +62,7 @@ const DictDataForm: React.FC<DataFormProps> = (props) => { | |||||
| return ( | return ( | ||||
| <KFModal | <KFModal | ||||
| width={640} | |||||
| width={680} | |||||
| title={intl.formatMessage({ | title={intl.formatMessage({ | ||||
| id: 'system.dict.data.title', | id: 'system.dict.data.title', | ||||
| defaultMessage: '编辑字典数据', | defaultMessage: '编辑字典数据', | ||||
| @@ -76,6 +79,10 @@ const DictDataForm: React.FC<DataFormProps> = (props) => { | |||||
| submitter={false} | submitter={false} | ||||
| layout="horizontal" | layout="horizontal" | ||||
| onFinish={handleFinish} | onFinish={handleFinish} | ||||
| {...formLayout} | |||||
| size="large" | |||||
| labelAlign="right" | |||||
| autoComplete="off" | |||||
| > | > | ||||
| <ProFormDigit | <ProFormDigit | ||||
| name="dictCode" | name="dictCode" | ||||
| @@ -184,7 +191,7 @@ const DictDataForm: React.FC<DataFormProps> = (props) => { | |||||
| id: 'system.dict.data.dict_sort', | id: 'system.dict.data.dict_sort', | ||||
| defaultMessage: '字典排序', | defaultMessage: '字典排序', | ||||
| })} | })} | ||||
| colProps={{ md: 12, xl: 12 }} | |||||
| colProps={{ md: 12, xl: 24 }} | |||||
| placeholder="请输入字典排序" | placeholder="请输入字典排序" | ||||
| rules={[ | rules={[ | ||||
| { | { | ||||
| @@ -203,7 +210,6 @@ const DictDataForm: React.FC<DataFormProps> = (props) => { | |||||
| Y: '是', | Y: '是', | ||||
| N: '否', | N: '否', | ||||
| }} | }} | ||||
| initialValue={'N'} | |||||
| colProps={{ md: 12, xl: 24 }} | colProps={{ md: 12, xl: 24 }} | ||||
| placeholder="请输入是否默认" | placeholder="请输入是否默认" | ||||
| rules={[ | rules={[ | ||||
| @@ -220,7 +226,6 @@ const DictDataForm: React.FC<DataFormProps> = (props) => { | |||||
| id: 'system.dict.data.status', | id: 'system.dict.data.status', | ||||
| defaultMessage: '状态', | defaultMessage: '状态', | ||||
| })} | })} | ||||
| initialValue={'0'} | |||||
| colProps={{ md: 12, xl: 24 }} | colProps={{ md: 12, xl: 24 }} | ||||
| placeholder="请输入状态" | placeholder="请输入状态" | ||||
| rules={[ | rules={[ | ||||
| @@ -22,8 +22,11 @@ export type LogininforFormProps = { | |||||
| const LogininforForm: React.FC<LogininforFormProps> = (props) => { | const LogininforForm: React.FC<LogininforFormProps> = (props) => { | ||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const { statusOptions } = props; | const { statusOptions } = props; | ||||
| const formLayout = { | |||||
| labelCol: { span: 4 }, | |||||
| wrapperCol: { span: 20 }, | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| form.resetFields(); | form.resetFields(); | ||||
| @@ -54,7 +57,7 @@ const LogininforForm: React.FC<LogininforFormProps> = (props) => { | |||||
| return ( | return ( | ||||
| <KFModal | <KFModal | ||||
| width={640} | |||||
| width={680} | |||||
| title={intl.formatMessage({ | title={intl.formatMessage({ | ||||
| id: 'system.logininfor.title', | id: 'system.logininfor.title', | ||||
| defaultMessage: '编辑系统访问记录', | defaultMessage: '编辑系统访问记录', | ||||
| @@ -65,7 +68,16 @@ const LogininforForm: React.FC<LogininforFormProps> = (props) => { | |||||
| onOk={handleOk} | onOk={handleOk} | ||||
| onCancel={handleCancel} | onCancel={handleCancel} | ||||
| > | > | ||||
| <ProForm form={form} grid={true} layout="horizontal" onFinish={handleFinish}> | |||||
| <ProForm | |||||
| form={form} | |||||
| grid={true} | |||||
| layout="horizontal" | |||||
| onFinish={handleFinish} | |||||
| {...formLayout} | |||||
| size="large" | |||||
| labelAlign="right" | |||||
| autoComplete="off" | |||||
| > | |||||
| <ProFormDigit | <ProFormDigit | ||||
| name="infoId" | name="infoId" | ||||
| label={intl.formatMessage({ | label={intl.formatMessage({ | ||||
| @@ -24,8 +24,11 @@ export type NoticeFormProps = { | |||||
| const NoticeForm: React.FC<NoticeFormProps> = (props) => { | const NoticeForm: React.FC<NoticeFormProps> = (props) => { | ||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const { noticeTypeOptions, statusOptions } = props; | const { noticeTypeOptions, statusOptions } = props; | ||||
| const formLayout = { | |||||
| labelCol: { span: 4 }, | |||||
| wrapperCol: { span: 20 }, | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| form.resetFields(); | form.resetFields(); | ||||
| @@ -34,7 +37,7 @@ const NoticeForm: React.FC<NoticeFormProps> = (props) => { | |||||
| noticeTitle: props.values.noticeTitle, | noticeTitle: props.values.noticeTitle, | ||||
| noticeType: props.values.noticeType, | noticeType: props.values.noticeType, | ||||
| noticeContent: props.values.noticeContent, | noticeContent: props.values.noticeContent, | ||||
| status: props.values.status, | |||||
| status: props.values.status || Object.keys(statusOptions)[0], | |||||
| createBy: props.values.createBy, | createBy: props.values.createBy, | ||||
| createTime: props.values.createTime, | createTime: props.values.createTime, | ||||
| updateBy: props.values.updateBy, | updateBy: props.values.updateBy, | ||||
| @@ -56,7 +59,7 @@ const NoticeForm: React.FC<NoticeFormProps> = (props) => { | |||||
| return ( | return ( | ||||
| <KFModal | <KFModal | ||||
| width={640} | |||||
| width={680} | |||||
| title={intl.formatMessage({ | title={intl.formatMessage({ | ||||
| id: 'system.notice.title', | id: 'system.notice.title', | ||||
| defaultMessage: '编辑通知公告', | defaultMessage: '编辑通知公告', | ||||
| @@ -73,6 +76,10 @@ const NoticeForm: React.FC<NoticeFormProps> = (props) => { | |||||
| submitter={false} | submitter={false} | ||||
| layout="horizontal" | layout="horizontal" | ||||
| onFinish={handleFinish} | onFinish={handleFinish} | ||||
| {...formLayout} | |||||
| size="large" | |||||
| labelAlign="right" | |||||
| autoComplete="off" | |||||
| > | > | ||||
| <ProFormDigit | <ProFormDigit | ||||
| name="noticeId" | name="noticeId" | ||||
| @@ -29,7 +29,7 @@ const OperlogDetailForm: React.FC<OperlogFormProps> = (props) => { | |||||
| return ( | return ( | ||||
| <KFModal | <KFModal | ||||
| width={640} | |||||
| width={680} | |||||
| title={intl.formatMessage({ | title={intl.formatMessage({ | ||||
| id: 'monitor.operlog.title', | id: 'monitor.operlog.title', | ||||
| defaultMessage: '编辑操作日志记录', | defaultMessage: '编辑操作日志记录', | ||||
| @@ -245,17 +245,17 @@ const OperlogTableList: React.FC = () => { | |||||
| tableAlertRender={false} | tableAlertRender={false} | ||||
| tableAlertOptionRender={false} | tableAlertOptionRender={false} | ||||
| toolBarRender={() => [ | toolBarRender={() => [ | ||||
| <Button | |||||
| type="primary" | |||||
| key="add" | |||||
| hidden={!access.hasPerms('system:operlog:add')} | |||||
| onClick={async () => { | |||||
| setCurrentRow(undefined); | |||||
| setModalVisible(true); | |||||
| }} | |||||
| > | |||||
| <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" /> | |||||
| </Button>, | |||||
| // <Button | |||||
| // type="primary" | |||||
| // key="add" | |||||
| // hidden={!access.hasPerms('system:operlog:add')} | |||||
| // onClick={async () => { | |||||
| // setCurrentRow(undefined); | |||||
| // setModalVisible(true); | |||||
| // }} | |||||
| // > | |||||
| // <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" /> | |||||
| // </Button>, | |||||
| <Button | <Button | ||||
| type="primary" | type="primary" | ||||
| key="remove" | key="remove" | ||||
| @@ -33,6 +33,10 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => { | |||||
| ); | ); | ||||
| const [deptTreeExpandKey, setDeptTreeExpandKey] = useState<Key[]>([]); | const [deptTreeExpandKey, setDeptTreeExpandKey] = useState<Key[]>([]); | ||||
| const [checkStrictly, setCheckStrictly] = useState<boolean>(true); | const [checkStrictly, setCheckStrictly] = useState<boolean>(true); | ||||
| const formLayout = { | |||||
| labelCol: { span: 4 }, | |||||
| wrapperCol: { span: 20 }, | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| setDeptIds(deptCheckedKeys); | setDeptIds(deptCheckedKeys); | ||||
| @@ -91,7 +95,7 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => { | |||||
| return ( | return ( | ||||
| <KFModal | <KFModal | ||||
| width={640} | |||||
| width={680} | |||||
| title={intl.formatMessage({ | title={intl.formatMessage({ | ||||
| id: 'system.user.auth.role', | id: 'system.user.auth.role', | ||||
| defaultMessage: '分配角色', | defaultMessage: '分配角色', | ||||
| @@ -111,6 +115,10 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => { | |||||
| login_password: '', | login_password: '', | ||||
| confirm_password: '', | confirm_password: '', | ||||
| }} | }} | ||||
| {...formLayout} | |||||
| size="large" | |||||
| labelAlign="right" | |||||
| autoComplete="off" | |||||
| > | > | ||||
| <ProFormDigit | <ProFormDigit | ||||
| name="roleId" | name="roleId" | ||||
| @@ -73,7 +73,11 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| props.onCancel(); | props.onCancel(); | ||||
| }; | }; | ||||
| const handleFinish = async (values: Record<string, any>) => { | const handleFinish = async (values: Record<string, any>) => { | ||||
| props.onSubmit(values as UserFormData); | |||||
| const params = { | |||||
| ...values, | |||||
| userId: props.values.userId, | |||||
| }; | |||||
| props.onSubmit(params as UserFormData); | |||||
| }; | }; | ||||
| return ( | return ( | ||||
| @@ -189,9 +193,6 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| hidden={userId} | hidden={userId} | ||||
| placeholder="请输入用户账号" | placeholder="请输入用户账号" | ||||
| colProps={{ md: 12, xl: 12 }} | colProps={{ md: 12, xl: 12 }} | ||||
| fieldProps={{ | |||||
| autoComplete: 'off', | |||||
| }} | |||||
| rules={[ | rules={[ | ||||
| { | { | ||||
| required: true, | required: true, | ||||
| @@ -22,7 +22,6 @@ const Login = () => { | |||||
| const [captchaCode, setCaptchaCode] = useState<string>(''); | const [captchaCode, setCaptchaCode] = useState<string>(''); | ||||
| const [uuid, setUuid] = useState<string>(''); | const [uuid, setUuid] = useState<string>(''); | ||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const [usernameReadOnly, setUsernameReadOnly] = useState<boolean>(true); | |||||
| const captchaInputRef = useRef<InputRef>(null); | const captchaInputRef = useRef<InputRef>(null); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -82,7 +81,9 @@ const Login = () => { | |||||
| history.push(urlParams.get('redirect') || '/'); | history.push(urlParams.get('redirect') || '/'); | ||||
| } else { | } else { | ||||
| if (error?.data?.code === 500 && error?.data?.msg === '验证码错误') { | if (error?.data?.code === 500 && error?.data?.msg === '验证码错误') { | ||||
| captchaInputRef.current?.focus(); | |||||
| captchaInputRef.current?.focus({ | |||||
| cursor: 'all', | |||||
| }); | |||||
| } | } | ||||
| clearSessionToken(); | clearSessionToken(); | ||||
| @@ -140,8 +141,6 @@ const Login = () => { | |||||
| placeholder="请输入用户名" | placeholder="请输入用户名" | ||||
| prefix={<LoginInputPrefix icon={require('@/assets/img/login-user.png')} />} | prefix={<LoginInputPrefix icon={require('@/assets/img/login-user.png')} />} | ||||
| allowClear | allowClear | ||||
| readOnly={usernameReadOnly} | |||||
| onFocus={() => setUsernameReadOnly(false)} | |||||
| /> | /> | ||||
| </Form.Item> | </Form.Item> | ||||
| @@ -39,7 +39,7 @@ function patchRouteItems(route: any, menu: any, parentPath: string) { | |||||
| } | } | ||||
| } else { | } else { | ||||
| if (getLocalRoute(route, menuItem)) { | if (getLocalRoute(route, menuItem)) { | ||||
| return; | |||||
| continue; | |||||
| } | } | ||||
| const names: string[] = menuItem.component.split('/'); | const names: string[] = menuItem.component.split('/'); | ||||
| let path = ''; | let path = ''; | ||||
| @@ -24,8 +24,15 @@ export const menuItemRender = (isSubMenu: boolean) => { | |||||
| <span className="kf-menu-item__name">{item.name}</span> | <span className="kf-menu-item__name">{item.name}</span> | ||||
| </> | </> | ||||
| ); | ); | ||||
| if (isSubMenu) { | if (isSubMenu) { | ||||
| return <div className="kf-menu-item">{childen}</div>; | return <div className="kf-menu-item">{childen}</div>; | ||||
| } else if (item.isUrl) { | |||||
| return ( | |||||
| <a href={item.path || ''} target="_blank" className="kf-menu-item" rel="noreferrer"> | |||||
| {childen} | |||||
| </a> | |||||
| ); | |||||
| } else { | } else { | ||||
| return ( | return ( | ||||
| <Link to={item.path || ''} className="kf-menu-item"> | <Link to={item.path || ''} className="kf-menu-item"> | ||||