Browse Source

Merge pull request '合并dev' (#127) from dev into master

dev-lhz
cp3hnu 1 year ago
parent
commit
0044971a74
26 changed files with 596 additions and 68 deletions
  1. +10
    -0
      react-ui/config/routes.ts
  2. +2
    -2
      react-ui/src/pages/CodeConfig/List/index.tsx
  3. +38
    -12
      react-ui/src/pages/CodeConfig/components/AddCodeConfigModal/index.tsx
  4. +8
    -1
      react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.less
  5. +33
    -6
      react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx
  6. +61
    -0
      react-ui/src/pages/Dataset/components/ResourceItem/index.less
  7. +54
    -0
      react-ui/src/pages/Dataset/components/ResourceItem/index.tsx
  8. +1
    -1
      react-ui/src/pages/Monitor/JobLog/detail.tsx
  9. +4
    -1
      react-ui/src/pages/Pipeline/Info/utils.tsx
  10. +68
    -0
      react-ui/src/pages/Pipeline/components/CodeConfigItem/index.less
  11. +39
    -0
      react-ui/src/pages/Pipeline/components/CodeConfigItem/index.tsx
  12. +29
    -0
      react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less
  13. +114
    -0
      react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx
  14. +50
    -6
      react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx
  15. +10
    -3
      react-ui/src/pages/System/Config/edit.tsx
  16. +1
    -1
      react-ui/src/pages/System/Config/index.tsx
  17. +12
    -7
      react-ui/src/pages/System/DictData/edit.tsx
  18. +15
    -3
      react-ui/src/pages/System/Logininfor/edit.tsx
  19. +10
    -3
      react-ui/src/pages/System/Notice/edit.tsx
  20. +1
    -1
      react-ui/src/pages/System/Operlog/detail.tsx
  21. +11
    -11
      react-ui/src/pages/System/Operlog/index.tsx
  22. +9
    -1
      react-ui/src/pages/System/Role/components/DataScope.tsx
  23. +5
    -4
      react-ui/src/pages/System/User/edit.tsx
  24. +3
    -4
      react-ui/src/pages/User/Login/index.tsx
  25. +1
    -1
      react-ui/src/services/session.ts
  26. +7
    -0
      react-ui/src/utils/menuRender.tsx

+ 10
- 0
react-ui/config/routes.ts View File

@@ -360,6 +360,16 @@ export default [
path: 'role-auth/user/:id',
component: './System/Role/authUser',
},
{
name: '日志',
path: 'log',
routes: [
{
path: '',
redirect: '/system/log/operlog',
},
],
},
],
},
{


+ 2
- 2
react-ui/src/pages/CodeConfig/List/index.tsx View File

@@ -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}
/>
))}
</div>


+ 38
- 12
react-ui/src/pages/CodeConfig/components/AddCodeConfigModal/index.tsx View File

@@ -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<ModalProps, 'onOk'> {
}

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
>
<Form
name="form"
form={form}
layout="vertical"
onFinish={onFinish}
initialValues={initialValues}
autoComplete="off"
>
{/* 禁止 Chrome 自动填充 */}
{/* <Input type="text" style={{ display: 'none' }} />
<Input type="password" style={{ display: 'none' }} /> */}
<Form.Item
label="代码仓库名称"
name="code_repo_name"
@@ -122,13 +146,15 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo
required: true,
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
label="代码分支/Tag"
@@ -224,8 +250,8 @@ function AddCodeConfigModal({ opType, codeConfigData, onOk, ...rest }: AddCodeCo
<Input.TextArea
placeholder="请输入 SSH Key"
showCount
maxLength={1024}
autoSize={{ minRows: 3, maxRows: 6 }}
maxLength={4096}
autoSize={{ minRows: 4, maxRows: 8 }}
allowClear
/>
</Form.Item>


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

@@ -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;
}


+ 33
- 6
react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx View File

@@ -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 (
<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' }}>
<Typography.Paragraph
className={styles['code-config-item__name']}
@@ -22,18 +24,43 @@ function CodeConfigItem({ item, onClick, onRemove }: CodeConfigItemProps) {
>
{item.code_repo_name}
</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
type="text"
shape="circle"
style={{ marginRight: '-4px' }}
onClick={(e) => {
e.stopPropagation();
onRemove(item);
onRemove?.(item);
}}
>
<KFIcon type="icon-shanchu" font={17} />
</Button>
</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">
<div className={styles['code-config-item__time']}>
<img style={{ width: '17px', marginRight: '6px' }} src={creatByImg} alt="" />


+ 61
- 0
react-ui/src/pages/Dataset/components/ResourceItem/index.less View File

@@ -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: '';
}
}

+ 54
- 0
react-ui/src/pages/Dataset/components/ResourceItem/index.tsx View File

@@ -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;

+ 1
- 1
react-ui/src/pages/Monitor/JobLog/detail.tsx View File

@@ -28,7 +28,7 @@ const JobLogDetailForm: React.FC<JobLogFormProps> = (props) => {

return (
<KFModal
width={640}
width={680}
title={intl.formatMessage({
id: 'monitor.job.log.title',
defaultMessage: '定时任务调度日志',


+ 4
- 1
react-ui/src/pages/Pipeline/Info/utils.tsx View File

@@ -86,6 +86,9 @@ export function canInput(parameter: PipelineNodeModelParameter) {
const { type, item_type } = parameter;
return !(
type === 'ref' &&
(item_type === 'dataset' || item_type === 'model' || item_type === 'image')
(item_type === 'dataset' ||
item_type === 'model' ||
item_type === 'image' ||
item_type === 'code')
);
}

+ 68
- 0
react-ui/src/pages/Pipeline/components/CodeConfigItem/index.less View File

@@ -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: '';
}
}

+ 39
- 0
react-ui/src/pages/Pipeline/components/CodeConfigItem/index.tsx View File

@@ -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;

+ 29
- 0
react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.less View File

@@ -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;
}
}

+ 114
- 0
react-ui/src/pages/Pipeline/components/CodeSelectorModal/index.tsx View File

@@ -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;

+ 50
- 6
react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx View File

@@ -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<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 = (
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}


+ 10
- 3
react-ui/src/pages/System/Config/edit.tsx View File

@@ -22,8 +22,11 @@ export type ConfigFormProps = {

const ConfigForm: React.FC<ConfigFormProps> = (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<ConfigFormProps> = (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<ConfigFormProps> = (props) => {

return (
<KFModal
width={640}
width={680}
title={intl.formatMessage({
id: 'system.config.title',
defaultMessage: '编辑参数配置',
@@ -71,6 +74,10 @@ const ConfigForm: React.FC<ConfigFormProps> = (props) => {
submitter={false}
layout="horizontal"
onFinish={handleFinish}
{...formLayout}
size="large"
labelAlign="right"
autoComplete="off"
>
<ProFormDigit
name="configId"


+ 1
- 1
react-ui/src/pages/System/Config/index.tsx View File

@@ -331,7 +331,7 @@ const ConfigTableList: React.FC = () => {
handleRefreshCache();
}}
>
<ReloadOutlined />
<ReloadOutlined />{' '}
<FormattedMessage id="system.config.refreshCache" defaultMessage="刷新缓存" />
</Button>,
]}


+ 12
- 7
react-ui/src/pages/System/DictData/edit.tsx View File

@@ -23,8 +23,11 @@ export type DataFormProps = {

const DictDataForm: React.FC<DataFormProps> = (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<DataFormProps> = (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<DataFormProps> = (props) => {

return (
<KFModal
width={640}
width={680}
title={intl.formatMessage({
id: 'system.dict.data.title',
defaultMessage: '编辑字典数据',
@@ -76,6 +79,10 @@ const DictDataForm: React.FC<DataFormProps> = (props) => {
submitter={false}
layout="horizontal"
onFinish={handleFinish}
{...formLayout}
size="large"
labelAlign="right"
autoComplete="off"
>
<ProFormDigit
name="dictCode"
@@ -184,7 +191,7 @@ const DictDataForm: React.FC<DataFormProps> = (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<DataFormProps> = (props) => {
Y: '是',
N: '否',
}}
initialValue={'N'}
colProps={{ md: 12, xl: 24 }}
placeholder="请输入是否默认"
rules={[
@@ -220,7 +226,6 @@ const DictDataForm: React.FC<DataFormProps> = (props) => {
id: 'system.dict.data.status',
defaultMessage: '状态',
})}
initialValue={'0'}
colProps={{ md: 12, xl: 24 }}
placeholder="请输入状态"
rules={[


+ 15
- 3
react-ui/src/pages/System/Logininfor/edit.tsx View File

@@ -22,8 +22,11 @@ export type LogininforFormProps = {

const LogininforForm: React.FC<LogininforFormProps> = (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<LogininforFormProps> = (props) => {

return (
<KFModal
width={640}
width={680}
title={intl.formatMessage({
id: 'system.logininfor.title',
defaultMessage: '编辑系统访问记录',
@@ -65,7 +68,16 @@ const LogininforForm: React.FC<LogininforFormProps> = (props) => {
onOk={handleOk}
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
name="infoId"
label={intl.formatMessage({


+ 10
- 3
react-ui/src/pages/System/Notice/edit.tsx View File

@@ -24,8 +24,11 @@ export type NoticeFormProps = {

const NoticeForm: React.FC<NoticeFormProps> = (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<NoticeFormProps> = (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<NoticeFormProps> = (props) => {

return (
<KFModal
width={640}
width={680}
title={intl.formatMessage({
id: 'system.notice.title',
defaultMessage: '编辑通知公告',
@@ -73,6 +76,10 @@ const NoticeForm: React.FC<NoticeFormProps> = (props) => {
submitter={false}
layout="horizontal"
onFinish={handleFinish}
{...formLayout}
size="large"
labelAlign="right"
autoComplete="off"
>
<ProFormDigit
name="noticeId"


+ 1
- 1
react-ui/src/pages/System/Operlog/detail.tsx View File

@@ -29,7 +29,7 @@ const OperlogDetailForm: React.FC<OperlogFormProps> = (props) => {

return (
<KFModal
width={640}
width={680}
title={intl.formatMessage({
id: 'monitor.operlog.title',
defaultMessage: '编辑操作日志记录',


+ 11
- 11
react-ui/src/pages/System/Operlog/index.tsx View File

@@ -245,17 +245,17 @@ const OperlogTableList: React.FC = () => {
tableAlertRender={false}
tableAlertOptionRender={false}
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
type="primary"
key="remove"


+ 9
- 1
react-ui/src/pages/System/Role/components/DataScope.tsx View File

@@ -33,6 +33,10 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => {
);
const [deptTreeExpandKey, setDeptTreeExpandKey] = useState<Key[]>([]);
const [checkStrictly, setCheckStrictly] = useState<boolean>(true);
const formLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 20 },
};

useEffect(() => {
setDeptIds(deptCheckedKeys);
@@ -91,7 +95,7 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => {

return (
<KFModal
width={640}
width={680}
title={intl.formatMessage({
id: 'system.user.auth.role',
defaultMessage: '分配角色',
@@ -111,6 +115,10 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => {
login_password: '',
confirm_password: '',
}}
{...formLayout}
size="large"
labelAlign="right"
autoComplete="off"
>
<ProFormDigit
name="roleId"


+ 5
- 4
react-ui/src/pages/System/User/edit.tsx View File

@@ -73,7 +73,11 @@ const UserForm: React.FC<UserFormProps> = (props) => {
props.onCancel();
};
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 (
@@ -189,9 +193,6 @@ const UserForm: React.FC<UserFormProps> = (props) => {
hidden={userId}
placeholder="请输入用户账号"
colProps={{ md: 12, xl: 12 }}
fieldProps={{
autoComplete: 'off',
}}
rules={[
{
required: true,


+ 3
- 4
react-ui/src/pages/User/Login/index.tsx View File

@@ -22,7 +22,6 @@ const Login = () => {
const [captchaCode, setCaptchaCode] = useState<string>('');
const [uuid, setUuid] = useState<string>('');
const [form] = Form.useForm();
const [usernameReadOnly, setUsernameReadOnly] = useState<boolean>(true);
const captchaInputRef = useRef<InputRef>(null);

useEffect(() => {
@@ -82,7 +81,9 @@ const Login = () => {
history.push(urlParams.get('redirect') || '/');
} else {
if (error?.data?.code === 500 && error?.data?.msg === '验证码错误') {
captchaInputRef.current?.focus();
captchaInputRef.current?.focus({
cursor: 'all',
});
}

clearSessionToken();
@@ -140,8 +141,6 @@ const Login = () => {
placeholder="请输入用户名"
prefix={<LoginInputPrefix icon={require('@/assets/img/login-user.png')} />}
allowClear
readOnly={usernameReadOnly}
onFocus={() => setUsernameReadOnly(false)}
/>
</Form.Item>



+ 1
- 1
react-ui/src/services/session.ts View File

@@ -39,7 +39,7 @@ function patchRouteItems(route: any, menu: any, parentPath: string) {
}
} else {
if (getLocalRoute(route, menuItem)) {
return;
continue;
}
const names: string[] = menuItem.component.split('/');
let path = '';


+ 7
- 0
react-ui/src/utils/menuRender.tsx View File

@@ -24,8 +24,15 @@ export const menuItemRender = (isSubMenu: boolean) => {
<span className="kf-menu-item__name">{item.name}</span>
</>
);

if (isSubMenu) {
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 {
return (
<Link to={item.path || ''} className="kf-menu-item">


Loading…
Cancel
Save