| @@ -62,3 +62,4 @@ mvnw | |||||
| *storybook.log | *storybook.log | ||||
| /react-ui/docs | /react-ui/docs | ||||
| /react-ui/types/tsconfig.tsbuildinfo | |||||
| @@ -18,7 +18,11 @@ spec: | |||||
| image: ${k8s-6system-image} | image: ${k8s-6system-image} | ||||
| ports: | ports: | ||||
| - containerPort: 9201 | - containerPort: 9201 | ||||
| env: | |||||
| - name: TZ | |||||
| value: Asia/Shanghai | |||||
| - name: JAVA_TOOL_OPTIONS | |||||
| value: "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005" | |||||
| --- | --- | ||||
| apiVersion: v1 | apiVersion: v1 | ||||
| kind: Service | kind: Service | ||||
| @@ -28,9 +32,15 @@ metadata: | |||||
| spec: | spec: | ||||
| type: NodePort | type: NodePort | ||||
| ports: | ports: | ||||
| - port: 9201 | |||||
| - name: http | |||||
| port: 9201 | |||||
| nodePort: 31207 | nodePort: 31207 | ||||
| protocol: TCP | protocol: TCP | ||||
| - name: debug | |||||
| nodePort: 31220 | |||||
| port: 5005 | |||||
| protocol: TCP | |||||
| targetPort: 5005 | |||||
| selector: | selector: | ||||
| app: ci4s-system | app: ci4s-system | ||||
| @@ -143,6 +143,11 @@ export default [ | |||||
| path: 'compare', | path: 'compare', | ||||
| component: './Experiment/Comparison/index', | component: './Experiment/Comparison/index', | ||||
| }, | }, | ||||
| { | |||||
| name: '实验可视化对比', | |||||
| path: 'compare-visual', | |||||
| component: './Experiment/Aim/index', | |||||
| }, | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -16,7 +16,7 @@ | |||||
| "docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up", | "docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up", | ||||
| "docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro", | "docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro", | ||||
| "docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro", | "docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro", | ||||
| "docs": "typedoc --entryPointStrategy expand --entryPoints 'src/utils' --skipErrorChecking --out docs", | |||||
| "docs": "typedoc", | |||||
| "gh-pages": "gh-pages -d dist", | "gh-pages": "gh-pages -d dist", | ||||
| "i18n-remove": "pro i18n-remove --locale=zh-CN --write", | "i18n-remove": "pro i18n-remove --locale=zh-CN --write", | ||||
| "postinstall": "max setup", | "postinstall": "max setup", | ||||
| @@ -10,7 +10,7 @@ import '../public/fonts/font.css'; | |||||
| import { getAccessToken } from './access'; | import { getAccessToken } from './access'; | ||||
| import ErrorBoundary from './components/ErrorBoundary'; | import ErrorBoundary from './components/ErrorBoundary'; | ||||
| import './dayjsConfig'; | import './dayjsConfig'; | ||||
| import { removeAllPageCacheState } from './hooks/pageCacheState'; | |||||
| import { removeAllPageCacheState } from './hooks/useCacheState'; | |||||
| import { | import { | ||||
| getRemoteMenu, | getRemoteMenu, | ||||
| getRoutersInfo, | getRoutersInfo, | ||||
| @@ -3,6 +3,7 @@ import KFSpin from '@/components/KFSpin'; | |||||
| import { getLabelStudioUrl } from '@/services/developmentEnvironment'; | import { getLabelStudioUrl } from '@/services/developmentEnvironment'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import SessionStorage from '@/utils/sessionStorage'; | import SessionStorage from '@/utils/sessionStorage'; | ||||
| import { FloatButton } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import { createPortal } from 'react-dom'; | import { createPortal } from 'react-dom'; | ||||
| @@ -13,6 +14,7 @@ export enum IframePageType { | |||||
| AppDevelopment = 'AppDevelopment', // 应用开发 | AppDevelopment = 'AppDevelopment', // 应用开发 | ||||
| DevEnv = 'DevEnv', // 开发环境 | DevEnv = 'DevEnv', // 开发环境 | ||||
| GitLink = 'GitLink', // git link | GitLink = 'GitLink', // git link | ||||
| Aim = 'Aim', // 实验对比 | |||||
| } | } | ||||
| const getRequestAPI = (type: IframePageType): (() => Promise<any>) => { | const getRequestAPI = (type: IframePageType): (() => Promise<any>) => { | ||||
| @@ -29,12 +31,20 @@ const getRequestAPI = (type: IframePageType): (() => Promise<any>) => { | |||||
| }); | }); | ||||
| case IframePageType.GitLink: // git link | case IframePageType.GitLink: // git link | ||||
| return () => Promise.resolve({ code: 200, data: 'http://172.20.32.201:4000' }); | return () => Promise.resolve({ code: 200, data: 'http://172.20.32.201:4000' }); | ||||
| case IframePageType.Aim: // Aim | |||||
| return () => | |||||
| Promise.resolve({ | |||||
| code: 200, | |||||
| data: SessionStorage.getItem(SessionStorage.aimUrlKey) || '', | |||||
| }); | |||||
| } | } | ||||
| }; | }; | ||||
| type IframePageProps = { | type IframePageProps = { | ||||
| /** 子系统 */ | /** 子系统 */ | ||||
| type: IframePageType; | type: IframePageType; | ||||
| /** 是否可以在页签上打开 */ | |||||
| openInTab: boolean; | |||||
| /** 自定义样式类名 */ | /** 自定义样式类名 */ | ||||
| className?: string; | className?: string; | ||||
| /** 自定义样式 */ | /** 自定义样式 */ | ||||
| @@ -42,7 +52,7 @@ type IframePageProps = { | |||||
| }; | }; | ||||
| /** 系统内嵌 iframe,目前系统有数据标注、应用开发、开发环境、GitLink 四个子系统,使用时可以添加其他子系统 */ | /** 系统内嵌 iframe,目前系统有数据标注、应用开发、开发环境、GitLink 四个子系统,使用时可以添加其他子系统 */ | ||||
| function IframePage({ type, className, style }: IframePageProps) { | |||||
| function IframePage({ type, openInTab = false, className, style }: IframePageProps) { | |||||
| const [iframeUrl, setIframeUrl] = useState(''); | const [iframeUrl, setIframeUrl] = useState(''); | ||||
| const [loading, setLoading] = useState(false); | const [loading, setLoading] = useState(false); | ||||
| @@ -65,6 +75,7 @@ function IframePage({ type, className, style }: IframePageProps) { | |||||
| <div className={classNames('kf-iframe-page', className)} style={style}> | <div className={classNames('kf-iframe-page', className)} style={style}> | ||||
| {loading && createPortal(<KFSpin size="large" />, document.body)} | {loading && createPortal(<KFSpin size="large" />, document.body)} | ||||
| <FullScreenFrame url={iframeUrl} onLoad={hideLoading} onError={hideLoading} /> | <FullScreenFrame url={iframeUrl} onLoad={hideLoading} onError={hideLoading} /> | ||||
| {openInTab && <FloatButton onClick={() => window.open(iframeUrl, '_blank')} />} | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -22,7 +22,7 @@ | |||||
| border-radius: 4px; | border-radius: 4px; | ||||
| &__value { | &__value { | ||||
| .singleLine(); | |||||
| //.singleLine(); | |||||
| margin-right: 8px; | margin-right: 8px; | ||||
| font-size: @font-size-input; | font-size: @font-size-input; | ||||
| line-height: 1.5714285714285714; | line-height: 1.5714285714285714; | ||||
| @@ -6,7 +6,7 @@ | |||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { CloseOutlined } from '@ant-design/icons'; | import { CloseOutlined } from '@ant-design/icons'; | ||||
| import { ConfigProvider, Form, Input } from 'antd'; | |||||
| import { ConfigProvider, Form, Input, Typography } from 'antd'; | |||||
| import { RuleObject } from 'antd/es/form'; | import { RuleObject } from 'antd/es/form'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import './index.less'; | import './index.less'; | ||||
| @@ -120,7 +120,12 @@ function ParameterInput({ | |||||
| > | > | ||||
| {valueObj?.showValue ? ( | {valueObj?.showValue ? ( | ||||
| <div className="parameter-input__content"> | <div className="parameter-input__content"> | ||||
| <span className="parameter-input__content__value">{valueObj?.showValue}</span> | |||||
| <Typography.Text | |||||
| className="parameter-input__content__value" | |||||
| ellipsis={{ tooltip: valueObj.showValue }} | |||||
| > | |||||
| {valueObj.showValue} | |||||
| </Typography.Text> | |||||
| <CloseOutlined | <CloseOutlined | ||||
| className="parameter-input__content__close-icon" | className="parameter-input__content__close-icon" | ||||
| onClick={handleRemove} | onClick={handleRemove} | ||||
| @@ -1,4 +1,4 @@ | |||||
| import { filterResourceStandard, resourceFieldNames } from '@/hooks/resource'; | |||||
| import { filterResourceStandard, resourceFieldNames } from '@/hooks/useComputingResource'; | |||||
| import { ServiceData } from '@/pages/ModelDeployment/types'; | import { ServiceData } from '@/pages/ModelDeployment/types'; | ||||
| import { getDatasetList, getModelList } from '@/services/dataset/index.js'; | import { getDatasetList, getModelList } from '@/services/dataset/index.js'; | ||||
| import { getServiceListReq } from '@/services/modelDeployment'; | import { getServiceListReq } from '@/services/modelDeployment'; | ||||
| @@ -4,7 +4,7 @@ | |||||
| * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 | * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 | ||||
| */ | */ | ||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Select, type SelectProps } from 'antd'; | import { Select, type SelectProps } from 'antd'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| @@ -16,13 +16,17 @@ export type ParameterSelectObject = { | |||||
| [key: string]: any; | [key: string]: any; | ||||
| }; | }; | ||||
| export type ParameterSelectDataType = 'dataset' | 'model' | 'service' | 'resource'; | |||||
| export interface ParameterSelectProps extends SelectProps { | export interface ParameterSelectProps extends SelectProps { | ||||
| /** 类型 */ | /** 类型 */ | ||||
| dataType: 'dataset' | 'model' | 'service' | 'resource'; | |||||
| dataType: ParameterSelectDataType; | |||||
| /** 是否只是展示信息 */ | /** 是否只是展示信息 */ | ||||
| display?: boolean; | display?: boolean; | ||||
| /** 值,支持对象,对象必须包含 value */ | /** 值,支持对象,对象必须包含 value */ | ||||
| value?: string | ParameterSelectObject; | value?: string | ParameterSelectObject; | ||||
| /** 用于流水线, 流水线资源规格要求 id 为字符串 */ | |||||
| isPipeline?: boolean; | |||||
| /** 修改后回调 */ | /** 修改后回调 */ | ||||
| onChange?: (value: string | ParameterSelectObject) => void; | onChange?: (value: string | ParameterSelectObject) => void; | ||||
| } | } | ||||
| @@ -32,6 +36,7 @@ function ParameterSelect({ | |||||
| dataType, | dataType, | ||||
| display = false, | display = false, | ||||
| value, | value, | ||||
| isPipeline = false, | |||||
| onChange, | onChange, | ||||
| ...rest | ...rest | ||||
| }: ParameterSelectProps) { | }: ParameterSelectProps) { | ||||
| @@ -39,6 +44,12 @@ function ParameterSelect({ | |||||
| const propsConfig = paramSelectConfig[dataType]; | const propsConfig = paramSelectConfig[dataType]; | ||||
| const valueText = typeof value === 'object' && value !== null ? value.value : value; | const valueText = typeof value === 'object' && value !== null ? value.value : value; | ||||
| const [resourceStandardList] = useComputingResource(); | const [resourceStandardList] = useComputingResource(); | ||||
| const computingResource = isPipeline | |||||
| ? resourceStandardList.map((v) => ({ | |||||
| ...v, | |||||
| id: String(v.id), | |||||
| })) | |||||
| : resourceStandardList; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| // 获取下拉数据 | // 获取下拉数据 | ||||
| @@ -56,7 +67,7 @@ function ParameterSelect({ | |||||
| getSelectOptions(); | getSelectOptions(); | ||||
| }, [propsConfig]); | }, [propsConfig]); | ||||
| const selectOptions = dataType === 'resource' ? resourceStandardList : options; | |||||
| const selectOptions = dataType === 'resource' ? computingResource : options; | |||||
| const handleChange = (text: string) => { | const handleChange = (text: string) => { | ||||
| if (typeof value === 'object' && value !== null) { | if (typeof value === 'object' && value !== null) { | ||||
| @@ -224,6 +224,8 @@ export class MirrorSelector implements SelectorTypeInfo { | |||||
| image_id: parentKey, | image_id: parentKey, | ||||
| page: 0, | page: 0, | ||||
| size: 2000, | size: 2000, | ||||
| status: 'available', | |||||
| state: 1, | |||||
| }); | }); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| const list = res.data.content || []; | const list = res.data.content || []; | ||||
| @@ -1,202 +0,0 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-15 10:01:29 | |||||
| * @Description: 自定义 hooks | |||||
| */ | |||||
| import { FormInstance } from 'antd'; | |||||
| import { debounce } from 'lodash'; | |||||
| import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; | |||||
| /** | |||||
| * 生成具有初始值的状态引用 | |||||
| * | |||||
| * @param initialValue - 状态的初始值 | |||||
| * @return 包含状态值、状态设置函数和可变引用对象的数组 | |||||
| */ | |||||
| export function useStateRef<T>(initialValue: T) { | |||||
| const [value, setValue] = useState(initialValue); | |||||
| const ref = useRef(value); | |||||
| useEffect(() => { | |||||
| ref.current = value; | |||||
| }, [value]); | |||||
| return [value, setValue, ref] as const; | |||||
| } | |||||
| /** | |||||
| * 生成一个自定义钩子,用于管理模态框的可见性状态。 | |||||
| * | |||||
| * @param initialValue - 模态框的初始可见性状态。 | |||||
| * @return 一个数组,包含可见性状态和打开和关闭模态框的函数。 | |||||
| */ | |||||
| export function useVisible(initialValue: boolean) { | |||||
| const [visible, setVisible] = useState(initialValue); | |||||
| const ref = useRef(initialValue); | |||||
| const open = useCallback(() => { | |||||
| setVisible(true); | |||||
| }, []); | |||||
| const close = useCallback(() => { | |||||
| setVisible(false); | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| ref.current = visible; | |||||
| }, [visible]); | |||||
| return [visible, open, close, ref] as const; | |||||
| } | |||||
| type Callback<T> = (state: T) => void; | |||||
| /** | |||||
| * 生成一个具有回调机制的可变状态值和更新它的函数。 | |||||
| * | |||||
| * @param initialValue - 初始状态值。 | |||||
| * @return 一个元组,包含当前状态值和用于更新状态的函数。 | |||||
| */ | |||||
| export function useCallbackState<T>(initialValue: T) { | |||||
| const [state, _setState] = useState<T>(initialValue); | |||||
| const callbackQueue = useRef<Callback<T>[]>([]); | |||||
| useEffect(() => { | |||||
| callbackQueue.current.forEach((cb) => cb(state)); | |||||
| callbackQueue.current = []; | |||||
| }, [state]); | |||||
| const setState = (newValue: T | ((prevState: T) => T), callback?: Callback<T>) => { | |||||
| _setState(newValue); | |||||
| if (callback && typeof callback === 'function') { | |||||
| callbackQueue.current.push(callback); | |||||
| } | |||||
| }; | |||||
| return [state, setState] as const; | |||||
| } | |||||
| /** | |||||
| * 用于追踪 DOM 元素尺寸的 hook。 | |||||
| * | |||||
| * @param initialWidth - 初始宽度。 | |||||
| * @param initialHeight - 初始高度。 | |||||
| * @param deps - 依赖列表。 | |||||
| * @return 一个元组,包含 DOM 元素的 ref、当前宽度和当前高度。 | |||||
| */ | |||||
| export function useDomSize<T extends HTMLElement>( | |||||
| initialWidth: number, | |||||
| initialHeight: number, | |||||
| deps: React.DependencyList = [], | |||||
| ) { | |||||
| const domRef = useRef<T>(null); | |||||
| const [width, setWidth] = useState(initialWidth); | |||||
| const [height, setHeight] = useState(initialHeight); | |||||
| useEffect(() => { | |||||
| const setDomHeight = () => { | |||||
| if (domRef.current) { | |||||
| setHeight(domRef.current.offsetHeight); | |||||
| setWidth(domRef.current.offsetWidth); | |||||
| } | |||||
| }; | |||||
| const debounceFunc = debounce(setDomHeight, 100); | |||||
| setDomHeight(); | |||||
| window.addEventListener('resize', debounceFunc); | |||||
| return () => { | |||||
| window.removeEventListener('resize', debounceFunc); | |||||
| }; | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, [domRef, ...deps]); | |||||
| return [domRef, { width, height }] as const; | |||||
| } | |||||
| /** | |||||
| * 用于在 modal 关闭时重置 Form 表单的 hook。 | |||||
| * | |||||
| * @param form - Ant Design Form 表单实例 | |||||
| * @param open - modal 是否打开 | |||||
| */ | |||||
| export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => { | |||||
| const prevOpenRef = useRef<boolean>(); | |||||
| useEffect(() => { | |||||
| prevOpenRef.current = open; | |||||
| }, [open]); | |||||
| const prevOpen = prevOpenRef.current; | |||||
| useEffect(() => { | |||||
| if (!open && prevOpen) { | |||||
| form.resetFields(); | |||||
| } | |||||
| }, [form, prevOpen, open]); | |||||
| }; | |||||
| /** | |||||
| * Executes the effect function when the specified condition is true. | |||||
| * | |||||
| * @param effect - The effect function to execute. | |||||
| * @param when - The condition to trigger the effect. | |||||
| * @param deps - The dependencies for the effect. | |||||
| */ | |||||
| export const useEffectWhen = (effect: () => void, when: boolean, deps: React.DependencyList) => { | |||||
| const requestFns = useRef<(() => void)[]>([]); | |||||
| useEffect(() => { | |||||
| if (when) { | |||||
| effect(); | |||||
| } else { | |||||
| requestFns.current.splice(0, 1, effect); | |||||
| } | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, deps); | |||||
| useEffect(() => { | |||||
| if (when) { | |||||
| const fn = requestFns.current.pop(); | |||||
| fn?.(); | |||||
| } | |||||
| }, [when]); | |||||
| }; | |||||
| // 选择、全选操作 | |||||
| export const useCheck = <T>(list: T[]) => { | |||||
| const [selected, setSelected] = useState<T[]>([]); | |||||
| const checked = useMemo(() => { | |||||
| return selected.length === list.length && selected.length > 0; | |||||
| }, [selected, list]); | |||||
| const indeterminate = useMemo(() => { | |||||
| return selected.length > 0 && selected.length < list.length; | |||||
| }, [selected, list]); | |||||
| const checkAll = useCallback(() => { | |||||
| setSelected(checked ? [] : list); | |||||
| }, [list, checked]); | |||||
| const isSingleChecked = useCallback((item: T) => selected.includes(item), [selected]); | |||||
| const checkSingle = useCallback( | |||||
| (item: T) => { | |||||
| setSelected((prev) => { | |||||
| if (isSingleChecked(item)) { | |||||
| return prev.filter((i) => i !== item); | |||||
| } else { | |||||
| return [...prev, item]; | |||||
| } | |||||
| }); | |||||
| }, | |||||
| [isSingleChecked], | |||||
| ); | |||||
| return [ | |||||
| selected, | |||||
| setSelected, | |||||
| checked, | |||||
| indeterminate, | |||||
| checkAll, | |||||
| isSingleChecked, | |||||
| checkSingle, | |||||
| ] as const; | |||||
| }; | |||||
| @@ -29,13 +29,18 @@ const removeCacheState = (key: string) => { | |||||
| } | } | ||||
| }; | }; | ||||
| // 移除所有页面 state 缓存 | |||||
| /** | |||||
| * 移除所有页面 state 缓存 | |||||
| */ | |||||
| export const removeAllPageCacheState = () => { | export const removeAllPageCacheState = () => { | ||||
| pageKeys.forEach((key) => { | pageKeys.forEach((key) => { | ||||
| sessionStorage.removeItem(key); | sessionStorage.removeItem(key); | ||||
| }); | }); | ||||
| }; | }; | ||||
| /** | |||||
| * 缓存页面数据 | |||||
| */ | |||||
| export const useCacheState = () => { | export const useCacheState = () => { | ||||
| const { pathname } = window.location; | const { pathname } = window.location; | ||||
| const key = 'pagecache:' + pathname; | const key = 'pagecache:' + pathname; | ||||
| @@ -0,0 +1,25 @@ | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| type Callback<T> = (state: T) => void; | |||||
| /** | |||||
| * 生成一个具有回调机制的可变状态值和更新它的函数。谨慎使用 | |||||
| * | |||||
| * @param initialValue - 初始状态值。 | |||||
| * @return 一个元组,包含当前状态值和用于更新状态的函数。 | |||||
| */ | |||||
| export function useCallbackState<T>(initialValue: T) { | |||||
| const [state, _setState] = useState<T>(initialValue); | |||||
| const callbackQueue = useRef<Callback<T>[]>([]); | |||||
| useEffect(() => { | |||||
| callbackQueue.current.forEach((cb) => cb(state)); | |||||
| callbackQueue.current = []; | |||||
| }, [state]); | |||||
| const setState = (newValue: T | ((prevState: T) => T), callback?: Callback<T>) => { | |||||
| _setState(newValue); | |||||
| if (callback && typeof callback === 'function') { | |||||
| callbackQueue.current.push(callback); | |||||
| } | |||||
| }; | |||||
| return [state, setState] as const; | |||||
| } | |||||
| @@ -0,0 +1,47 @@ | |||||
| import { useCallback, useMemo, useState } from 'react'; | |||||
| /** | |||||
| * 选择、全选操作 | |||||
| * @param list - 需要进行选择的列表 | |||||
| * @return [选中的项, 设置选中的方法, 是否全选, 是否部分选中, 全选方法,是否单个选中,选中单个方法] | |||||
| */ | |||||
| export const useCheck = <T>(list: T[]) => { | |||||
| const [selected, setSelected] = useState<T[]>([]); | |||||
| const checked = useMemo(() => { | |||||
| return selected.length === list.length && selected.length > 0; | |||||
| }, [selected, list]); | |||||
| const indeterminate = useMemo(() => { | |||||
| return selected.length > 0 && selected.length < list.length; | |||||
| }, [selected, list]); | |||||
| const checkAll = useCallback(() => { | |||||
| setSelected(checked ? [] : list); | |||||
| }, [list, checked]); | |||||
| const isSingleChecked = useCallback((item: T) => selected.includes(item), [selected]); | |||||
| const checkSingle = useCallback( | |||||
| (item: T) => { | |||||
| setSelected((prev) => { | |||||
| if (isSingleChecked(item)) { | |||||
| return prev.filter((i) => i !== item); | |||||
| } else { | |||||
| return [...prev, item]; | |||||
| } | |||||
| }); | |||||
| }, | |||||
| [isSingleChecked], | |||||
| ); | |||||
| return [ | |||||
| selected, | |||||
| setSelected, | |||||
| checked, | |||||
| indeterminate, | |||||
| checkAll, | |||||
| isSingleChecked, | |||||
| checkSingle, | |||||
| ] as const; | |||||
| }; | |||||
| @@ -12,7 +12,7 @@ import { useCallback, useEffect, useState } from 'react'; | |||||
| const computingResource: ComputingResource[] = []; | const computingResource: ComputingResource[] = []; | ||||
| // 过滤资源规格 | |||||
| /** 过滤资源规格 */ | |||||
| export const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = ( | export const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = ( | ||||
| input: string, | input: string, | ||||
| option?: ComputingResource, | option?: ComputingResource, | ||||
| @@ -22,13 +22,13 @@ export const filterResourceStandard: SelectProps<string, ComputingResource>['fil | |||||
| ); | ); | ||||
| }; | }; | ||||
| // 资源规格字段 | |||||
| /** 资源规格字段 */ | |||||
| export const resourceFieldNames = { | export const resourceFieldNames = { | ||||
| label: 'description', | label: 'description', | ||||
| value: 'id', | value: 'id', | ||||
| }; | }; | ||||
| // 获取资源规格 | |||||
| /** 获取资源规格 */ | |||||
| export function useComputingResource() { | export function useComputingResource() { | ||||
| const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]); | const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]); | ||||
| @@ -0,0 +1,40 @@ | |||||
| import { debounce } from 'lodash'; | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| /** | |||||
| * 用于追踪 DOM 元素尺寸的 hook。 | |||||
| * | |||||
| * @param initialWidth - 初始宽度。 | |||||
| * @param initialHeight - 初始高度。 | |||||
| * @param deps - 依赖列表。 | |||||
| * @return 一个元组,包含 DOM 元素的 ref、当前宽度和当前高度。 | |||||
| */ | |||||
| export function useDomSize<T extends HTMLElement>( | |||||
| initialWidth: number, | |||||
| initialHeight: number, | |||||
| deps: React.DependencyList = [], | |||||
| ) { | |||||
| const domRef = useRef<T>(null); | |||||
| const [width, setWidth] = useState(initialWidth); | |||||
| const [height, setHeight] = useState(initialHeight); | |||||
| useEffect(() => { | |||||
| const setDomHeight = () => { | |||||
| if (domRef.current) { | |||||
| setHeight(domRef.current.offsetHeight); | |||||
| setWidth(domRef.current.offsetWidth); | |||||
| } | |||||
| }; | |||||
| const debounceFunc = debounce(setDomHeight, 100); | |||||
| setDomHeight(); | |||||
| window.addEventListener('resize', debounceFunc); | |||||
| return () => { | |||||
| window.removeEventListener('resize', debounceFunc); | |||||
| }; | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, deps); | |||||
| return [domRef, { width, height }] as const; | |||||
| } | |||||
| @@ -1,6 +1,8 @@ | |||||
| // 处理 react-draggable 组件拖动结束时,响应了点击事件的 | |||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||
| /** | |||||
| * 处理 react-draggable 组件拖动结束时,响应了点击事件的 | |||||
| */ | |||||
| export const useDraggable = (onClick: () => void) => { | export const useDraggable = (onClick: () => void) => { | ||||
| const [isDragging, setIsDragging] = useState(false); | const [isDragging, setIsDragging] = useState(false); | ||||
| @@ -0,0 +1,24 @@ | |||||
| import { useEffect, useRef } from 'react'; | |||||
| /** | |||||
| * 当指定的条件为真时执行 Effect 函数。 | |||||
| * | |||||
| * @param effect - The effect function to execute. | |||||
| * @param when - The condition to trigger the effect. | |||||
| * @param deps - The dependencies for the effect. | |||||
| */ | |||||
| export const useEffectWhen = (effect: () => void, when: boolean, deps: React.DependencyList) => { | |||||
| const requestFn = useRef<(() => void) | undefined>(effect); | |||||
| useEffect(() => { | |||||
| requestFn.current = effect; | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, [...deps, effect]); | |||||
| useEffect(() => { | |||||
| if (when && requestFn.current) { | |||||
| requestFn.current(); | |||||
| requestFn.current = undefined; | |||||
| } | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, [...deps, when]); | |||||
| }; | |||||
| @@ -0,0 +1,24 @@ | |||||
| import { FormInstance } from 'antd'; | |||||
| import { useEffect, useRef } from 'react'; | |||||
| /** | |||||
| * 用于在 modal 关闭时重置 Form 表单的 hook。 | |||||
| * | |||||
| * @param form - Ant Design Form 表单实例 | |||||
| * @param open - modal 是否打开 | |||||
| */ | |||||
| export const useResetForm = (form: FormInstance, open: boolean) => { | |||||
| const prevOpenRef = useRef<boolean>(); | |||||
| useEffect(() => { | |||||
| prevOpenRef.current = open; | |||||
| }, [open]); | |||||
| const prevOpen = prevOpenRef.current; | |||||
| useEffect(() => { | |||||
| if (!open && prevOpen) { | |||||
| form.resetFields(); | |||||
| } | |||||
| }, [form, prevOpen, open]); | |||||
| }; | |||||
| @@ -0,0 +1,46 @@ | |||||
| import { parseJsonText } from '@/utils'; | |||||
| import { useCallback, useRef } from 'react'; | |||||
| export const useSSE = (onMessage: (data: any) => void) => { | |||||
| const evtSourceRef = useRef<EventSource | null>(null); | |||||
| const setupSSE = useCallback( | |||||
| (name: string, namespace: string) => { | |||||
| const { origin } = location; | |||||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | |||||
| const evtSource = new EventSource( | |||||
| `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`, | |||||
| { withCredentials: false }, | |||||
| ); | |||||
| evtSource.onmessage = (event) => { | |||||
| const data = event?.data; | |||||
| if (!data) { | |||||
| return; | |||||
| } | |||||
| const dataJson = parseJsonText(data); | |||||
| if (dataJson) { | |||||
| const nodes = dataJson?.result?.object?.status?.nodes; | |||||
| if (nodes) { | |||||
| onMessage(nodes); | |||||
| } | |||||
| } | |||||
| }; | |||||
| evtSource.onerror = (error) => { | |||||
| console.error('SSE error: ', error); | |||||
| }; | |||||
| evtSourceRef.current = evtSource; | |||||
| }, | |||||
| [onMessage], | |||||
| ); | |||||
| const closeSSE = useCallback(() => { | |||||
| if (evtSourceRef.current) { | |||||
| evtSourceRef.current.close(); | |||||
| evtSourceRef.current = null; | |||||
| } | |||||
| }, []); | |||||
| return [setupSSE, closeSSE]; | |||||
| }; | |||||
| @@ -0,0 +1,19 @@ | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| /** | |||||
| * 生成具有初始值的状态引用 | |||||
| * | |||||
| * @param initialValue - 状态的初始值 | |||||
| * @return 包含状态值、状态设置函数和可变引用对象的数组 | |||||
| */ | |||||
| export function useStateRef<T>(initialValue: T) { | |||||
| const [value, setValue] = useState(initialValue); | |||||
| const ref = useRef(value); | |||||
| useEffect(() => { | |||||
| ref.current = value; | |||||
| }, [value]); | |||||
| return [value, setValue, ref] as const; | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||||
| /** | |||||
| * 生成一个自定义钩子,用于管理模态框的可见性状态。 | |||||
| * | |||||
| * @param initialValue - 模态框的初始可见性状态。 | |||||
| * @return 一个数组,包含 visible、打开函数、关闭函数和 visible ref。 | |||||
| */ | |||||
| export function useVisible(initialValue: boolean) { | |||||
| const [visible, setVisible] = useState(initialValue); | |||||
| const ref = useRef(initialValue); | |||||
| const open = useCallback(() => { | |||||
| setVisible(true); | |||||
| }, []); | |||||
| const close = useCallback(() => { | |||||
| setVisible(false); | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| ref.current = visible; | |||||
| }, [visible]); | |||||
| return [visible, open, close, ref] as const; | |||||
| } | |||||
| @@ -1,6 +1,6 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useCheck } from '@/hooks'; | |||||
| import { useCheck } from '@/hooks/useCheck'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { type ExperimentInstance } from '@/types'; | import { type ExperimentInstance } from '@/types'; | ||||
| @@ -58,7 +58,8 @@ function ExperimentInstanceComponent({ | |||||
| // 删除实验实例确认 | // 删除实验实例确认 | ||||
| const handleRemove = (instance: ExperimentInstance) => { | const handleRemove = (instance: ExperimentInstance) => { | ||||
| modalConfirm({ | modalConfirm({ | ||||
| title: '确定删除该条实例吗?', | |||||
| title: '删除后,该实验实例将不可恢复', | |||||
| content: '是否确认删除?', | |||||
| onOk: () => { | onOk: () => { | ||||
| deleteExperimentInstance(instance.id); | deleteExperimentInstance(instance.id); | ||||
| }, | }, | ||||
| @@ -96,6 +97,18 @@ function ExperimentInstanceComponent({ | |||||
| } | } | ||||
| }; | }; | ||||
| // 终止实验实例 | |||||
| const handleTerminate = (instance: ExperimentInstance) => { | |||||
| modalConfirm({ | |||||
| title: '终止后,该次实验运行将不可恢复', | |||||
| content: '是否确认终止?', | |||||
| isDelete: false, | |||||
| onOk: () => { | |||||
| terminateExperimentInstance(instance); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| // 终止实验实例 | // 终止实验实例 | ||||
| const terminateExperimentInstance = async (instance: ExperimentInstance) => { | const terminateExperimentInstance = async (instance: ExperimentInstance) => { | ||||
| const request = config.stopInsReq; | const request = config.stopInsReq; | ||||
| @@ -188,7 +201,7 @@ function ExperimentInstanceComponent({ | |||||
| item.status === ExperimentStatus.Terminated | item.status === ExperimentStatus.Terminated | ||||
| } | } | ||||
| icon={<KFIcon type="icon-zhongzhi" />} | icon={<KFIcon type="icon-zhongzhi" />} | ||||
| onClick={() => terminateExperimentInstance(item)} | |||||
| onClick={() => handleTerminate(item)} | |||||
| > | > | ||||
| 终止 | 终止 | ||||
| </Button> | </Button> | ||||
| @@ -7,7 +7,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { AutoMLData } from '@/pages/AutoML/types'; | import { AutoMLData } from '@/pages/AutoML/types'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| @@ -4,7 +4,12 @@ import KFModal from '@/components/KFModal'; | |||||
| import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | ||||
| import { addDataset } from '@/services/dataset/index.js'; | import { addDataset } from '@/services/dataset/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { getFileListFromEvent, limitUploadFileType, validateUploadFiles } from '@/utils/ui'; | |||||
| import { | |||||
| getFileListFromEvent, | |||||
| limitUploadFileType, | |||||
| removeUploadedFile, | |||||
| validateUploadFiles, | |||||
| } from '@/utils/ui'; | |||||
| import { | import { | ||||
| Button, | Button, | ||||
| Form, | Form, | ||||
| @@ -29,11 +34,6 @@ interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> { | |||||
| function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalProps) { | function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalProps) { | ||||
| const [uuid] = useState(Date.now()); | const [uuid] = useState(Date.now()); | ||||
| // const [clusterOptions, setClusterOptions] = useState<DictValueEnumObj[]>([]); | |||||
| // useEffect(() => { | |||||
| // getClusterOptions(); | |||||
| // }, []); | |||||
| // 上传组件参数 | // 上传组件参数 | ||||
| const uploadProps: UploadProps = { | const uploadProps: UploadProps = { | ||||
| @@ -44,16 +44,9 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr | |||||
| defaultFileList: [], | defaultFileList: [], | ||||
| accept: '.zip,.tgz', | accept: '.zip,.tgz', | ||||
| beforeUpload: limitUploadFileType('zip,tgz'), | beforeUpload: limitUploadFileType('zip,tgz'), | ||||
| onRemove: removeUploadedFile, | |||||
| }; | }; | ||||
| // 获取集群版本数据 | |||||
| // const getClusterOptions = async () => { | |||||
| // const [res] = await to(getDictSelectOption('available_cluster')); | |||||
| // if (res) { | |||||
| // setClusterOptions(res); | |||||
| // } | |||||
| // }; | |||||
| // 上传请求 | // 上传请求 | ||||
| const createDataset = async (params: any) => { | const createDataset = async (params: any) => { | ||||
| const [res] = await to(addDataset(params)); | const [res] = await to(addDataset(params)); | ||||
| @@ -113,7 +106,7 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| <Input placeholder="请输入数据名称" showCount allowClear maxLength={50} /> | |||||
| <Input placeholder="请输入数据名称" showCount allowClear maxLength={40} /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| label="数据集版本" | label="数据集版本" | ||||
| @@ -4,7 +4,7 @@ import KFModal from '@/components/KFModal'; | |||||
| import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | ||||
| import { addModel } from '@/services/dataset/index.js'; | import { addModel } from '@/services/dataset/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | |||||
| import { getFileListFromEvent, removeUploadedFile, validateUploadFiles } from '@/utils/ui'; | |||||
| import { | import { | ||||
| Button, | Button, | ||||
| Form, | Form, | ||||
| @@ -37,6 +37,7 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| Authorization: getAccessToken() || '', | Authorization: getAccessToken() || '', | ||||
| }, | }, | ||||
| defaultFileList: [], | defaultFileList: [], | ||||
| onRemove: removeUploadedFile, | |||||
| }; | }; | ||||
| // 上传请求 | // 上传请求 | ||||
| @@ -96,7 +97,7 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| <Input placeholder="请输入模型名称" showCount allowClear maxLength={50} /> | |||||
| <Input placeholder="请输入模型名称" showCount allowClear maxLength={40} /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| label="模型版本" | label="模型版本" | ||||
| @@ -3,7 +3,7 @@ import KFIcon from '@/components/KFIcon'; | |||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | import { DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui'; | |||||
| import { getFileListFromEvent, removeUploadedFile, validateUploadFiles } from '@/utils/ui'; | |||||
| import { | import { | ||||
| Button, | Button, | ||||
| Form, | Form, | ||||
| @@ -50,6 +50,7 @@ function AddVersionModal({ | |||||
| defaultFileList: [], | defaultFileList: [], | ||||
| beforeUpload: config.beforeUpload, | beforeUpload: config.beforeUpload, | ||||
| accept: config.uploadAccept, | accept: config.uploadAccept, | ||||
| onRemove: removeUploadedFile, | |||||
| }; | }; | ||||
| // 上传请求 | // 上传请求 | ||||
| @@ -1,5 +1,5 @@ | |||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { getAssetIcon } from '@/services/dataset/index.js'; | import { getAssetIcon } from '@/services/dataset/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Flex, Tabs, type TabsProps } from 'antd'; | import { Flex, Tabs, type TabsProps } from 'antd'; | ||||
| @@ -1,3 +1,9 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2025-03-24 15:41:42 | |||||
| * @Description: 版本文件列表 | |||||
| */ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { | import { | ||||
| ResourceData, | ResourceData, | ||||
| @@ -6,7 +6,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { DevEditorStatus } from '@/enums'; | import { DevEditorStatus } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { | import { | ||||
| deleteEditorReq, | deleteEditorReq, | ||||
| getEditorListReq, | getEditorListReq, | ||||
| @@ -0,0 +1,12 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2025-03-31 16:38:59 | |||||
| * @Description: 实验对比 Aim | |||||
| */ | |||||
| import IframePage, { IframePageType } from '@/components/IFramePage'; | |||||
| function AimPage() { | |||||
| return <IframePage type={IframePageType.Aim}></IframePage>; | |||||
| } | |||||
| export default AimPage; | |||||
| @@ -12,8 +12,9 @@ import { | |||||
| } from '@/services/experiment'; | } from '@/services/experiment'; | ||||
| import { tableSorter } from '@/utils'; | import { tableSorter } from '@/utils'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import SessionStorage from '@/utils/sessionStorage'; | |||||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | import tableCellRender, { TableCellValueType } from '@/utils/table'; | ||||
| import { useSearchParams } from '@umijs/max'; | |||||
| import { useNavigate, useSearchParams } from '@umijs/max'; | |||||
| import { App, Button, Table, TablePaginationConfig, TableProps } from 'antd'; | import { App, Button, Table, TablePaginationConfig, TableProps } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useEffect, useMemo, useState } from 'react'; | import { useEffect, useMemo, useState } from 'react'; | ||||
| @@ -46,6 +47,7 @@ function ExperimentComparison() { | |||||
| }); | }); | ||||
| const { message } = App.useApp(); | const { message } = App.useApp(); | ||||
| const navigate = useNavigate(); | |||||
| const config = comparisonConfig[comparisonType]; | const config = comparisonConfig[comparisonType]; | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -73,7 +75,9 @@ function ExperimentComparison() { | |||||
| const [res] = await to(getExpMetricsReq(selectedRowKeys)); | const [res] = await to(getExpMetricsReq(selectedRowKeys)); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| const url = res.data; | const url = res.data; | ||||
| window.open(url, '_blank'); | |||||
| // window.open(url, '_blank'); | |||||
| SessionStorage.setItem(SessionStorage.aimUrlKey, url); | |||||
| navigate('../compare-visual'); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -1,5 +1,6 @@ | |||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useStateRef, useVisible } from '@/hooks'; | |||||
| import { useStateRef } from '@/hooks/useStateRef'; | |||||
| import { useVisible } from '@/hooks/useVisible'; | |||||
| import { getExperimentIns } from '@/services/experiment/index.js'; | import { getExperimentIns } from '@/services/experiment/index.js'; | ||||
| import { getWorkflowById } from '@/services/pipeline/index.js'; | import { getWorkflowById } from '@/services/pipeline/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| @@ -1,6 +1,6 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useCheck } from '@/hooks'; | |||||
| import { useCheck } from '@/hooks/useCheck'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import { | import { | ||||
| deleteManyExperimentIns, | deleteManyExperimentIns, | ||||
| @@ -62,7 +62,8 @@ function ExperimentInstanceComponent({ | |||||
| // 删除实验实例确认 | // 删除实验实例确认 | ||||
| const handleRemove = (instance: ExperimentInstance) => { | const handleRemove = (instance: ExperimentInstance) => { | ||||
| modalConfirm({ | modalConfirm({ | ||||
| title: '确定删除该条实例吗?', | |||||
| title: '删除后,该实验实例将不可恢复', | |||||
| content: '是否确认删除?', | |||||
| onOk: () => { | onOk: () => { | ||||
| deleteExperimentInstance(instance.id); | deleteExperimentInstance(instance.id); | ||||
| }, | }, | ||||
| @@ -101,7 +102,8 @@ function ExperimentInstanceComponent({ | |||||
| // 终止实验实例 | // 终止实验实例 | ||||
| const handleTerminate = (instance: ExperimentInstance) => { | const handleTerminate = (instance: ExperimentInstance) => { | ||||
| modalConfirm({ | modalConfirm({ | ||||
| title: '确定要终止此次实验运行吗?', | |||||
| title: '终止后,该次实验运行将不可恢复', | |||||
| content: '是否确认终止?', | |||||
| isDelete: false, | isDelete: false, | ||||
| onOk: () => { | onOk: () => { | ||||
| terminateExperimentInstance(instance); | terminateExperimentInstance(instance); | ||||
| @@ -5,7 +5,7 @@ | |||||
| */ | */ | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useStateRef } from '@/hooks'; | |||||
| import { useStateRef } from '@/hooks/useStateRef'; | |||||
| import { getExperimentPodsLog } from '@/services/experiment/index.js'; | import { getExperimentPodsLog } from '@/services/experiment/index.js'; | ||||
| import { DoubleRightOutlined, DownOutlined, UpOutlined } from '@ant-design/icons'; | import { DoubleRightOutlined, DownOutlined, UpOutlined } from '@ant-design/icons'; | ||||
| import { Button } from 'antd'; | import { Button } from 'antd'; | ||||
| @@ -1,7 +1,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { ExperimentStatus, TensorBoardStatus } from '@/enums'; | import { ExperimentStatus, TensorBoardStatus } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { | import { | ||||
| deleteExperimentById, | deleteExperimentById, | ||||
| getExperiment, | getExperiment, | ||||
| @@ -286,8 +286,6 @@ function Experiment() { | |||||
| message.success('运行成功'); | message.success('运行成功'); | ||||
| refreshExperimentList(); | refreshExperimentList(); | ||||
| refreshExperimentIns(id); | refreshExperimentIns(id); | ||||
| } else { | |||||
| message.error('运行失败'); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -1,6 +1,6 @@ | |||||
| import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | ||||
| import { hyperParameterOptimizedMode } from '@/enums'; | import { hyperParameterOptimizedMode } from '@/enums'; | ||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import { | import { | ||||
| schedulerAlgorithms, | schedulerAlgorithms, | ||||
| @@ -7,8 +7,8 @@ import KFIcon from '@/components/KFIcon'; | |||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | import SubAreaTitle from '@/components/SubAreaTitle'; | ||||
| import { MirrorVersionStatus } from '@/enums'; | import { MirrorVersionStatus } from '@/enums'; | ||||
| import { useDomSize } from '@/hooks'; | |||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { useDomSize } from '@/hooks/useDomSize'; | |||||
| import { | import { | ||||
| deleteMirrorVersionReq, | deleteMirrorVersionReq, | ||||
| getMirrorInfoReq, | getMirrorInfoReq, | ||||
| @@ -5,7 +5,7 @@ | |||||
| */ | */ | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { deleteMirrorReq, getMirrorListReq } from '@/services/mirror'; | import { deleteMirrorReq, getMirrorListReq } from '@/services/mirror'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -4,12 +4,12 @@ | |||||
| * @Description: 模型演化 | * @Description: 模型演化 | ||||
| */ | */ | ||||
| import { useEffectWhen } from '@/hooks'; | |||||
| import { useEffectWhen } from '@/hooks/useEffectWhen'; | |||||
| import { getModelAtlasReq } from '@/services/dataset/index.js'; | import { getModelAtlasReq } from '@/services/dataset/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import G6, { G6GraphEvent, Graph, INode } from '@antv/g6'; | import G6, { G6GraphEvent, Graph, INode } from '@antv/g6'; | ||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||||
| import NodeTooltips from '../NodeTooltips'; | import NodeTooltips from '../NodeTooltips'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -73,17 +73,45 @@ function ModelEvolution({ | |||||
| }; | }; | ||||
| }, []); | }, []); | ||||
| useEffectWhen( | |||||
| () => { | |||||
| if (version) { | |||||
| getModelAtlas(); | |||||
| } else { | |||||
| clearGraphData(); | |||||
| } | |||||
| }, | |||||
| isActive, | |||||
| [resourceId, version], | |||||
| ); | |||||
| const getModelAtlas = useCallback(async () => { | |||||
| // 请求失败或者版本不存在时,清除图形 | |||||
| function clearGraphData() { | |||||
| graph.data({ | |||||
| nodes: [], | |||||
| edges: [], | |||||
| }); | |||||
| graph.render(); | |||||
| graph.fitView(); | |||||
| } | |||||
| if (!resourceId || !identifier || !version) { | |||||
| clearGraphData(); | |||||
| return; | |||||
| } | |||||
| const params = { | |||||
| id: resourceId, | |||||
| identifier, | |||||
| version, | |||||
| }; | |||||
| const [res] = await to(getModelAtlasReq(params)); | |||||
| if (res && res.data) { | |||||
| const data = normalizeTreeData(res.data); | |||||
| apiData.current = data; | |||||
| hierarchyNodes.current = traverseHierarchically(data); | |||||
| const graphData = getGraphData(data, hierarchyNodes.current); | |||||
| graph.data(graphData); | |||||
| graph.render(); | |||||
| graph.fitView(); | |||||
| setShowNodeTooltip(false); | |||||
| setEnterTooltip(false); | |||||
| } else { | |||||
| clearGraphData(); | |||||
| } | |||||
| }, [resourceId, identifier, version]); | |||||
| useEffectWhen(getModelAtlas, isActive, [resourceId, identifier, version]); | |||||
| // 初始化图 | // 初始化图 | ||||
| const initGraph = () => { | const initGraph = () => { | ||||
| @@ -249,40 +277,6 @@ function ModelEvolution({ | |||||
| }, 100); | }, 100); | ||||
| }; | }; | ||||
| // 获取模型依赖 | |||||
| const getModelAtlas = async () => { | |||||
| const params = { | |||||
| id: resourceId, | |||||
| identifier, | |||||
| version, | |||||
| }; | |||||
| const [res] = await to(getModelAtlasReq(params)); | |||||
| if (res && res.data) { | |||||
| const data = normalizeTreeData(res.data); | |||||
| apiData.current = data; | |||||
| hierarchyNodes.current = traverseHierarchically(data); | |||||
| const graphData = getGraphData(data, hierarchyNodes.current); | |||||
| graph.data(graphData); | |||||
| graph.render(); | |||||
| graph.fitView(); | |||||
| setShowNodeTooltip(false); | |||||
| setEnterTooltip(false); | |||||
| } else { | |||||
| clearGraphData(); | |||||
| } | |||||
| }; | |||||
| // 请求失败或者版本不存在时,清除图形 | |||||
| function clearGraphData() { | |||||
| graph.data({ | |||||
| nodes: [], | |||||
| edges: [], | |||||
| }); | |||||
| graph.render(); | |||||
| graph.fitView(); | |||||
| } | |||||
| return ( | return ( | ||||
| <div className={styles['model-evolution']}> | <div className={styles['model-evolution']}> | ||||
| <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div> | <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div> | ||||
| @@ -1,6 +1,6 @@ | |||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | import SubAreaTitle from '@/components/SubAreaTitle'; | ||||
| import TableColTitle from '@/components/TableColTitle'; | import TableColTitle from '@/components/TableColTitle'; | ||||
| import { useCheck } from '@/hooks'; | |||||
| import { useCheck } from '@/hooks/useCheck'; | |||||
| import { getModelPageVersionsReq, getModelVersionsMetricsReq } from '@/services/dataset'; | import { getModelPageVersionsReq, getModelVersionsMetricsReq } from '@/services/dataset'; | ||||
| import { tableSorter } from '@/utils'; | import { tableSorter } from '@/utils'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -6,7 +6,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { serviceTypeOptions } from '@/enums'; | import { serviceTypeOptions } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { deleteServiceReq, getServiceListReq } from '@/services/modelDeployment'; | import { deleteServiceReq, getServiceListReq } from '@/services/modelDeployment'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -8,8 +8,8 @@ import KFIcon from '@/components/KFIcon'; | |||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | import SubAreaTitle from '@/components/SubAreaTitle'; | ||||
| import { ServiceRunStatus, serviceStatusOptions } from '@/enums'; | import { ServiceRunStatus, serviceStatusOptions } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { | import { | ||||
| deleteServiceVersionReq, | deleteServiceVersionReq, | ||||
| getServiceInfoReq, | getServiceInfoReq, | ||||
| @@ -1,6 +1,6 @@ | |||||
| import BasicInfo, { type BasicInfoData } from '@/components/BasicInfo'; | import BasicInfo, { type BasicInfoData } from '@/components/BasicInfo'; | ||||
| import { ServiceRunStatus } from '@/enums'; | import { ServiceRunStatus } from '@/enums'; | ||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { formatCodeConfig, formatModel } from '@/utils/format'; | import { formatCodeConfig, formatModel } from '@/utils/format'; | ||||
| @@ -1,6 +1,6 @@ | |||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { ServiceRunStatus } from '@/enums'; | import { ServiceRunStatus } from '@/enums'; | ||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; | import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; | ||||
| import { getServiceVersionCompareReq } from '@/services/modelDeployment'; | import { getServiceVersionCompareReq } from '@/services/modelDeployment'; | ||||
| import { isEmpty } from '@/utils'; | import { isEmpty } from '@/utils'; | ||||
| @@ -1,5 +1,6 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { useStateRef, useVisible } from '@/hooks'; | |||||
| import { useStateRef } from '@/hooks/useStateRef'; | |||||
| import { useVisible } from '@/hooks/useVisible'; | |||||
| import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js'; | import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { fittingString, parseJsonText, s8 } from '@/utils'; | import { fittingString, parseJsonText, s8 } from '@/utils'; | ||||
| @@ -1,7 +1,7 @@ | |||||
| import CodeSelectorModal from '@/components/CodeSelectorModal'; | import CodeSelectorModal from '@/components/CodeSelectorModal'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import ParameterInput, { requiredValidator } from '@/components/ParameterInput'; | import ParameterInput, { requiredValidator } from '@/components/ParameterInput'; | ||||
| import ParameterSelect from '@/components/ParameterSelect'; | |||||
| import ParameterSelect, { type ParameterSelectDataType } from '@/components/ParameterSelect'; | |||||
| import ResourceSelectorModal, { | import ResourceSelectorModal, { | ||||
| ResourceSelectorType, | ResourceSelectorType, | ||||
| selectorTypeConfig, | selectorTypeConfig, | ||||
| @@ -520,7 +520,8 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| {item.value.type === 'select' ? ( | {item.value.type === 'select' ? ( | ||||
| ['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? ( | ['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? ( | ||||
| <ParameterSelect | <ParameterSelect | ||||
| dataType={item.value.item_type as any} | |||||
| isPipeline | |||||
| dataType={item.value.item_type as ParameterSelectDataType} | |||||
| placeholder={item.value.placeholder} | placeholder={item.value.placeholder} | ||||
| /> | /> | ||||
| ) : null | ) : null | ||||
| @@ -1,7 +1,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { | import { | ||||
| addWorkflow, | addWorkflow, | ||||
| cloneWorkflow, | cloneWorkflow, | ||||
| @@ -11,6 +11,7 @@ import { | |||||
| removeWorkflow, | removeWorkflow, | ||||
| } from '@/services/pipeline/index.js'; | } from '@/services/pipeline/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { to } from '@/utils/promise'; | |||||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | import tableCellRender, { TableCellValueType } from '@/utils/table'; | ||||
| import { modalConfirm } from '@/utils/ui'; | import { modalConfirm } from '@/utils/ui'; | ||||
| import { App, Button, ConfigProvider, Form, Input, Space, Table } from 'antd'; | import { App, Button, ConfigProvider, Form, Input, Space, Table } from 'antd'; | ||||
| @@ -132,23 +133,38 @@ const Pipeline = () => { | |||||
| modalConfirm({ | modalConfirm({ | ||||
| title: '删除后,该流水线将不可恢复', | title: '删除后,该流水线将不可恢复', | ||||
| content: '是否确认删除?', | content: '是否确认删除?', | ||||
| onOk: () => { | |||||
| removeWorkflow(record.id).then((ret) => { | |||||
| if (ret.code === 200) { | |||||
| message.success('删除成功'); | |||||
| // 如果是一页的唯一数据,删除后,请求第一页的数据 | |||||
| // 否则直接刷新这一页的数据 | |||||
| setPagination((prev) => { | |||||
| return { | |||||
| ...prev, | |||||
| current: pipeList.length === 1 ? Math.max(1, prev.current - 1) : prev.current, | |||||
| }; | |||||
| }); | |||||
| getList(); | |||||
| } else { | |||||
| message.error(ret.msg); | |||||
| } | |||||
| }); | |||||
| onOk: async () => { | |||||
| const { id } = record; | |||||
| const [res] = await to(removeWorkflow(id)); | |||||
| if (res) { | |||||
| message.success('删除成功'); | |||||
| // 如果是一页的唯一数据,删除后,请求第一页的数据 | |||||
| // 否则直接刷新这一页的数据 | |||||
| setPagination((prev) => { | |||||
| return { | |||||
| ...prev, | |||||
| current: pipeList.length === 1 ? Math.max(1, prev.current - 1) : prev.current, | |||||
| }; | |||||
| }); | |||||
| } | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| // 处理复制 | |||||
| const handlePipelineCopy = (record) => { | |||||
| modalConfirm({ | |||||
| title: '确定复制该条流水线吗?', | |||||
| okText: '确认', | |||||
| cancelText: '取消', | |||||
| isDelete: false, | |||||
| onOk: async () => { | |||||
| const { id } = record; | |||||
| const [res] = await to(cloneWorkflow(id)); | |||||
| if (res) { | |||||
| message.success('复制成功'); | |||||
| getList(); | |||||
| } | |||||
| }, | }, | ||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -225,30 +241,7 @@ const Pipeline = () => { | |||||
| size="small" | size="small" | ||||
| key="clone" | key="clone" | ||||
| icon={<KFIcon type="icon-fuzhi" />} | icon={<KFIcon type="icon-fuzhi" />} | ||||
| onClick={async () => { | |||||
| modalConfirm({ | |||||
| title: '确定复制该条流水线吗?', | |||||
| okText: '确认', | |||||
| cancelText: '取消', | |||||
| isDelete: false, | |||||
| onOk: () => { | |||||
| cloneWorkflow(record.id).then((ret) => { | |||||
| if (ret.code === 200) { | |||||
| message.success('复制成功'); | |||||
| getList(); | |||||
| } else { | |||||
| message.error('复制失败'); | |||||
| } | |||||
| }); | |||||
| // if (success) { | |||||
| // if (actionRef.current) { | |||||
| // actionRef.current.reload(); | |||||
| // } | |||||
| // } | |||||
| }, | |||||
| }); | |||||
| }} | |||||
| onClick={() => handlePipelineCopy(record)} | |||||
| > | > | ||||
| 复制 | 复制 | ||||
| </Button> | </Button> | ||||
| @@ -25,19 +25,19 @@ enum TaskType { | |||||
| const taskTypeOptions = [ | const taskTypeOptions = [ | ||||
| { | { | ||||
| value: 'dev_environment', | |||||
| value: TaskType.DevEnvironment, | |||||
| label: '开发环境', | label: '开发环境', | ||||
| }, | }, | ||||
| { | { | ||||
| value: 'workflow', | |||||
| value: TaskType.Workflow, | |||||
| label: '实验', | label: '实验', | ||||
| }, | }, | ||||
| { | { | ||||
| value: 'ray', | |||||
| value: TaskType.Ray, | |||||
| label: '超参数自动寻优', | label: '超参数自动寻优', | ||||
| }, | }, | ||||
| { | { | ||||
| value: 'service', | |||||
| value: TaskType.Service, | |||||
| label: '服务', | label: '服务', | ||||
| }, | }, | ||||
| ]; | ]; | ||||
| @@ -17,6 +17,7 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => { | |||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const loginPassword = Form.useWatch('password', form); | const loginPassword = Form.useWatch('password', form); | ||||
| const userId = props.values.userId; | const userId = props.values.userId; | ||||
| const originPassword = props.values.originPassword; | |||||
| const intl = useIntl(); | const intl = useIntl(); | ||||
| const handleOk = () => { | const handleOk = () => { | ||||
| @@ -26,7 +27,7 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => { | |||||
| props.onCancel(); | props.onCancel(); | ||||
| }; | }; | ||||
| const handleFinish = async (values: Record<string, any>) => { | const handleFinish = async (values: Record<string, any>) => { | ||||
| props.onSubmit({ ...values, userId } as FormValueType); | |||||
| props.onSubmit({ password: values.password, userId, originPassword } as FormValueType); | |||||
| }; | }; | ||||
| const checkPassword = (rule: any, value: string) => { | const checkPassword = (rule: any, value: string) => { | ||||
| @@ -63,8 +63,8 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| loginIp: props.values.loginIp, | loginIp: props.values.loginIp, | ||||
| loginDate: props.values.loginDate, | loginDate: props.values.loginDate, | ||||
| remark: props.values.remark, | remark: props.values.remark, | ||||
| gitLinkUsername: props.values.gitLinkUsername, | |||||
| gitLinkPassword: props.values.gitLinkPassword, | |||||
| // gitLinkUsername: props.values.gitLinkUsername, | |||||
| // gitLinkPassword: props.values.gitLinkPassword, | |||||
| credit: props.values.credit, | credit: props.values.credit, | ||||
| }); | }); | ||||
| }, [form, props, statusOptions]); | }, [form, props, statusOptions]); | ||||
| @@ -80,6 +80,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| const params = { | const params = { | ||||
| ...values, | ...values, | ||||
| userId: props.values.userId, | userId: props.values.userId, | ||||
| originPassword: props.values.originPassword, | |||||
| }; | }; | ||||
| props.onSubmit(params as UserFormData); | props.onSubmit(params as UserFormData); | ||||
| }; | }; | ||||
| @@ -150,7 +151,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| colProps={{ md: 12, xl: 12 }} | colProps={{ md: 12, xl: 12 }} | ||||
| rules={[ | rules={[ | ||||
| { | { | ||||
| required: false, | |||||
| required: true, | |||||
| message: <FormattedMessage id="请输入手机号码!" defaultMessage="请输入手机号码!" />, | message: <FormattedMessage id="请输入手机号码!" defaultMessage="请输入手机号码!" />, | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -174,7 +175,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| colProps={{ md: 12, xl: 12 }} | colProps={{ md: 12, xl: 12 }} | ||||
| rules={[ | rules={[ | ||||
| { | { | ||||
| required: false, | |||||
| required: true, | |||||
| message: <FormattedMessage id="请输入用户邮箱!" defaultMessage="请输入用户邮箱!" />, | message: <FormattedMessage id="请输入用户邮箱!" defaultMessage="请输入用户邮箱!" />, | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -194,7 +195,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| id: 'system.user.user_name', | id: 'system.user.user_name', | ||||
| defaultMessage: '用户账号', | defaultMessage: '用户账号', | ||||
| })} | })} | ||||
| hidden={userId} | |||||
| disabled={!!props.values.userId} | |||||
| placeholder="请输入用户账号" | placeholder="请输入用户账号" | ||||
| colProps={{ md: 12, xl: 12 }} | colProps={{ md: 12, xl: 12 }} | ||||
| rules={[ | rules={[ | ||||
| @@ -202,9 +203,9 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| required: true, | required: true, | ||||
| }, | }, | ||||
| { | { | ||||
| pattern: /^[a-zA-Z0-9](?:[a-zA-Z0-9_.-]*[a-zA-Z0-9])?$/, | |||||
| pattern: /^[a-zA-Z](?:[a-zA-Z0-9_.-]*[a-zA-Z0-9])?$/, | |||||
| message: | message: | ||||
| '只能包含数字,字母,下划线(_),中横线(-),英文句号(.),且必须以数字或字母开头与结尾', | |||||
| '只能包含数字,字母,下划线(_),中横线(-),英文句号(.),且必须以字母开头,数字或字母结尾', | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| /> | /> | ||||
| @@ -214,14 +215,23 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| id: 'system.user.password', | id: 'system.user.password', | ||||
| defaultMessage: '密码', | defaultMessage: '密码', | ||||
| })} | })} | ||||
| hidden={userId} | |||||
| placeholder="请输入密码" | placeholder="请输入密码" | ||||
| colProps={{ md: 12, xl: 12 }} | colProps={{ md: 12, xl: 12 }} | ||||
| fieldProps={{ | fieldProps={{ | ||||
| autoComplete: 'new-password', | autoComplete: 'new-password', | ||||
| }} | }} | ||||
| allowClear | allowClear | ||||
| rules={props.values.userId ? [] : [{ required: true, message: '请输入密码!' }]} | |||||
| rules={ | |||||
| props.values.userId | |||||
| ? [] | |||||
| : [ | |||||
| { required: true, message: '请输入密码!' }, | |||||
| { | |||||
| pattern: /^[A-Za-z0-9!"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]{8,16}$/, | |||||
| message: '密码长度为8 ~ 16位,只支持字母数字和符号', | |||||
| }, | |||||
| ] | |||||
| } | |||||
| /> | /> | ||||
| <ProFormSelect | <ProFormSelect | ||||
| valueEnum={sexOptions} | valueEnum={sexOptions} | ||||
| @@ -279,7 +289,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| colProps={{ md: 12, xl: 12 }} | colProps={{ md: 12, xl: 12 }} | ||||
| rules={[{ required: true, message: '请选择角色!' }]} | rules={[{ required: true, message: '请选择角色!' }]} | ||||
| /> | /> | ||||
| <ProFormText | |||||
| {/* <ProFormText | |||||
| name="gitLinkUsername" | name="gitLinkUsername" | ||||
| label="Git 用户名" | label="Git 用户名" | ||||
| placeholder="请输入 Git 用户名" | placeholder="请输入 Git 用户名" | ||||
| @@ -300,7 +310,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||||
| autoComplete: 'new-password', | autoComplete: 'new-password', | ||||
| }} | }} | ||||
| rules={props.values.userId ? [] : [{ required: true, message: '请输入 Git 密码!' }]} | rules={props.values.userId ? [] : [{ required: true, message: '请输入 Git 密码!' }]} | ||||
| /> | |||||
| /> */} | |||||
| <ProFormDigit | <ProFormDigit | ||||
| name="credit" | name="credit" | ||||
| label="算力积分" | label="算力积分" | ||||
| @@ -4,7 +4,6 @@ import { getRoleList } from '@/services/system/role'; | |||||
| import { | import { | ||||
| addUser, | addUser, | ||||
| changeUserStatus, | changeUserStatus, | ||||
| exportUser, | |||||
| getDeptTree, | getDeptTree, | ||||
| getUser, | getUser, | ||||
| getUserList, | getUserList, | ||||
| @@ -13,6 +12,7 @@ import { | |||||
| updateAuthRole, | updateAuthRole, | ||||
| updateUser, | updateUser, | ||||
| } from '@/services/system/user'; | } from '@/services/system/user'; | ||||
| import { downloadXlsx } from '@/utils/downloadfile'; | |||||
| import { | import { | ||||
| DeleteOutlined, | DeleteOutlined, | ||||
| DownOutlined, | DownOutlined, | ||||
| @@ -132,15 +132,12 @@ const handleRemoveOne = async (selectedRow: API.System.User) => { | |||||
| /** | /** | ||||
| * 导出数据 | * 导出数据 | ||||
| * | |||||
| * | |||||
| */ | */ | ||||
| const handleExport = async () => { | |||||
| const handleExport = async (deptId: string) => { | |||||
| const hide = message.loading('正在导出'); | const hide = message.loading('正在导出'); | ||||
| try { | try { | ||||
| await exportUser(); | |||||
| await downloadXlsx('/api/system/user/export', 'POST', { data: { deptId: deptId } }); | |||||
| hide(); | hide(); | ||||
| message.success('导出成功'); | |||||
| return true; | return true; | ||||
| } catch (error) { | } catch (error) { | ||||
| hide(); | hide(); | ||||
| @@ -470,7 +467,7 @@ const UserTableList: React.FC = () => { | |||||
| key="export" | key="export" | ||||
| hidden={!access.hasPerms('system:user:export')} | hidden={!access.hasPerms('system:user:export')} | ||||
| onClick={async () => { | onClick={async () => { | ||||
| handleExport(); | |||||
| handleExport(selectDept.id); | |||||
| }} | }} | ||||
| > | > | ||||
| <PlusOutlined />{' '} | <PlusOutlined />{' '} | ||||
| @@ -563,7 +560,7 @@ const UserTableList: React.FC = () => { | |||||
| /> | /> | ||||
| <ResetPwd | <ResetPwd | ||||
| onSubmit={async (values: any) => { | onSubmit={async (values: any) => { | ||||
| const success = await resetUserPwd(values.userId, values.password); | |||||
| const success = await resetUserPwd(values); | |||||
| if (success) { | if (success) { | ||||
| setResetPwdModalVisible(false); | setResetPwdModalVisible(false); | ||||
| setSelectedRows([]); | setSelectedRows([]); | ||||
| @@ -581,7 +578,7 @@ const UserTableList: React.FC = () => { | |||||
| /> | /> | ||||
| <AuthRoleForm | <AuthRoleForm | ||||
| onSubmit={async (values: any) => { | onSubmit={async (values: any) => { | ||||
| const success = await updateAuthRole(values); | |||||
| const success = await updateAuthRole(currentRow!.userId, values.roleIds); | |||||
| if (success) { | if (success) { | ||||
| setAuthRoleModalVisible(false); | setAuthRoleModalVisible(false); | ||||
| setSelectedRows([]); | setSelectedRows([]); | ||||
| @@ -1,4 +1,4 @@ | |||||
| import { useDraggable } from '@/hooks/draggable'; | |||||
| import { useDraggable } from '@/hooks/useDraggable'; | |||||
| import { getWorkspaceOverviewReq } from '@/services/workspace'; | import { getWorkspaceOverviewReq } from '@/services/workspace'; | ||||
| import { ExperimentInstance } from '@/types'; | import { ExperimentInstance } from '@/types'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -53,7 +53,7 @@ export const requestConfig: RequestConfig = { | |||||
| ], | ], | ||||
| responseInterceptors: [ | responseInterceptors: [ | ||||
| [ | [ | ||||
| (response: AxiosResponse) => { | |||||
| async (response: AxiosResponse) => { | |||||
| const { status, data, config } = response || {}; | const { status, data, config } = response || {}; | ||||
| const options = config as RequestOptions; | const options = config as RequestOptions; | ||||
| const skipErrorHandler = options?.skipErrorHandler; | const skipErrorHandler = options?.skipErrorHandler; | ||||
| @@ -63,20 +63,45 @@ export const requestConfig: RequestConfig = { | |||||
| Loading.hide(); | Loading.hide(); | ||||
| } | } | ||||
| if (status >= 200 && status < 300) { | if (status >= 200 && status < 300) { | ||||
| if (status === 204) { | |||||
| // 无内容或者无需验证 | |||||
| if (status === 204 || skipValidating) { | |||||
| return response; | return response; | ||||
| } else if (data && (skipValidating || data instanceof Blob || data.code === 200)) { | |||||
| } | |||||
| if (data && data.code === 200) { | |||||
| return response; | |||||
| } | |||||
| // Blob 数据 | |||||
| if (data && data instanceof Blob && data.size > 0) { | |||||
| // 下载文件失败时,返回的是 JSON 数据,格式为:{code: 500, msg: "xxx"} | |||||
| if (data.type === 'application/json') { | |||||
| try { | |||||
| const text = await data.text(); | |||||
| const json = JSON.parse(text); | |||||
| if (json.code === 500) { | |||||
| popupError(json.msg || '请求失败', skipErrorHandler); | |||||
| return Promise.reject(json); | |||||
| } | |||||
| } catch (error) { | |||||
| console.error('JSON 解析失败', error); | |||||
| } | |||||
| } | |||||
| return response; | return response; | ||||
| } else if (data && data.code === 401) { | |||||
| } | |||||
| // Token 失效 | |||||
| if (data && data.code === 401) { | |||||
| clearSessionToken(); | clearSessionToken(); | ||||
| setRemoteMenu(null); | setRemoteMenu(null); | ||||
| gotoLoginPage(false); | gotoLoginPage(false); | ||||
| popupError('请重新登录'); | popupError('请重新登录'); | ||||
| return Promise.reject(response); | return Promise.reject(response); | ||||
| } else { | |||||
| popupError(data?.msg ?? '请求失败', skipErrorHandler); | |||||
| return Promise.reject(response); | |||||
| } | } | ||||
| popupError(data?.msg ?? '请求失败', skipErrorHandler); | |||||
| return Promise.reject(response); | |||||
| } else { | } else { | ||||
| popupError('请求失败', skipErrorHandler); | popupError('请求失败', skipErrorHandler); | ||||
| return Promise.reject(response); | return Promise.reject(response); | ||||
| @@ -181,4 +181,13 @@ export function compareModelVersion(data) { | |||||
| method: 'POST', | method: 'POST', | ||||
| data, | data, | ||||
| }); | }); | ||||
| } | |||||
| } | |||||
| // 删除上传的文件 | |||||
| export function deleteUploadFileReq(params) { | |||||
| return request(`/api/mmp/newdataset/deleteFile`, { | |||||
| method: 'DELETE', | |||||
| params, | |||||
| }); | |||||
| } | |||||
| @@ -60,8 +60,9 @@ export async function removeUser(ids: string, options?: { [key: string]: any }) | |||||
| // 导出用户信息 | // 导出用户信息 | ||||
| export function exportUser(params?: API.System.UserListParams, options?: { [key: string]: any }) { | export function exportUser(params?: API.System.UserListParams, options?: { [key: string]: any }) { | ||||
| return request<API.Result>(`/api/system/user/export`, { | return request<API.Result>(`/api/system/user/export`, { | ||||
| method: 'GET', | |||||
| params, | |||||
| method: 'POST', | |||||
| data: params, | |||||
| skipValidating: true, | |||||
| ...(options || {}), | ...(options || {}), | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -93,11 +94,7 @@ export function updateUserProfile(data: API.CurrentUser) { | |||||
| } | } | ||||
| // 用户密码重置 | // 用户密码重置 | ||||
| export function resetUserPwd(userId: number, password: string) { | |||||
| const data = { | |||||
| userId, | |||||
| password, | |||||
| }; | |||||
| export function resetUserPwd(data: any) { | |||||
| return request<API.Result>('/api/system/user/resetPwd', { | return request<API.Result>('/api/system/user/resetPwd', { | ||||
| method: 'put', | method: 'put', | ||||
| data: data, | data: data, | ||||
| @@ -126,16 +123,19 @@ export function uploadAvatar(data: any) { | |||||
| // 查询授权角色 | // 查询授权角色 | ||||
| export function getAuthRole(userId: number) { | export function getAuthRole(userId: number) { | ||||
| return request('/system/user/authRole/' + userId, { | |||||
| return request('/api/system/user/authRole/' + userId, { | |||||
| method: 'get', | method: 'get', | ||||
| }); | }); | ||||
| } | } | ||||
| // 保存授权角色 | // 保存授权角色 | ||||
| export function updateAuthRole(data: Record<string, any>) { | |||||
| return request('/system/user/authRole', { | |||||
| method: 'put', | |||||
| params: data, | |||||
| export function updateAuthRole(userId: number, data: Record<string, any>) { | |||||
| return request(`/api/system/user/authRole/${userId}`, { | |||||
| method: 'PUT', | |||||
| headers: { | |||||
| 'Content-Type': 'application/json;charset=UTF-8', | |||||
| }, | |||||
| data: data, | |||||
| }); | }); | ||||
| } | } | ||||
| @@ -76,6 +76,45 @@ export const Select: Story = { | |||||
| }, | }, | ||||
| }; | }; | ||||
| export const Ellipse: Story = { | |||||
| args: { | |||||
| placeholder: '请输入工作目录', | |||||
| style: { width: 300 }, | |||||
| canInput: true, | |||||
| size: 'large', | |||||
| }, | |||||
| render: function Render(args) { | |||||
| const [value, setValue] = useState<ParameterInputValue | undefined>(''); | |||||
| const onClick = () => { | |||||
| const value = { | |||||
| value: 'storybook', | |||||
| showValue: | |||||
| 'storybookstorybookstorybookstorybookstorybookstorybookstorybookstorybookstorybookstorybook', | |||||
| fromSelect: true, | |||||
| otherValue: 'others', | |||||
| }; | |||||
| setValue(value); | |||||
| action('onChange')(value); | |||||
| }; | |||||
| return ( | |||||
| <> | |||||
| <ParameterInput | |||||
| {...args} | |||||
| value={value} | |||||
| onChange={(value) => { | |||||
| setValue(value); | |||||
| action('onChange')(value); | |||||
| }} | |||||
| ></ParameterInput> | |||||
| <Button type="primary" style={{ display: 'block', marginTop: 10 }} onClick={onClick}> | |||||
| 模拟从全局参数选择 | |||||
| </Button> | |||||
| </> | |||||
| ); | |||||
| }, | |||||
| }; | |||||
| export const Disabled: Story = { | export const Disabled: Story = { | ||||
| args: { | args: { | ||||
| placeholder: '请输入工作目录', | placeholder: '请输入工作目录', | ||||
| @@ -136,3 +136,17 @@ export type NodeStatus = { | |||||
| startedAt: string; | startedAt: string; | ||||
| finishedAt: string; | finishedAt: string; | ||||
| }; | }; | ||||
| // 应用响应 | |||||
| export type AppResponse<T> = { | |||||
| code: number; | |||||
| msg: string; | |||||
| data: T; | |||||
| }; | |||||
| // 上传文件的响应 | |||||
| export type UploadFileRes = { | |||||
| fileName: string; | |||||
| fileSize: number; | |||||
| url: string; | |||||
| }; | |||||
| @@ -22,6 +22,7 @@ declare namespace API.System { | |||||
| gitLinkUsername?: string; | gitLinkUsername?: string; | ||||
| gitLinkPassword?: string; | gitLinkPassword?: string; | ||||
| credit?: number; | credit?: number; | ||||
| originPassword?: string; | |||||
| } | } | ||||
| export interface UserListParams { | export interface UserListParams { | ||||
| @@ -1,3 +1,13 @@ | |||||
| export const xlCols = { span: 12 }; | |||||
| export const xllCols = { span: 10 }; | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2025-02-21 09:52:50 | |||||
| * @Description: 通用表单项输入控件宽度 | |||||
| */ | |||||
| const xlCols = { span: 12 }; | |||||
| const xllCols = { span: 10 }; | |||||
| /** | |||||
| * 输入控件宽度,xl: 12, xll: 10 | |||||
| */ | |||||
| export const formCols = { xl: xlCols, xxl: xllCols }; | export const formCols = { xl: xlCols, xxl: xllCols }; | ||||
| @@ -1,7 +1,7 @@ | |||||
| import dayjs from 'dayjs'; | import dayjs from 'dayjs'; | ||||
| /** | /** | ||||
| * Calculates the elapsed time between two dates and returns a formatted string representing the duration. | |||||
| * 计算两个日期之间经过的时间,如 "3分12秒" | |||||
| * | * | ||||
| * @param {string | null | undefined} begin - The starting date. | * @param {string | null | undefined} begin - The starting date. | ||||
| * @param {string | null | undefined} end - The ending date. | * @param {string | null | undefined} end - The ending date. | ||||
| @@ -29,22 +29,30 @@ export const elapsedTime = (begin?: string | null, end?: string | null): string | |||||
| const hours = duration.hours(); | const hours = duration.hours(); | ||||
| const minutes = duration.minutes(); | const minutes = duration.minutes(); | ||||
| const seconds = duration.seconds(); | const seconds = duration.seconds(); | ||||
| const elspsedArray = []; | |||||
| if (years !== 0) { | if (years !== 0) { | ||||
| return `${years}年${months}个月`; | |||||
| elspsedArray.push(`${years}年`); | |||||
| } | } | ||||
| if (months !== 0) { | if (months !== 0) { | ||||
| return `${months}个月${days}天`; | |||||
| elspsedArray.push(`${months}个月`); | |||||
| } | } | ||||
| if (days !== 0) { | if (days !== 0) { | ||||
| return `${days}天${hours}小时`; | |||||
| elspsedArray.push(`${days}天`); | |||||
| } | } | ||||
| if (hours !== 0) { | if (hours !== 0) { | ||||
| return `${hours}小时${minutes}分`; | |||||
| elspsedArray.push(`${hours}小时`); | |||||
| } | } | ||||
| if (minutes !== 0) { | if (minutes !== 0) { | ||||
| return `${minutes}分${seconds}秒`; | |||||
| elspsedArray.push(`${minutes}分`); | |||||
| } | } | ||||
| return `${seconds}秒`; | |||||
| if (seconds !== 0) { | |||||
| elspsedArray.push(`${seconds}秒`); | |||||
| } | |||||
| if (elspsedArray.length === 0) { | |||||
| return '0秒'; | |||||
| } | |||||
| return elspsedArray.slice(0, 2).join(''); | |||||
| }; | }; | ||||
| /** | /** | ||||
| @@ -1,19 +1,21 @@ | |||||
| import { request } from '@umijs/max'; | import { request } from '@umijs/max'; | ||||
| const mimeMap = { | |||||
| /** MimeType */ | |||||
| export const mimeMap = { | |||||
| xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', | xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', | ||||
| zip: 'application/zip', | zip: 'application/zip', | ||||
| }; | }; | ||||
| /** | /** | ||||
| * 解析blob响应内容并下载 | * 解析blob响应内容并下载 | ||||
| * @param {*} res blob响应内容 | |||||
| * @param {String} mimeType MIME类型 | |||||
| * @param res - blob响应内容 | |||||
| * @param mimeType - MIME类型 | |||||
| */ | */ | ||||
| export function resolveBlob(res: any, mimeType: string) { | export function resolveBlob(res: any, mimeType: string) { | ||||
| const aLink = document.createElement('a'); | const aLink = document.createElement('a'); | ||||
| const blob = new Blob([res.data], { type: mimeType }); | const blob = new Blob([res.data], { type: mimeType }); | ||||
| // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名; | |||||
| // 从response的headers中获取filename, | |||||
| // 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名; | |||||
| const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*'); | const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*'); | ||||
| // console.log(res); | // console.log(res); | ||||
| const contentDisposition = decodeURI(res.headers['content-disposition']); | const contentDisposition = decodeURI(res.headers['content-disposition']); | ||||
| @@ -29,6 +31,11 @@ export function resolveBlob(res: any, mimeType: string) { | |||||
| document.body.removeChild(aLink); | document.body.removeChild(aLink); | ||||
| } | } | ||||
| /** | |||||
| * 下载 Zip 文件 | |||||
| * @param url - url 地址 | |||||
| * @param options - 请求参数 | |||||
| */ | |||||
| export function downLoadZip(url: string, params?: any) { | export function downLoadZip(url: string, params?: any) { | ||||
| request(url, { | request(url, { | ||||
| method: 'GET', | method: 'GET', | ||||
| @@ -40,24 +47,30 @@ export function downLoadZip(url: string, params?: any) { | |||||
| }); | }); | ||||
| } | } | ||||
| export async function downLoadXlsx(url: string, params: any, fileName: string) { | |||||
| /** | |||||
| * 下载 XLSX 文件 | |||||
| * @param url - url 地址 | |||||
| * @param method - 请求方式 | |||||
| * @param options - 请求选项 | |||||
| */ | |||||
| export async function downloadXlsx( | |||||
| url: string, | |||||
| method: string = 'GET', | |||||
| options?: Record<string, any>, | |||||
| ) { | |||||
| return request(url, { | return request(url, { | ||||
| ...params, | |||||
| method: 'POST', | |||||
| method: method, | |||||
| ...options, | |||||
| responseType: 'blob', | responseType: 'blob', | ||||
| }).then((data) => { | |||||
| const aLink = document.createElement('a'); | |||||
| const blob = data as any; // new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); | |||||
| aLink.style.display = 'none'; | |||||
| aLink.href = URL.createObjectURL(blob); | |||||
| aLink.setAttribute('download', fileName); // 设置下载文件名称 | |||||
| document.body.appendChild(aLink); | |||||
| aLink.click(); | |||||
| URL.revokeObjectURL(aLink.href); // 清除引用 | |||||
| document.body.removeChild(aLink); | |||||
| getResponse: true, | |||||
| }).then((res) => { | |||||
| resolveBlob(res, mimeMap.xlsx); | |||||
| }); | }); | ||||
| } | } | ||||
| /** | |||||
| * @deprecated 无效 | |||||
| */ | |||||
| export function download(fileName: string) { | export function download(fileName: string) { | ||||
| window.location.href = `/api/common/download?fileName=${encodeURI(fileName)}&delete=${true}`; | window.location.href = `/api/common/download?fileName=${encodeURI(fileName)}&delete=${true}`; | ||||
| } | } | ||||
| @@ -1,3 +1,4 @@ | |||||
| import { BasicInfoLink } from '@/components/BasicInfo/types'; | |||||
| import { ResourceSelectorResponse } from '@/components/ResourceSelectorModal'; | import { ResourceSelectorResponse } from '@/components/ResourceSelectorModal'; | ||||
| import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceInfo'; | import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceInfo'; | ||||
| import { | import { | ||||
| @@ -18,8 +19,13 @@ type SelectedCodeConfig = { | |||||
| show_value?: string; // 后端使用的 | show_value?: string; // 后端使用的 | ||||
| }; | }; | ||||
| // 格式化数据集数组 | |||||
| export const formatDatasets = (datasets?: DatasetData[]) => { | |||||
| /** | |||||
| * 格式化数据集数组 | |||||
| * | |||||
| * @param datasets - 数据集数组 | |||||
| * @return 基本信息链接对象数组 | |||||
| */ | |||||
| export const formatDatasets = (datasets?: DatasetData[]): BasicInfoLink[] | undefined => { | |||||
| if (!datasets || datasets.length === 0) { | if (!datasets || datasets.length === 0) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| @@ -29,8 +35,13 @@ export const formatDatasets = (datasets?: DatasetData[]) => { | |||||
| })); | })); | ||||
| }; | }; | ||||
| // 格式化数据集 | |||||
| export const formatDataset = (dataset?: DatasetData) => { | |||||
| /** | |||||
| * 格式化数据集 | |||||
| * | |||||
| * @param dataset - 数据集 | |||||
| * @return 基本信息链接对象 | |||||
| */ | |||||
| export const formatDataset = (dataset?: DatasetData): BasicInfoLink | undefined => { | |||||
| if (!dataset) { | if (!dataset) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| @@ -40,8 +51,13 @@ export const formatDataset = (dataset?: DatasetData) => { | |||||
| }; | }; | ||||
| }; | }; | ||||
| // 格式化模型 | |||||
| export const formatModel = (model: ModelData) => { | |||||
| /** | |||||
| * 格式化模型 | |||||
| * | |||||
| * @param model - 模型 | |||||
| * @return 基本信息链接对象 | |||||
| */ | |||||
| export const formatModel = (model: ModelData): BasicInfoLink | undefined => { | |||||
| if (!model) { | if (!model) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| @@ -51,16 +67,28 @@ export const formatModel = (model: ModelData) => { | |||||
| }; | }; | ||||
| }; | }; | ||||
| // 格式化镜像 | |||||
| export const formatMirror = (mirror: ResourceSelectorResponse) => { | |||||
| /** | |||||
| * 格式化镜像 | |||||
| * | |||||
| * @param mirror - 选择的镜像 | |||||
| * @return 镜像地址 | |||||
| */ | |||||
| export const formatMirror = (mirror: ResourceSelectorResponse): string | undefined => { | |||||
| if (!mirror) { | if (!mirror) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| return mirror.path; | return mirror.path; | ||||
| }; | }; | ||||
| // 格式化代码配置 | |||||
| export const formatCodeConfig = (project?: ProjectDependency | SelectedCodeConfig) => { | |||||
| /** | |||||
| * 格式化代码配置 | |||||
| * | |||||
| * @param project - 代码配置或者选择的代码配置 | |||||
| * @return 基本信息链接对象 | |||||
| */ | |||||
| export const formatCodeConfig = ( | |||||
| project?: ProjectDependency | SelectedCodeConfig, | |||||
| ): BasicInfoLink | undefined => { | |||||
| if (!project) { | if (!project) { | ||||
| return undefined; | return undefined; | ||||
| } | } | ||||
| @@ -81,7 +109,12 @@ export const formatCodeConfig = (project?: ProjectDependency | SelectedCodeConfi | |||||
| } | } | ||||
| }; | }; | ||||
| // 格式化训练任务(实验实例) | |||||
| /** | |||||
| * 格式化训练任务(实验实例) | |||||
| * | |||||
| * @param task - 训练任务 | |||||
| * @return 基本信息链接对象 | |||||
| */ | |||||
| export const formatTrainTask = (task?: TrainTask) => { | export const formatTrainTask = (task?: TrainTask) => { | ||||
| if (!task) { | if (!task) { | ||||
| return undefined; | return undefined; | ||||
| @@ -92,8 +125,13 @@ export const formatTrainTask = (task?: TrainTask) => { | |||||
| }; | }; | ||||
| }; | }; | ||||
| // 格式化数据来源 | |||||
| export const formatSource = (source?: string) => { | |||||
| /** | |||||
| * 格式化数据来源 | |||||
| * | |||||
| * @param source - 数据来源枚举值 | |||||
| * @return 数据来源中文名称 | |||||
| */ | |||||
| export const formatSource = (source?: string): string | undefined => { | |||||
| if (source === DataSource.Create) { | if (source === DataSource.Create) { | ||||
| return '用户上传'; | return '用户上传'; | ||||
| } else if (source === DataSource.HandExport) { | } else if (source === DataSource.HandExport) { | ||||
| @@ -106,7 +144,12 @@ export const formatSource = (source?: string) => { | |||||
| return source; | return source; | ||||
| }; | }; | ||||
| // 格式化字符串数组,以逗号分隔 | |||||
| /** | |||||
| * 格式化字符串数组,以逗号分隔 | |||||
| * | |||||
| * @param value - 字符串数组 | |||||
| * @return 字符串,以逗号分隔 | |||||
| */ | |||||
| export const formatList = (value: string[] | null | undefined): string => { | export const formatList = (value: string[] | null | undefined): string => { | ||||
| if ( | if ( | ||||
| value === undefined || | value === undefined || | ||||
| @@ -119,14 +162,24 @@ export const formatList = (value: string[] | null | undefined): string => { | |||||
| return value.join(','); | return value.join(','); | ||||
| }; | }; | ||||
| // 格式化布尔值 | |||||
| /** | |||||
| * 格式化布尔值 | |||||
| * | |||||
| * @param value - 布尔值 | |||||
| * @return true 为 "是",false 为 "否" | |||||
| */ | |||||
| export const formatBoolean = (value: boolean): string => { | export const formatBoolean = (value: boolean): string => { | ||||
| return value ? '是' : '否'; | return value ? '是' : '否'; | ||||
| }; | }; | ||||
| type FormatEnumFunc = (value: string | number) => React.ReactNode; | type FormatEnumFunc = (value: string | number) => React.ReactNode; | ||||
| // 格式化枚举 | |||||
| /** | |||||
| * 格式化枚举 | |||||
| * | |||||
| * @param options - 枚举选项数组 | |||||
| * @return 一个函数,参数是枚举值,从选项数组中找到对应的项,然后返回该项的 label | |||||
| */ | |||||
| export const formatEnum = ( | export const formatEnum = ( | ||||
| options: { value?: string | number | null; label?: React.ReactNode }[], | options: { value?: string | number | null; label?: React.ReactNode }[], | ||||
| ): FormatEnumFunc => { | ): FormatEnumFunc => { | ||||
| @@ -28,7 +28,7 @@ export class Loading { | |||||
| } | } | ||||
| const container = document.createElement('div'); | const container = document.createElement('div'); | ||||
| container.id = 'loading'; | container.id = 'loading'; | ||||
| const rootContainer = document.body; //document.getElementsByTagName('main')[0]; | |||||
| const rootContainer = document.body; // document.getElementsByTagName('main')[0]; | |||||
| rootContainer?.appendChild(container); | rootContainer?.appendChild(container); | ||||
| const root = createRoot(container); | const root = createRoot(container); | ||||
| const global = globalConfig(); | const global = globalConfig(); | ||||
| @@ -69,10 +69,9 @@ export class Loading { | |||||
| static removeLoading() { | static removeLoading() { | ||||
| this.clearRemoveTimeout(); | this.clearRemoveTimeout(); | ||||
| const rootContainer = document.body; //document.getElementsByTagName('main')[0]; | |||||
| const container = document.getElementById('loading'); | const container = document.getElementById('loading'); | ||||
| if (container) { | if (container) { | ||||
| rootContainer?.removeChild(container); | |||||
| container.parentNode?.removeChild(container); | |||||
| } | } | ||||
| this.isShowing = false; | this.isShowing = false; | ||||
| } | } | ||||
| @@ -6,6 +6,11 @@ export default class LocalStorage { | |||||
| // 记住密码 | // 记住密码 | ||||
| static readonly rememberPasswordKey = 'login-remember-password'; | static readonly rememberPasswordKey = 'login-remember-password'; | ||||
| /** | |||||
| * 获取 LocalStorage 值 | |||||
| * @param key - LocalStorage key | |||||
| * @param isObject - 是不是对象 | |||||
| */ | |||||
| static getItem(key: string, isObject: boolean = false) { | static getItem(key: string, isObject: boolean = false) { | ||||
| const jsonStr = localStorage.getItem(key); | const jsonStr = localStorage.getItem(key); | ||||
| if (!isObject) { | if (!isObject) { | ||||
| @@ -17,12 +22,22 @@ export default class LocalStorage { | |||||
| return null; | return null; | ||||
| } | } | ||||
| /** | |||||
| * 设置 LocalStorage 值 | |||||
| * @param key - LocalStorage key | |||||
| * @param state - SessionStorage state | |||||
| * @param isObject - 是不是对象 | |||||
| */ | |||||
| static setItem(key: string, state?: any, isObject: boolean = false) { | static setItem(key: string, state?: any, isObject: boolean = false) { | ||||
| if (state) { | if (state) { | ||||
| localStorage.setItem(key, isObject ? JSON.stringify(state) : state); | localStorage.setItem(key, isObject ? JSON.stringify(state) : state); | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * 移除 LocalStorage 值 | |||||
| * @param key - LocalStorage key | |||||
| */ | |||||
| static removeItem(key: string) { | static removeItem(key: string) { | ||||
| localStorage.removeItem(key); | localStorage.removeItem(key); | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| /** | /** | ||||
| * @param { Promise } promise | |||||
| * @return { Promise } | |||||
| * 封装 Promise,不会抛异常,resolve 的时候返回 [data, null], reject 的时候返回 [null, error] | |||||
| * @param promise | |||||
| * @return resolve 的时候返回 [data, null], reject 的时候返回 [null, error] | |||||
| */ | */ | ||||
| export async function to<T, U = any>(promise: Promise<T>): Promise<[T, null] | [null, U]> { | export async function to<T, U = any>(promise: Promise<T>): Promise<[T, null] | [null, U]> { | ||||
| try { | try { | ||||
| @@ -1,15 +1,22 @@ | |||||
| import { parseJsonText } from './index'; | import { parseJsonText } from './index'; | ||||
| export default class SessionStorage { | export default class SessionStorage { | ||||
| // 用于新建镜像 | |||||
| /** 用于新建镜像 */ | |||||
| static readonly mirrorNameKey = 'mirror-name'; | static readonly mirrorNameKey = 'mirror-name'; | ||||
| // 模型部署服务版本 | |||||
| /** 模型部署服务版本 */ | |||||
| static readonly serviceVersionInfoKey = 'service-version-info'; | static readonly serviceVersionInfoKey = 'service-version-info'; | ||||
| // 编辑器 url | |||||
| /** 编辑器 url */ | |||||
| static readonly editorUrlKey = 'editor-url'; | static readonly editorUrlKey = 'editor-url'; | ||||
| // 客户端信息 | |||||
| /** 客户端信息 */ | |||||
| static readonly clientInfoKey = 'client-info'; | static readonly clientInfoKey = 'client-info'; | ||||
| /** aim url */ | |||||
| static readonly aimUrlKey = 'aim-url'; | |||||
| /** | |||||
| * 获取 SessionStorage 值 | |||||
| * @param key - SessionStorage key | |||||
| * @param isObject - 是不是对象 | |||||
| */ | |||||
| static getItem(key: string, isObject: boolean = false) { | static getItem(key: string, isObject: boolean = false) { | ||||
| const jsonStr = sessionStorage.getItem(key); | const jsonStr = sessionStorage.getItem(key); | ||||
| if (!isObject) { | if (!isObject) { | ||||
| @@ -21,12 +28,22 @@ export default class SessionStorage { | |||||
| return null; | return null; | ||||
| } | } | ||||
| /** | |||||
| * 设置 SessionStorage 值 | |||||
| * @param key - SessionStorage key | |||||
| * @param state - SessionStorage state | |||||
| * @param isObject - 是不是对象 | |||||
| */ | |||||
| static setItem(key: string, state?: any, isObject: boolean = false) { | static setItem(key: string, state?: any, isObject: boolean = false) { | ||||
| if (state) { | if (state) { | ||||
| sessionStorage.setItem(key, isObject ? JSON.stringify(state) : state); | sessionStorage.setItem(key, isObject ? JSON.stringify(state) : state); | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * 移除 SessionStorage 值 | |||||
| * @param key - SessionStorage key | |||||
| */ | |||||
| static removeItem(key: string) { | static removeItem(key: string) { | ||||
| sessionStorage.removeItem(key); | sessionStorage.removeItem(key); | ||||
| } | } | ||||
| @@ -10,6 +10,10 @@ export type StatusInfo = { | |||||
| color?: string; | color?: string; | ||||
| }; | }; | ||||
| /** | |||||
| * 通用的 Table 状态单元格 | |||||
| * @param infos - 选项数组 | |||||
| */ | |||||
| function statusTableCell(infos: StatusInfo[]) { | function statusTableCell(infos: StatusInfo[]) { | ||||
| return function (status?: string | number | null) { | return function (status?: string | number | null) { | ||||
| const info = infos.find((item) => item.value === status); | const info = infos.find((item) => item.value === status); | ||||
| @@ -10,27 +10,44 @@ import { Tooltip, TooltipProps, Typography } from 'antd'; | |||||
| import dayjs from 'dayjs'; | import dayjs from 'dayjs'; | ||||
| export enum TableCellValueType { | export enum TableCellValueType { | ||||
| /** 序号 */ | |||||
| Index = 'Index', | Index = 'Index', | ||||
| /** 文本 */ | |||||
| Text = 'Text', | Text = 'Text', | ||||
| /** 日期 */ | |||||
| Date = 'Date', | Date = 'Date', | ||||
| /** 数组 */ | |||||
| Array = 'Array', | Array = 'Array', | ||||
| /** 链接 */ | |||||
| Link = 'Link', | Link = 'Link', | ||||
| /** 自定义 */ | |||||
| Custom = 'Custom', | Custom = 'Custom', | ||||
| } | } | ||||
| export type TableCellValueOptions<T> = { | export type TableCellValueOptions<T> = { | ||||
| page?: number; // 类型为 Index 时有效 | |||||
| pageSize?: number; // 类型为 Index 时有效 | |||||
| property?: string; // 类型为 Array 时有效 | |||||
| dateFormat?: string; // 类型为 Date 时有效 | |||||
| onClick?: (record: T, e: React.MouseEvent) => void; // 类型为 Link 时有效 | |||||
| format?: (value: any | undefined | null, record: T, index: number) => string | undefined | null; // 类型为 Custom 时有效 | |||||
| copyable?: boolean; // 省略时是否可以复制 | |||||
| /** 页数,类型为 Index 时有效 */ | |||||
| page?: number; | |||||
| /** 分页大小,类型为 Index 时有效 */ | |||||
| pageSize?: number; | |||||
| /** 取数组对象的哪个属性值,类型为 Array 时有效 */ | |||||
| property?: string; | |||||
| /** 日期格式,类型为 Date 时有效*/ | |||||
| dateFormat?: string; | |||||
| /** 链接点击回调,类型为 Link 时有效 */ | |||||
| onClick?: (record: T, e: React.MouseEvent) => void; | |||||
| /** 自定义函数,类型为 Custom 时有效*/ | |||||
| format?: (value: any | undefined | null, record: T, index: number) => string | undefined | null; | |||||
| /** 省略时是否可以复制 */ | |||||
| copyable?: boolean; | |||||
| }; | }; | ||||
| type TableCellFormatter = (value: any | undefined | null) => string | undefined | null; | type TableCellFormatter = (value: any | undefined | null) => string | undefined | null; | ||||
| // 日期转换函数 | |||||
| /** | |||||
| * 日期转换函数 | |||||
| * @param {string | undefined} dateFormat - 日期格式 | |||||
| * @returns {TableCellFormatter} Table cell 渲染函数 | |||||
| */ | |||||
| function formatDateText(dateFormat?: string): TableCellFormatter { | function formatDateText(dateFormat?: string): TableCellFormatter { | ||||
| return (value: any | undefined | null): ReturnType<TableCellFormatter> => { | return (value: any | undefined | null): ReturnType<TableCellFormatter> => { | ||||
| if (value === undefined || value === null || value === '') { | if (value === undefined || value === null || value === '') { | ||||
| @@ -45,7 +62,7 @@ function formatDateText(dateFormat?: string): TableCellFormatter { | |||||
| /** | /** | ||||
| * 数组转换函数,将数组元素转换为字符串,用逗号分隔 | * 数组转换函数,将数组元素转换为字符串,用逗号分隔 | ||||
| * @param {string} property 如果数组元素是对象,那么取数组元素的某个属性 | |||||
| * @param {string} property - 如果数组元素是对象,那么取数组元素的某个属性 | |||||
| * @returns {TableCellFormatter} Table cell 渲染函数 | * @returns {TableCellFormatter} Table cell 渲染函数 | ||||
| */ | */ | ||||
| function formatArray(property?: string): TableCellFormatter { | function formatArray(property?: string): TableCellFormatter { | ||||
| @@ -65,6 +82,13 @@ function formatArray(property?: string): TableCellFormatter { | |||||
| }; | }; | ||||
| } | } | ||||
| /** | |||||
| * Table cell render 函数 | |||||
| * @param ellipsis - 是否省略 | |||||
| * @param type - 类型 | |||||
| * @param options - 选项 | |||||
| * @returns React 节点 | |||||
| */ | |||||
| function tableCellRender<T>( | function tableCellRender<T>( | ||||
| ellipsis: boolean | TooltipProps | 'auto' = false, | ellipsis: boolean | TooltipProps | 'auto' = false, | ||||
| type: TableCellValueType = TableCellValueType.Text, | type: TableCellValueType = TableCellValueType.Text, | ||||
| @@ -4,9 +4,11 @@ | |||||
| * @Description: UI 公共方法 | * @Description: UI 公共方法 | ||||
| */ | */ | ||||
| import { PageEnum } from '@/enums/pagesEnums'; | import { PageEnum } from '@/enums/pagesEnums'; | ||||
| import { removeAllPageCacheState } from '@/hooks/pageCacheState'; | |||||
| import { removeAllPageCacheState } from '@/hooks/useCacheState'; | |||||
| import { deleteUploadFileReq } from '@/services/dataset/index.js'; | |||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { type ClientInfo } from '@/types'; | |||||
| import { type AppResponse, type ClientInfo, type UploadFileRes } from '@/types'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { history } from '@umijs/max'; | import { history } from '@umijs/max'; | ||||
| import { | import { | ||||
| Modal, | Modal, | ||||
| @@ -25,7 +27,9 @@ type ModalConfirmProps = ModalFuncProps & { | |||||
| isDelete?: boolean; | isDelete?: boolean; | ||||
| }; | }; | ||||
| // 自定义删除 Confirm 弹框 | |||||
| /** | |||||
| * 自定义 Confirm 弹框 | |||||
| */ | |||||
| export function modalConfirm({ | export function modalConfirm({ | ||||
| title, | title, | ||||
| content, | content, | ||||
| @@ -45,7 +49,7 @@ export function modalConfirm({ | |||||
| src={ | src={ | ||||
| isDelete | isDelete | ||||
| ? require('@/assets/img/delete-icon.png') | ? require('@/assets/img/delete-icon.png') | ||||
| : require('@/assets/img/comfirm-icon.png') | |||||
| : require('@/assets/img/confirm-icon.png') | |||||
| } | } | ||||
| style={{ width: '120px', marginBottom: '24px' }} | style={{ width: '120px', marginBottom: '24px' }} | ||||
| draggable={false} | draggable={false} | ||||
| @@ -63,7 +67,7 @@ export function modalConfirm({ | |||||
| /** | /** | ||||
| * 跳转到登录页 | * 跳转到登录页 | ||||
| * @param toHome 是否跳转到首页 | |||||
| * @param toHome - 是否跳转到首页 | |||||
| */ | */ | ||||
| export const gotoLoginPage = (toHome: boolean = true) => { | export const gotoLoginPage = (toHome: boolean = true) => { | ||||
| const { pathname, search } = location; | const { pathname, search } = location; | ||||
| @@ -80,6 +84,9 @@ export const gotoLoginPage = (toHome: boolean = true) => { | |||||
| } | } | ||||
| }; | }; | ||||
| /** | |||||
| * 跳转到 OAuth2 登录页 | |||||
| */ | |||||
| export const gotoOAuth2 = () => { | export const gotoOAuth2 = () => { | ||||
| const clientInfo = SessionStorage.getItem(SessionStorage.clientInfoKey, true) as ClientInfo; | const clientInfo = SessionStorage.getItem(SessionStorage.clientInfoKey, true) as ClientInfo; | ||||
| if (clientInfo) { | if (clientInfo) { | ||||
| @@ -89,7 +96,10 @@ export const gotoOAuth2 = () => { | |||||
| } | } | ||||
| }; | }; | ||||
| // 从事件中获取上传文件列表,用于 Upload + Form 中 | |||||
| /** | |||||
| * 从事件中获取上传文件列表,用于 Upload + Form 中 | |||||
| * @param e - 事件,包含文件列表 fileList | |||||
| */ | |||||
| export const getFileListFromEvent = (e: any) => { | export const getFileListFromEvent = (e: any) => { | ||||
| const fileList: UploadFile[] = (Array.isArray(e) ? e : e?.fileList) || []; | const fileList: UploadFile[] = (Array.isArray(e) ? e : e?.fileList) || []; | ||||
| return fileList.map((item) => { | return fileList.map((item) => { | ||||
| @@ -137,7 +147,10 @@ export const validateUploadFiles = (files: UploadFile[], required: boolean = tru | |||||
| return !hasError; | return !hasError; | ||||
| }; | }; | ||||
| // 限制上传文件类型 | |||||
| /** | |||||
| * 限制上传文件类型 | |||||
| * @param type - 只允许上次的的文件类型 | |||||
| */ | |||||
| export const limitUploadFileType = (type: string) => { | export const limitUploadFileType = (type: string) => { | ||||
| return (file: UploadFile): boolean | string => { | return (file: UploadFile): boolean | string => { | ||||
| const acceptTypes = type.split(',').map((item) => item.trim()); | const acceptTypes = type.split(',').map((item) => item.trim()); | ||||
| @@ -151,14 +164,40 @@ export const limitUploadFileType = (type: string) => { | |||||
| }; | }; | ||||
| }; | }; | ||||
| /** | |||||
| * 删除已上传的文件 | |||||
| * @param file - 已上传的文件 | |||||
| */ | |||||
| export const removeUploadedFile = async (file: UploadFile<AppResponse<UploadFileRes[]>>) => { | |||||
| const { status, response } = file; | |||||
| const { code, data } = response ?? {}; | |||||
| if (status === 'done' && code === 200 && Array.isArray(data) && data.length > 0) { | |||||
| const uploadRes = data[0]; | |||||
| const { fileName, url } = uploadRes; | |||||
| const [res] = await to( | |||||
| deleteUploadFileReq({ | |||||
| fileName, | |||||
| url, | |||||
| }), | |||||
| ); | |||||
| if (res) { | |||||
| return true; | |||||
| } else { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| }; | |||||
| /** | /** | ||||
| * 删除 FormList 表单项,如果表单项没有值,则直接删除,否则弹出确认框 | * 删除 FormList 表单项,如果表单项没有值,则直接删除,否则弹出确认框 | ||||
| * @param form From实例 | |||||
| * @param listName FormList 的 name | |||||
| * @param name FormList 的其中一项 | |||||
| * @param remove FormList 的删除方法 | |||||
| * @param fieldNames FormList 的子项名称数组 | |||||
| * @param confirmTitle 弹出确认框的标题 | |||||
| * @param form - From实例 | |||||
| * @param listName - FormList 的 name | |||||
| * @param name - FormList 的其中一项 | |||||
| * @param remove - FormList 的删除方法 | |||||
| * @param fieldNames - FormList 的子项名称数组 | |||||
| * @param confirmTitle - 弹出确认框的标题 | |||||
| */ | */ | ||||
| export const removeFormListItem = ( | export const removeFormListItem = ( | ||||
| form: FormInstance, | form: FormInstance, | ||||
| @@ -1,5 +1,5 @@ | |||||
| { | { | ||||
| "entryPoints": ["./src/utils"], | |||||
| "entryPoints": ["./src/utils", "./src/hooks"], | |||||
| "entryPointStrategy": "expand", | "entryPointStrategy": "expand", | ||||
| "out": "docs", | "out": "docs", | ||||
| "excludePrivate": true, | "excludePrivate": true, | ||||
| @@ -7,5 +7,14 @@ | |||||
| "excludeExternals": true, | "excludeExternals": true, | ||||
| "includeVersion": true, | "includeVersion": true, | ||||
| "categorizeByGroup": true, | "categorizeByGroup": true, | ||||
| "skipErrorChecking": true, | |||||
| "exclude": [ | |||||
| "src/utils/formRules.ts", | |||||
| "src/utils/loading.tsx", | |||||
| "src/utils/menuRender.tsx", | |||||
| "src/utils/IconUtil.ts", | |||||
| "src/utils/permission.ts", | |||||
| "src/utils/tree.ts" | |||||
| ], | |||||
| "name": "工具类文档" | "name": "工具类文档" | ||||
| } | } | ||||
| @@ -22,7 +22,13 @@ | |||||
| <groupId>com.ruoyi</groupId> | <groupId>com.ruoyi</groupId> | ||||
| <artifactId>ruoyi-common-core</artifactId> | <artifactId>ruoyi-common-core</artifactId> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>com.alibaba</groupId> | |||||
| <artifactId>easyexcel-core</artifactId> | |||||
| <version>3.3.2</version> | |||||
| <scope>compile</scope> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| </project> | </project> | ||||
| @@ -0,0 +1,21 @@ | |||||
| package com.ruoyi.system.api; | |||||
| import com.ruoyi.common.core.constant.ServiceNameConstants; | |||||
| import com.ruoyi.common.core.web.domain.AjaxResult; | |||||
| import com.ruoyi.system.api.domain.SysUser; | |||||
| import com.ruoyi.system.api.factory.RemoteAuthFallbackFactory; | |||||
| import org.springframework.cloud.openfeign.FeignClient; | |||||
| import org.springframework.validation.annotation.Validated; | |||||
| import org.springframework.web.bind.annotation.PostMapping; | |||||
| import org.springframework.web.bind.annotation.PutMapping; | |||||
| import org.springframework.web.bind.annotation.RequestBody; | |||||
| @FeignClient(contextId = "remoteAuthService", value = ServiceNameConstants.AUTH_SERVICE, fallbackFactory = RemoteAuthFallbackFactory.class) | |||||
| public interface RemoteAuthService { | |||||
| @PostMapping("/oauth2") | |||||
| public AjaxResult add(@Validated @RequestBody SysUser user); | |||||
| @PutMapping("/oauth2") | |||||
| public AjaxResult edit(@Validated @RequestBody SysUser user); | |||||
| } | |||||
| @@ -0,0 +1,30 @@ | |||||
| package com.ruoyi.system.api; | |||||
| import com.ruoyi.common.core.constant.ServiceNameConstants; | |||||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | |||||
| import com.ruoyi.system.api.domain.SysUser; | |||||
| import com.ruoyi.system.api.factory.RemoteMmpFallbackFactory; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import org.springframework.cloud.openfeign.FeignClient; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| @FeignClient(contextId = "remoteMmpService", value = ServiceNameConstants.MANAGEMENT_SERVICE, fallbackFactory = RemoteMmpFallbackFactory.class) | |||||
| public interface RemoteMmpService { | |||||
| @GetMapping("/gitLink/login") | |||||
| public GenericsAjaxResult<String> gitLinkLogin(@RequestParam("username") String username, @RequestParam("password") String password); | |||||
| @PostMapping("/gitLink/createGitLinkUser") | |||||
| public GenericsAjaxResult<String> createGitLinkUser(@RequestBody SysUser sysUser) throws Exception; | |||||
| @PostMapping("/gitLink/resetPwd") | |||||
| public GenericsAjaxResult<String> resetPwd(@RequestBody SysUser sysUser) throws Exception; | |||||
| @PutMapping("/gitLink/resetEmail") | |||||
| public GenericsAjaxResult<String> resetEmail(@RequestBody SysUser sysUser) throws Exception; | |||||
| @PutMapping("/gitLink/resetPhoneNum") | |||||
| public GenericsAjaxResult<String> resetPhoneNum(@RequestBody SysUser sysUser) throws Exception; | |||||
| @DeleteMapping("/gitLink/deleteGitLinkUser") | |||||
| public GenericsAjaxResult<String> deleteGitLinkUser(@RequestBody SysUser sysUser) throws Exception; | |||||
| } | |||||
| @@ -1,11 +1,7 @@ | |||||
| package com.ruoyi.system.api; | package com.ruoyi.system.api; | ||||
| import org.springframework.cloud.openfeign.FeignClient; | import org.springframework.cloud.openfeign.FeignClient; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | |||||
| import org.springframework.web.bind.annotation.PathVariable; | |||||
| import org.springframework.web.bind.annotation.PostMapping; | |||||
| import org.springframework.web.bind.annotation.RequestBody; | |||||
| import org.springframework.web.bind.annotation.RequestHeader; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| import com.ruoyi.common.core.constant.SecurityConstants; | import com.ruoyi.common.core.constant.SecurityConstants; | ||||
| import com.ruoyi.common.core.constant.ServiceNameConstants; | import com.ruoyi.common.core.constant.ServiceNameConstants; | ||||
| import com.ruoyi.common.core.domain.R; | import com.ruoyi.common.core.domain.R; | ||||
| @@ -1,7 +1,6 @@ | |||||
| package com.ruoyi.platform.constant; | |||||
| package com.ruoyi.system.api.constant; | |||||
| public class Constant { | public class Constant { | ||||
| public final static int Image_Type_Pub = 1; // 公共镜像 | public final static int Image_Type_Pub = 1; // 公共镜像 | ||||
| public final static int Image_Type_Pri = 0; // 私有镜像 | public final static int Image_Type_Pri = 0; // 私有镜像 | ||||
| @@ -22,8 +21,8 @@ public class Constant { | |||||
| public final static int Git_Category_Id = 39; | public final static int Git_Category_Id = 39; | ||||
| public final static String Topic_Dataset = "ci4s-dataset"; | |||||
| public final static String Topic_model = "ci4s-model"; | |||||
| public final static String Topic_Dataset = "dataset"; | |||||
| public final static String Topic_model = "model"; | |||||
| public final static String Item_Public = "public"; | public final static String Item_Public = "public"; | ||||
| @@ -31,6 +30,8 @@ public class Constant { | |||||
| public final static String Source_Hand_Export = "hand_export"; | public final static String Source_Hand_Export = "hand_export"; | ||||
| public final static String Source_Add = "add"; | public final static String Source_Add = "add"; | ||||
| public final static String Building = "building"; | |||||
| public final static String Running = "Running"; | public final static String Running = "Running"; | ||||
| public final static String Failed = "Failed"; | public final static String Failed = "Failed"; | ||||
| public final static String Pending = "Pending"; | public final static String Pending = "Pending"; | ||||
| @@ -56,4 +57,7 @@ public class Constant { | |||||
| public final static String TaskType_Ray = "ray"; | public final static String TaskType_Ray = "ray"; | ||||
| public final static String TaskType_ActiveLearn = "active_learn"; | public final static String TaskType_ActiveLearn = "active_learn"; | ||||
| public final static String TaskType_Service = "service"; | public final static String TaskType_Service = "service"; | ||||
| public final static String DelFlag = "2"; | |||||
| public final static String Code = "123123"; | |||||
| } | } | ||||
| @@ -1,7 +1,11 @@ | |||||
| package com.ruoyi.system.api.domain; | package com.ruoyi.system.api.domain; | ||||
| import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | |||||
| import com.alibaba.excel.annotation.ExcelProperty; | |||||
| import com.alibaba.excel.annotation.write.style.ContentStyle; | |||||
| import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum; | |||||
| import com.alibaba.excel.enums.poi.VerticalAlignmentEnum; | |||||
| import com.ruoyi.common.core.annotation.Excel; | import com.ruoyi.common.core.annotation.Excel; | ||||
| import com.ruoyi.common.core.annotation.Excel.ColumnType; | |||||
| import com.ruoyi.common.core.annotation.Excel.Type; | import com.ruoyi.common.core.annotation.Excel.Type; | ||||
| import com.ruoyi.common.core.annotation.Excels; | import com.ruoyi.common.core.annotation.Excels; | ||||
| import com.ruoyi.common.core.web.domain.BaseEntity; | import com.ruoyi.common.core.web.domain.BaseEntity; | ||||
| @@ -20,49 +24,52 @@ import java.util.List; | |||||
| * | * | ||||
| * @author ruoyi | * @author ruoyi | ||||
| */ | */ | ||||
| @ExcelIgnoreUnannotated | |||||
| @ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, | |||||
| verticalAlignment = VerticalAlignmentEnum.CENTER) | |||||
| public class SysUser extends BaseEntity { | public class SysUser extends BaseEntity { | ||||
| private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
| /** | /** | ||||
| * 用户ID | * 用户ID | ||||
| */ | */ | ||||
| @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") | |||||
| @ExcelProperty(value = "用户序号", order = 1) | |||||
| private Long userId; | private Long userId; | ||||
| /** | /** | ||||
| * 部门ID | * 部门ID | ||||
| */ | */ | ||||
| @Excel(name = "部门编号", type = Type.IMPORT) | |||||
| @ExcelProperty(value = "部门编号", order = 2) | |||||
| private Long deptId; | private Long deptId; | ||||
| /** | /** | ||||
| * 用户账号 | * 用户账号 | ||||
| */ | */ | ||||
| @Excel(name = "登录名称") | |||||
| @ExcelProperty(value = "登录名称", order = 3) | |||||
| private String userName; | private String userName; | ||||
| /** | /** | ||||
| * 用户昵称 | * 用户昵称 | ||||
| */ | */ | ||||
| @Excel(name = "用户名称") | |||||
| @ExcelProperty(value = "用户名称", order = 4) | |||||
| private String nickName; | private String nickName; | ||||
| /** | /** | ||||
| * 用户邮箱 | * 用户邮箱 | ||||
| */ | */ | ||||
| @Excel(name = "用户邮箱") | |||||
| @ExcelProperty(value = "用户邮箱", order = 4) | |||||
| private String email; | private String email; | ||||
| /** | /** | ||||
| * 手机号码 | * 手机号码 | ||||
| */ | */ | ||||
| @Excel(name = "手机号码") | |||||
| @ExcelProperty(value = "手机号码", order = 5) | |||||
| private String phonenumber; | private String phonenumber; | ||||
| /** | /** | ||||
| * 用户性别 | * 用户性别 | ||||
| */ | */ | ||||
| @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") | |||||
| @ExcelProperty(value = "用户性别", order = 6) | |||||
| private String sex; | private String sex; | ||||
| /** | /** | ||||
| @@ -75,10 +82,12 @@ public class SysUser extends BaseEntity { | |||||
| */ | */ | ||||
| private String password; | private String password; | ||||
| private String originPassword; | |||||
| /** | /** | ||||
| * 帐号状态(0正常 1停用) | * 帐号状态(0正常 1停用) | ||||
| */ | */ | ||||
| @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") | |||||
| @ExcelProperty(value = "帐号状态", order = 7) | |||||
| private String status; | private String status; | ||||
| /** | /** | ||||
| @@ -89,22 +98,24 @@ public class SysUser extends BaseEntity { | |||||
| /** | /** | ||||
| * 最后登录IP | * 最后登录IP | ||||
| */ | */ | ||||
| @Excel(name = "最后登录IP", type = Type.EXPORT) | |||||
| @ExcelProperty(value = "最后登录IP", order = 8) | |||||
| private String loginIp; | private String loginIp; | ||||
| /** | /** | ||||
| * 最后登录时间 | * 最后登录时间 | ||||
| */ | */ | ||||
| @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) | |||||
| // @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) | |||||
| @ExcelProperty(value = "最后登录时间", order = 9) | |||||
| private Date loginDate; | private Date loginDate; | ||||
| /** | /** | ||||
| * 部门对象 | * 部门对象 | ||||
| */ | */ | ||||
| @Excels({ | |||||
| @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), | |||||
| @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) | |||||
| }) | |||||
| // @Excels({ | |||||
| // @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), | |||||
| // @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) | |||||
| // }) | |||||
| // @ExcelProperty(value = "部门", order = 4) | |||||
| private SysDept dept; | private SysDept dept; | ||||
| /** | /** | ||||
| @@ -127,10 +138,6 @@ public class SysUser extends BaseEntity { | |||||
| */ | */ | ||||
| private Long roleId; | private Long roleId; | ||||
| private String gitLinkUsername; | |||||
| private String gitLinkPassword; | |||||
| private Float credit; | private Float credit; | ||||
| public SysUser() { | public SysUser() { | ||||
| @@ -229,6 +236,14 @@ public class SysUser extends BaseEntity { | |||||
| this.password = password; | this.password = password; | ||||
| } | } | ||||
| public String getOriginPassword() { | |||||
| return originPassword; | |||||
| } | |||||
| public void setOriginPassword(String originPassword) { | |||||
| this.originPassword = originPassword; | |||||
| } | |||||
| public String getStatus() { | public String getStatus() { | ||||
| return status; | return status; | ||||
| } | } | ||||
| @@ -301,22 +316,6 @@ public class SysUser extends BaseEntity { | |||||
| this.roleId = roleId; | this.roleId = roleId; | ||||
| } | } | ||||
| public void setGitLinkUsername(String gitLinkUsername) { | |||||
| this.gitLinkUsername = gitLinkUsername; | |||||
| } | |||||
| public String getGitLinkUsername() { | |||||
| return gitLinkUsername; | |||||
| } | |||||
| public void setGitLinkPassword(String gitLinkPassword) { | |||||
| this.gitLinkPassword = gitLinkPassword; | |||||
| } | |||||
| public String getGitLinkPassword() { | |||||
| return gitLinkPassword; | |||||
| } | |||||
| public void setCredit(Float credit) { | public void setCredit(Float credit) { | ||||
| this.credit = credit; | this.credit = credit; | ||||
| } | } | ||||
| @@ -337,6 +336,7 @@ public class SysUser extends BaseEntity { | |||||
| .append("sex", getSex()) | .append("sex", getSex()) | ||||
| .append("avatar", getAvatar()) | .append("avatar", getAvatar()) | ||||
| .append("password", getPassword()) | .append("password", getPassword()) | ||||
| .append("originPassword", getOriginPassword()) | |||||
| .append("status", getStatus()) | .append("status", getStatus()) | ||||
| .append("delFlag", getDelFlag()) | .append("delFlag", getDelFlag()) | ||||
| .append("loginIp", getLoginIp()) | .append("loginIp", getLoginIp()) | ||||
| @@ -347,8 +347,6 @@ public class SysUser extends BaseEntity { | |||||
| .append("updateTime", getUpdateTime()) | .append("updateTime", getUpdateTime()) | ||||
| .append("remark", getRemark()) | .append("remark", getRemark()) | ||||
| .append("dept", getDept()) | .append("dept", getDept()) | ||||
| .append("gitLinkUsername", getGitLinkUsername()) | |||||
| .append("gitLinkPassword", getGitLinkPassword()) | |||||
| .append("credit", getCredit()) | .append("credit", getCredit()) | ||||
| .toString(); | .toString(); | ||||
| } | } | ||||
| @@ -0,0 +1,30 @@ | |||||
| package com.ruoyi.system.api.factory; | |||||
| import com.ruoyi.common.core.web.domain.AjaxResult; | |||||
| import com.ruoyi.system.api.RemoteAuthService; | |||||
| import com.ruoyi.system.api.domain.SysUser; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||||
| import org.springframework.stereotype.Component; | |||||
| @Component | |||||
| public class RemoteAuthFallbackFactory implements FallbackFactory<RemoteAuthService> { | |||||
| private static final Logger log = LoggerFactory.getLogger(RemoteAuthFallbackFactory.class); | |||||
| @Override | |||||
| public RemoteAuthService create(Throwable throwable) { | |||||
| log.error("Auth服务调用失败:{}", throwable.getMessage()); | |||||
| return new RemoteAuthService() { | |||||
| @Override | |||||
| public AjaxResult add(SysUser user) { | |||||
| return AjaxResult.error("新增Oauth2用户失败"); | |||||
| } | |||||
| @Override | |||||
| public AjaxResult edit(SysUser user) { | |||||
| return AjaxResult.error("更新Oauth2用户失败"); | |||||
| } | |||||
| }; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,51 @@ | |||||
| package com.ruoyi.system.api.factory; | |||||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | |||||
| import com.ruoyi.system.api.RemoteMmpService; | |||||
| import com.ruoyi.system.api.domain.SysUser; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||||
| import org.springframework.stereotype.Component; | |||||
| @Component | |||||
| public class RemoteMmpFallbackFactory implements FallbackFactory<RemoteMmpService> { | |||||
| private static final Logger log = LoggerFactory.getLogger(RemoteMmpFallbackFactory.class); | |||||
| @Override | |||||
| public RemoteMmpService create(Throwable throwable) { | |||||
| log.error("管理平台服务调用失败:{}", throwable.getMessage()); | |||||
| return new RemoteMmpService() { | |||||
| @Override | |||||
| public GenericsAjaxResult<String> gitLinkLogin(String username, String password) { | |||||
| return GenericsAjaxResult.error("刷新gitLink登录信息失败"); | |||||
| } | |||||
| @Override | |||||
| public GenericsAjaxResult<String> createGitLinkUser(SysUser sysUser) throws Exception { | |||||
| throw new Exception("新增gitLink用户失败:" + throwable.getMessage()); | |||||
| } | |||||
| @Override | |||||
| public GenericsAjaxResult<String> resetPwd(SysUser sysUser) throws Exception { | |||||
| throw new Exception("修改gitLink用户密码失败:" + throwable.getMessage()); | |||||
| } | |||||
| @Override | |||||
| public GenericsAjaxResult<String> resetEmail(SysUser sysUser) throws Exception { | |||||
| throw new Exception("修改gitLink用户邮箱失败:" + throwable.getMessage()); | |||||
| } | |||||
| @Override | |||||
| public GenericsAjaxResult<String> resetPhoneNum(SysUser sysUser) throws Exception { | |||||
| throw new Exception("修改gitLink用户手机号失败:" + throwable.getMessage()); | |||||
| } | |||||
| @Override | |||||
| public GenericsAjaxResult<String> deleteGitLinkUser(SysUser sysUser) throws Exception { | |||||
| throw new Exception("删除gitLink用户失败:" + throwable.getMessage()); | |||||
| } | |||||
| }; | |||||
| } | |||||
| } | |||||
| @@ -1,3 +1,5 @@ | |||||
| com.ruoyi.system.api.factory.RemoteUserFallbackFactory | com.ruoyi.system.api.factory.RemoteUserFallbackFactory | ||||
| com.ruoyi.system.api.factory.RemoteLogFallbackFactory | com.ruoyi.system.api.factory.RemoteLogFallbackFactory | ||||
| com.ruoyi.system.api.factory.RemoteFileFallbackFactory | com.ruoyi.system.api.factory.RemoteFileFallbackFactory | ||||
| com.ruoyi.system.api.factory.RemoteMmpFallbackFactory | |||||
| com.ruoyi.system.api.factory.RemoteAuthFallbackFactory | |||||
| @@ -51,9 +51,32 @@ | |||||
| <groupId>com.ruoyi</groupId> | <groupId>com.ruoyi</groupId> | ||||
| <artifactId>ruoyi-common-security</artifactId> | <artifactId>ruoyi-common-security</artifactId> | ||||
| </dependency> | </dependency> | ||||
| <!-- Mysql Connector --> | |||||
| <dependency> | |||||
| <groupId>com.mysql</groupId> | |||||
| <artifactId>mysql-connector-j</artifactId> | |||||
| </dependency> | |||||
| <!-- RuoYi Common DataSource --> | |||||
| <dependency> | |||||
| <groupId>com.ruoyi</groupId> | |||||
| <artifactId>ruoyi-common-datasource</artifactId> | |||||
| </dependency> | |||||
| <!-- RuoYi Common DataScope --> | |||||
| <dependency> | |||||
| <groupId>com.ruoyi</groupId> | |||||
| <artifactId>ruoyi-common-datascope</artifactId> | |||||
| </dependency> | |||||
| <!-- RuoYi Common Log --> | |||||
| <dependency> | |||||
| <groupId>com.ruoyi</groupId> | |||||
| <artifactId>ruoyi-common-log</artifactId> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| <build> | <build> | ||||
| <finalName>${project.artifactId}</finalName> | <finalName>${project.artifactId}</finalName> | ||||
| <plugins> | <plugins> | ||||
| @@ -1,5 +1,6 @@ | |||||
| package com.ruoyi.auth; | package com.ruoyi.auth; | ||||
| import com.ruoyi.common.security.annotation.EnableCustomConfig; | |||||
| import org.springframework.boot.SpringApplication; | import org.springframework.boot.SpringApplication; | ||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
| import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; | ||||
| @@ -10,6 +11,7 @@ import com.ruoyi.common.security.annotation.EnableRyFeignClients; | |||||
| * | * | ||||
| * @author ruoyi | * @author ruoyi | ||||
| */ | */ | ||||
| @EnableCustomConfig | |||||
| @EnableRyFeignClients | @EnableRyFeignClients | ||||
| @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) | @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) | ||||
| public class RuoYiAuthApplication | public class RuoYiAuthApplication | ||||
| @@ -0,0 +1,26 @@ | |||||
| package com.ruoyi.auth.controller; | |||||
| import com.ruoyi.auth.service.Oauth2Service; | |||||
| import com.ruoyi.common.core.web.controller.BaseController; | |||||
| import com.ruoyi.common.core.web.domain.AjaxResult; | |||||
| import com.ruoyi.system.api.domain.SysUser; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.validation.annotation.Validated; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| @RestController | |||||
| @RequestMapping("/oauth2") | |||||
| public class Oauth2Controller extends BaseController { | |||||
| @Autowired | |||||
| private Oauth2Service oauth2Service; | |||||
| @PostMapping | |||||
| public AjaxResult add(@Validated @RequestBody SysUser user) { | |||||
| return toAjax(oauth2Service.insertOauth2User(user)); | |||||
| } | |||||
| @PutMapping | |||||
| public AjaxResult edit(@Validated @RequestBody SysUser user) { | |||||
| return toAjax(oauth2Service.updateOauth2User(user)); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,37 @@ | |||||
| package com.ruoyi.auth.domain; | |||||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | |||||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | |||||
| import lombok.Data; | |||||
| import java.util.Date; | |||||
| @Data | |||||
| @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | |||||
| public class OauthAccount { | |||||
| private Long id; | |||||
| private String clientId; | |||||
| private String username; | |||||
| private String password; | |||||
| private String mobile; | |||||
| private String email; | |||||
| private Integer enabled; | |||||
| private Integer accountNonExpired; | |||||
| private Integer credentialsNonExpired; | |||||
| private Integer accountNonLocked; | |||||
| private Integer accountNonDeleted; | |||||
| private Date createdTime; | |||||
| private Date updatedTime; | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| package com.ruoyi.auth.mapper; | |||||
| import com.ruoyi.auth.domain.OauthAccount; | |||||
| import org.apache.ibatis.annotations.Param; | |||||
| public interface Oauth2Mapper { | |||||
| int insertOauth2User(@Param("oauthAccount")OauthAccount oauthAccount); | |||||
| int updateOauth2User(@Param("oauthAccount")OauthAccount oauthAccount); | |||||
| } | |||||
| @@ -0,0 +1,45 @@ | |||||
| package com.ruoyi.auth.service; | |||||
| import com.ruoyi.auth.domain.OauthAccount; | |||||
| import com.ruoyi.auth.mapper.Oauth2Mapper; | |||||
| import com.ruoyi.common.core.utils.StringUtils; | |||||
| import com.ruoyi.system.api.constant.Constant; | |||||
| import com.ruoyi.system.api.domain.SysUser; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.beans.factory.annotation.Value; | |||||
| import org.springframework.stereotype.Component; | |||||
| @Component | |||||
| public class Oauth2Service { | |||||
| @Value("${oauth2.client-id}") | |||||
| String clientId; | |||||
| @Autowired | |||||
| private Oauth2Mapper oauth2Mapper; | |||||
| public int insertOauth2User(SysUser user) { | |||||
| OauthAccount oauthAccount = new OauthAccount(); | |||||
| oauthAccount.setClientId(clientId); | |||||
| oauthAccount.setUsername(user.getUserName()); | |||||
| oauthAccount.setPassword(user.getPassword()); | |||||
| oauthAccount.setMobile(user.getPhonenumber()); | |||||
| oauthAccount.setEmail(user.getEmail()); | |||||
| oauthAccount.setEnabled(Constant.State_valid); | |||||
| return oauth2Mapper.insertOauth2User(oauthAccount); | |||||
| } | |||||
| public int updateOauth2User(SysUser user) { | |||||
| OauthAccount oauthAccount = new OauthAccount(); | |||||
| oauthAccount.setClientId(clientId); | |||||
| oauthAccount.setUsername(user.getUserName()); | |||||
| if (StringUtils.isNotEmpty(user.getPassword())) { | |||||
| oauthAccount.setPassword(user.getPassword()); | |||||
| } | |||||
| oauthAccount.setMobile(user.getPhonenumber()); | |||||
| oauthAccount.setEmail(user.getEmail()); | |||||
| if (Constant.DelFlag.equals(user.getDelFlag())) { | |||||
| oauthAccount.setEnabled(Constant.State_invalid); | |||||
| } | |||||
| return oauth2Mapper.updateOauth2User(oauthAccount); | |||||
| } | |||||
| } | |||||
| @@ -1,8 +1,6 @@ | |||||
| package com.ruoyi.auth.service; | package com.ruoyi.auth.service; | ||||
| import com.ruoyi.auth.form.AccessTokenVo; | import com.ruoyi.auth.form.AccessTokenVo; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Component; | |||||
| import com.ruoyi.common.core.constant.CacheConstants; | import com.ruoyi.common.core.constant.CacheConstants; | ||||
| import com.ruoyi.common.core.constant.Constants; | import com.ruoyi.common.core.constant.Constants; | ||||
| import com.ruoyi.common.core.constant.SecurityConstants; | import com.ruoyi.common.core.constant.SecurityConstants; | ||||
| @@ -15,18 +13,20 @@ import com.ruoyi.common.core.utils.StringUtils; | |||||
| import com.ruoyi.common.core.utils.ip.IpUtils; | import com.ruoyi.common.core.utils.ip.IpUtils; | ||||
| import com.ruoyi.common.redis.service.RedisService; | import com.ruoyi.common.redis.service.RedisService; | ||||
| import com.ruoyi.common.security.utils.SecurityUtils; | import com.ruoyi.common.security.utils.SecurityUtils; | ||||
| import com.ruoyi.system.api.RemoteMmpService; | |||||
| import com.ruoyi.system.api.RemoteUserService; | import com.ruoyi.system.api.RemoteUserService; | ||||
| import com.ruoyi.system.api.domain.SysUser; | import com.ruoyi.system.api.domain.SysUser; | ||||
| import com.ruoyi.system.api.model.LoginUser; | import com.ruoyi.system.api.model.LoginUser; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Component; | |||||
| /** | /** | ||||
| * 登录校验方法 | * 登录校验方法 | ||||
| * | |||||
| * | |||||
| * @author ruoyi | * @author ruoyi | ||||
| */ | */ | ||||
| @Component | @Component | ||||
| public class SysLoginService | |||||
| { | |||||
| public class SysLoginService { | |||||
| @Autowired | @Autowired | ||||
| private RemoteUserService remoteUserService; | private RemoteUserService remoteUserService; | ||||
| @@ -39,92 +39,82 @@ public class SysLoginService | |||||
| @Autowired | @Autowired | ||||
| private RedisService redisService; | private RedisService redisService; | ||||
| @Autowired | |||||
| private RemoteMmpService remoteMmpService; | |||||
| /** | /** | ||||
| * 登录 | * 登录 | ||||
| */ | */ | ||||
| public LoginUser login(String username, String password) | |||||
| { | |||||
| public LoginUser login(String username, String password) { | |||||
| // 用户名或密码为空 错误 | // 用户名或密码为空 错误 | ||||
| if (StringUtils.isAnyBlank(username, password)) | |||||
| { | |||||
| if (StringUtils.isAnyBlank(username, password)) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); | ||||
| throw new ServiceException("用户/密码必须填写"); | throw new ServiceException("用户/密码必须填写"); | ||||
| } | } | ||||
| // 密码如果不在指定范围内 错误 | // 密码如果不在指定范围内 错误 | ||||
| if (password.length() < UserConstants.PASSWORD_MIN_LENGTH | if (password.length() < UserConstants.PASSWORD_MIN_LENGTH | ||||
| || password.length() > UserConstants.PASSWORD_MAX_LENGTH) | |||||
| { | |||||
| || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); | ||||
| throw new ServiceException("用户密码不在指定范围"); | throw new ServiceException("用户密码不在指定范围"); | ||||
| } | } | ||||
| // 用户名不在指定范围内 错误 | // 用户名不在指定范围内 错误 | ||||
| if (username.length() < UserConstants.USERNAME_MIN_LENGTH | if (username.length() < UserConstants.USERNAME_MIN_LENGTH | ||||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) | |||||
| { | |||||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); | ||||
| throw new ServiceException("用户名不在指定范围"); | throw new ServiceException("用户名不在指定范围"); | ||||
| } | } | ||||
| // IP黑名单校验 | // IP黑名单校验 | ||||
| String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); | String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); | ||||
| if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) | |||||
| { | |||||
| if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); | ||||
| throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); | throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); | ||||
| } | } | ||||
| // 查询用户信息 | // 查询用户信息 | ||||
| R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | ||||
| if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) | |||||
| { | |||||
| if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); | ||||
| throw new ServiceException("登录用户:" + username + " 不存在"); | throw new ServiceException("登录用户:" + username + " 不存在"); | ||||
| } | } | ||||
| if (R.FAIL == userResult.getCode()) | |||||
| { | |||||
| if (R.FAIL == userResult.getCode()) { | |||||
| throw new ServiceException(userResult.getMsg()); | throw new ServiceException(userResult.getMsg()); | ||||
| } | } | ||||
| LoginUser userInfo = userResult.getData(); | LoginUser userInfo = userResult.getData(); | ||||
| SysUser user = userResult.getData().getSysUser(); | SysUser user = userResult.getData().getSysUser(); | ||||
| if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) | |||||
| { | |||||
| if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); | ||||
| throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); | throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); | ||||
| } | } | ||||
| if (UserStatus.DISABLE.getCode().equals(user.getStatus())) | |||||
| { | |||||
| if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); | ||||
| throw new ServiceException("对不起,您的账号:" + username + " 已停用"); | throw new ServiceException("对不起,您的账号:" + username + " 已停用"); | ||||
| } | } | ||||
| passwordService.validate(user, password); | passwordService.validate(user, password); | ||||
| remoteMmpService.gitLinkLogin(username, user.getOriginPassword()); | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); | recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); | ||||
| return userInfo; | return userInfo; | ||||
| } | } | ||||
| public void logout(String loginName) | |||||
| { | |||||
| public void logout(String loginName) { | |||||
| recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); | recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); | ||||
| } | } | ||||
| /** | /** | ||||
| * 注册 | * 注册 | ||||
| */ | */ | ||||
| public void register(String username, String password) | |||||
| { | |||||
| public void register(String username, String password) { | |||||
| // 用户名或密码为空 错误 | // 用户名或密码为空 错误 | ||||
| if (StringUtils.isAnyBlank(username, password)) | |||||
| { | |||||
| if (StringUtils.isAnyBlank(username, password)) { | |||||
| throw new ServiceException("用户/密码必须填写"); | throw new ServiceException("用户/密码必须填写"); | ||||
| } | } | ||||
| if (username.length() < UserConstants.USERNAME_MIN_LENGTH | if (username.length() < UserConstants.USERNAME_MIN_LENGTH | ||||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) | |||||
| { | |||||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) { | |||||
| throw new ServiceException("账户长度必须在2到20个字符之间"); | throw new ServiceException("账户长度必须在2到20个字符之间"); | ||||
| } | } | ||||
| if (password.length() < UserConstants.PASSWORD_MIN_LENGTH | if (password.length() < UserConstants.PASSWORD_MIN_LENGTH | ||||
| || password.length() > UserConstants.PASSWORD_MAX_LENGTH) | |||||
| { | |||||
| || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { | |||||
| throw new ServiceException("密码长度必须在5到20个字符之间"); | throw new ServiceException("密码长度必须在5到20个字符之间"); | ||||
| } | } | ||||
| @@ -135,8 +125,7 @@ public class SysLoginService | |||||
| sysUser.setPassword(SecurityUtils.encryptPassword(password)); | sysUser.setPassword(SecurityUtils.encryptPassword(password)); | ||||
| R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER); | R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER); | ||||
| if (R.FAIL == registerResult.getCode()) | |||||
| { | |||||
| if (R.FAIL == registerResult.getCode()) { | |||||
| throw new ServiceException(registerResult.getMsg()); | throw new ServiceException(registerResult.getMsg()); | ||||
| } | } | ||||
| recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功"); | recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功"); | ||||
| @@ -144,52 +133,45 @@ public class SysLoginService | |||||
| public LoginUser loginByKey(String username, String key) { | public LoginUser loginByKey(String username, String key) { | ||||
| // 用户名或密码为空 错误 | // 用户名或密码为空 错误 | ||||
| if (StringUtils.isAnyBlank(username, key)) | |||||
| { | |||||
| if (StringUtils.isAnyBlank(username, key)) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/key必须填写"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/key必须填写"); | ||||
| throw new ServiceException("用户/key必须填写"); | throw new ServiceException("用户/key必须填写"); | ||||
| } | } | ||||
| // 用户名不在指定范围内 错误 | // 用户名不在指定范围内 错误 | ||||
| if (username.length() < UserConstants.USERNAME_MIN_LENGTH | if (username.length() < UserConstants.USERNAME_MIN_LENGTH | ||||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) | |||||
| { | |||||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); | ||||
| throw new ServiceException("用户名不在指定范围"); | throw new ServiceException("用户名不在指定范围"); | ||||
| } | } | ||||
| // IP黑名单校验 | // IP黑名单校验 | ||||
| String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); | String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); | ||||
| if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) | |||||
| { | |||||
| if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); | ||||
| throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); | throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); | ||||
| } | } | ||||
| // 查询用户信息 | // 查询用户信息 | ||||
| R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | ||||
| if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) | |||||
| { | |||||
| if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); | ||||
| throw new ServiceException("登录用户:" + username + " 不存在"); | throw new ServiceException("登录用户:" + username + " 不存在"); | ||||
| } | } | ||||
| if (R.FAIL == userResult.getCode()) | |||||
| { | |||||
| if (R.FAIL == userResult.getCode()) { | |||||
| throw new ServiceException(userResult.getMsg()); | throw new ServiceException(userResult.getMsg()); | ||||
| } | } | ||||
| LoginUser userInfo = userResult.getData(); | LoginUser userInfo = userResult.getData(); | ||||
| SysUser user = userResult.getData().getSysUser(); | SysUser user = userResult.getData().getSysUser(); | ||||
| if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) | |||||
| { | |||||
| if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); | ||||
| throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); | throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); | ||||
| } | } | ||||
| if (UserStatus.DISABLE.getCode().equals(user.getStatus())) | |||||
| { | |||||
| if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); | ||||
| throw new ServiceException("对不起,您的账号:" + username + " 已停用"); | throw new ServiceException("对不起,您的账号:" + username + " 已停用"); | ||||
| } | } | ||||
| if (!StringUtils.equals(key,"h1n2x3j4y5@")){ | |||||
| if (!StringUtils.equals(key, "h1n2x3j4y5@")) { | |||||
| throw new ServiceException("对不起,您的key不正确"); | throw new ServiceException("对不起,您的key不正确"); | ||||
| } | } | ||||
| return userInfo; | return userInfo; | ||||
| @@ -200,51 +182,44 @@ public class SysLoginService | |||||
| String username = accountInfo.getUsername(); | String username = accountInfo.getUsername(); | ||||
| // 用户名或密码为空 错误 | // 用户名或密码为空 错误 | ||||
| if (StringUtils.isAnyBlank(username)) | |||||
| { | |||||
| if (StringUtils.isAnyBlank(username)) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); | ||||
| throw new ServiceException("用户/密码必须填写"); | throw new ServiceException("用户/密码必须填写"); | ||||
| } | } | ||||
| // 用户名不在指定范围内 错误 | // 用户名不在指定范围内 错误 | ||||
| if (username.length() < UserConstants.USERNAME_MIN_LENGTH | if (username.length() < UserConstants.USERNAME_MIN_LENGTH | ||||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) | |||||
| { | |||||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); | ||||
| throw new ServiceException("用户名不在指定范围"); | throw new ServiceException("用户名不在指定范围"); | ||||
| } | } | ||||
| // IP黑名单校验 | // IP黑名单校验 | ||||
| String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); | String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); | ||||
| if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) | |||||
| { | |||||
| if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); | ||||
| throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); | throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); | ||||
| } | } | ||||
| // 查询用户信息 | // 查询用户信息 | ||||
| R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | ||||
| if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) | |||||
| { | |||||
| if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); | ||||
| throw new ServiceException("登录用户:" + username + " 不存在"); | throw new ServiceException("登录用户:" + username + " 不存在"); | ||||
| // register(username, "123456"); | |||||
| // register(username, "123456"); | |||||
| // userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | // userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | ||||
| } | } | ||||
| if (R.FAIL == userResult.getCode()) | |||||
| { | |||||
| if (R.FAIL == userResult.getCode()) { | |||||
| throw new ServiceException(userResult.getMsg()); | throw new ServiceException(userResult.getMsg()); | ||||
| } | } | ||||
| LoginUser userInfo = userResult.getData(); | LoginUser userInfo = userResult.getData(); | ||||
| SysUser user = userResult.getData().getSysUser(); | SysUser user = userResult.getData().getSysUser(); | ||||
| if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) | |||||
| { | |||||
| if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); | ||||
| throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); | throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); | ||||
| } | } | ||||
| if (UserStatus.DISABLE.getCode().equals(user.getStatus())) | |||||
| { | |||||
| if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { | |||||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); | ||||
| throw new ServiceException("对不起,您的账号:" + username + " 已停用"); | throw new ServiceException("对不起,您的账号:" + username + " 已停用"); | ||||
| } | } | ||||
| @@ -0,0 +1,31 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||||
| <mapper namespace="com.ruoyi.auth.mapper.Oauth2Mapper"> | |||||
| <insert id="insertOauth2User" useGeneratedKeys="true" keyProperty="id"> | |||||
| insert into oauth_account(client_id, username, password, mobile, email, enabled) | |||||
| values (#{oauthAccount.clientId}, #{oauthAccount.username}, #{oauthAccount.password}, #{oauthAccount.mobile}, | |||||
| #{oauthAccount.email}, #{oauthAccount.enabled}) | |||||
| </insert> | |||||
| <update id="updateOauth2User"> | |||||
| update oauth_account | |||||
| <set> | |||||
| <if test="oauthAccount.username != null and oauthAccount.username !=''"> | |||||
| username = #{oauthAccount.username}, | |||||
| </if> | |||||
| <if test="oauthAccount.password != null and oauthAccount.password !=''"> | |||||
| password = #{oauthAccount.password}, | |||||
| </if> | |||||
| <if test="oauthAccount.mobile != null and oauthAccount.mobile !=''"> | |||||
| mobile = #{oauthAccount.mobile}, | |||||
| </if> | |||||
| <if test="oauthAccount.email != null and oauthAccount.email !=''"> | |||||
| email = #{oauthAccount.email}, | |||||
| </if> | |||||
| <if test="oauthAccount.enabled != null"> | |||||
| enabled = #{oauthAccount.enabled}, | |||||
| </if> | |||||
| </set> | |||||
| where username = #{oauthAccount.username} and enabled = 1 | |||||
| </update> | |||||
| </mapper> | |||||
| @@ -21,4 +21,6 @@ public class ServiceNameConstants | |||||
| * 文件服务的serviceid | * 文件服务的serviceid | ||||
| */ | */ | ||||
| public static final String FILE_SERVICE = "ruoyi-file"; | public static final String FILE_SERVICE = "ruoyi-file"; | ||||
| public static final String MANAGEMENT_SERVICE = "management-platform"; | |||||
| } | } | ||||
| @@ -257,7 +257,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 对excel表单默认第一个索引名转换成list | * 对excel表单默认第一个索引名转换成list | ||||
| * | |||||
| * | |||||
| * @param is 输入流 | * @param is 输入流 | ||||
| * @return 转换后集合 | * @return 转换后集合 | ||||
| */ | */ | ||||
| @@ -282,7 +282,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 对excel表单默认第一个索引名转换成list | * 对excel表单默认第一个索引名转换成list | ||||
| * | |||||
| * | |||||
| * @param is 输入流 | * @param is 输入流 | ||||
| * @param titleNum 标题占用行数 | * @param titleNum 标题占用行数 | ||||
| * @return 转换后集合 | * @return 转换后集合 | ||||
| @@ -294,7 +294,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 对excel表单指定表格索引名转换成list | * 对excel表单指定表格索引名转换成list | ||||
| * | |||||
| * | |||||
| * @param sheetName 表格索引名 | * @param sheetName 表格索引名 | ||||
| * @param titleNum 标题占用行数 | * @param titleNum 标题占用行数 | ||||
| * @param is 输入流 | * @param is 输入流 | ||||
| @@ -503,7 +503,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 对list数据源将其里面的数据导入到excel表单 | * 对list数据源将其里面的数据导入到excel表单 | ||||
| * | |||||
| * | |||||
| * @return 结果 | * @return 结果 | ||||
| */ | */ | ||||
| public void exportExcel(HttpServletResponse response) | public void exportExcel(HttpServletResponse response) | ||||
| @@ -565,7 +565,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 填充excel数据 | * 填充excel数据 | ||||
| * | |||||
| * | |||||
| * @param index 序号 | * @param index 序号 | ||||
| * @param row 单元格行 | * @param row 单元格行 | ||||
| */ | */ | ||||
| @@ -636,7 +636,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 创建表格样式 | * 创建表格样式 | ||||
| * | |||||
| * | |||||
| * @param wb 工作薄对象 | * @param wb 工作薄对象 | ||||
| * @return 样式列表 | * @return 样式列表 | ||||
| */ | */ | ||||
| @@ -689,7 +689,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 根据Excel注解创建表格头样式 | * 根据Excel注解创建表格头样式 | ||||
| * | |||||
| * | |||||
| * @param wb 工作薄对象 | * @param wb 工作薄对象 | ||||
| * @return 自定义样式列表 | * @return 自定义样式列表 | ||||
| */ | */ | ||||
| @@ -722,7 +722,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 根据Excel注解创建表格列样式 | * 根据Excel注解创建表格列样式 | ||||
| * | |||||
| * | |||||
| * @param wb 工作薄对象 | * @param wb 工作薄对象 | ||||
| * @return 自定义样式列表 | * @return 自定义样式列表 | ||||
| */ | */ | ||||
| @@ -784,7 +784,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 设置单元格信息 | * 设置单元格信息 | ||||
| * | |||||
| * | |||||
| * @param value 单元格值 | * @param value 单元格值 | ||||
| * @param attr 注解相关 | * @param attr 注解相关 | ||||
| * @param cell 单元格信息 | * @param cell 单元格信息 | ||||
| @@ -943,7 +943,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 设置 POI XSSFSheet 单元格提示或选择框 | * 设置 POI XSSFSheet 单元格提示或选择框 | ||||
| * | |||||
| * | |||||
| * @param sheet 表单 | * @param sheet 表单 | ||||
| * @param textlist 下拉框显示的内容 | * @param textlist 下拉框显示的内容 | ||||
| * @param promptContent 提示内容 | * @param promptContent 提示内容 | ||||
| @@ -953,7 +953,7 @@ public class ExcelUtil<T> | |||||
| * @param endCol 结束列 | * @param endCol 结束列 | ||||
| */ | */ | ||||
| public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, | public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, | ||||
| int firstCol, int endCol) | |||||
| int firstCol, int endCol) | |||||
| { | { | ||||
| DataValidationHelper helper = sheet.getDataValidationHelper(); | DataValidationHelper helper = sheet.getDataValidationHelper(); | ||||
| DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); | DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); | ||||
| @@ -980,7 +980,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). | * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). | ||||
| * | |||||
| * | |||||
| * @param sheet 要设置的sheet. | * @param sheet 要设置的sheet. | ||||
| * @param textlist 下拉框显示的内容 | * @param textlist 下拉框显示的内容 | ||||
| * @param promptContent 提示内容 | * @param promptContent 提示内容 | ||||
| @@ -1069,7 +1069,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 反向解析值 男=0,女=1,未知=2 | * 反向解析值 男=0,女=1,未知=2 | ||||
| * | |||||
| * | |||||
| * @param propertyValue 参数值 | * @param propertyValue 参数值 | ||||
| * @param converterExp 翻译注解 | * @param converterExp 翻译注解 | ||||
| * @param separator 分隔符 | * @param separator 分隔符 | ||||
| @@ -1311,7 +1311,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 创建工作表 | * 创建工作表 | ||||
| * | |||||
| * | |||||
| * @param sheetNo sheet数量 | * @param sheetNo sheet数量 | ||||
| * @param index 序号 | * @param index 序号 | ||||
| */ | */ | ||||
| @@ -1328,7 +1328,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 获取单元格值 | * 获取单元格值 | ||||
| * | |||||
| * | |||||
| * @param row 获取的行 | * @param row 获取的行 | ||||
| * @param column 获取单元格列号 | * @param column 获取单元格列号 | ||||
| * @return 单元格值 | * @return 单元格值 | ||||
| @@ -1388,7 +1388,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 判断是否是空行 | * 判断是否是空行 | ||||
| * | |||||
| * | |||||
| * @param row 判断的行 | * @param row 判断的行 | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| @@ -1411,7 +1411,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 格式化不同类型的日期对象 | * 格式化不同类型的日期对象 | ||||
| * | |||||
| * | |||||
| * @param dateFormat 日期格式 | * @param dateFormat 日期格式 | ||||
| * @param val 被格式化的日期对象 | * @param val 被格式化的日期对象 | ||||
| * @return 格式化后的日期字符 | * @return 格式化后的日期字符 | ||||
| @@ -1477,7 +1477,7 @@ public class ExcelUtil<T> | |||||
| /** | /** | ||||
| * 获取对象的子列表方法 | * 获取对象的子列表方法 | ||||
| * | |||||
| * | |||||
| * @param name 名称 | * @param name 名称 | ||||
| * @param pojoClass 类对象 | * @param pojoClass 类对象 | ||||
| * @return 子列表方法 | * @return 子列表方法 | ||||
| @@ -3,6 +3,7 @@ package com.ruoyi.platform.controller.dataset; | |||||
| import com.ruoyi.common.core.web.domain.AjaxResult; | import com.ruoyi.common.core.web.domain.AjaxResult; | ||||
| import com.ruoyi.platform.domain.Dataset; | import com.ruoyi.platform.domain.Dataset; | ||||
| import com.ruoyi.platform.service.NewDatasetService; | import com.ruoyi.platform.service.NewDatasetService; | ||||
| import com.ruoyi.platform.utils.DVCUtils; | |||||
| import com.ruoyi.platform.vo.LabelDatasetVersionVo; | import com.ruoyi.platform.vo.LabelDatasetVersionVo; | ||||
| import com.ruoyi.platform.vo.NewDatasetVo; | import com.ruoyi.platform.vo.NewDatasetVo; | ||||
| import com.ruoyi.platform.vo.QueryModelMetricsVo; | import com.ruoyi.platform.vo.QueryModelMetricsVo; | ||||
| @@ -15,6 +16,8 @@ import org.springframework.web.multipart.MultipartFile; | |||||
| import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||
| import java.nio.file.Path; | |||||
| import java.nio.file.Paths; | |||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Map; | import java.util.Map; | ||||
| import java.util.Optional; | import java.util.Optional; | ||||
| @@ -29,6 +32,8 @@ public class NewDatasetFromGitController { | |||||
| @Resource | @Resource | ||||
| private NewDatasetService newDatasetService; | private NewDatasetService newDatasetService; | ||||
| @Resource | |||||
| private DVCUtils dvcUtils; | |||||
| /** | /** | ||||
| * 新增数据集与版本新 | * 新增数据集与版本新 | ||||
| @@ -172,7 +177,15 @@ public class NewDatasetFromGitController { | |||||
| @PostMapping("/getVersionsCompare") | @PostMapping("/getVersionsCompare") | ||||
| @ApiOperation(value = "获取数据集版本对比") | @ApiOperation(value = "获取数据集版本对比") | ||||
| public AjaxResult getVersionsCompare(@RequestBody QueryModelMetricsVo querydatasetVo) throws Exception{ | |||||
| public AjaxResult getVersionsCompare(@RequestBody QueryModelMetricsVo querydatasetVo) throws Exception { | |||||
| return AjaxResult.success(this.newDatasetService.getVersionsCompare(querydatasetVo)); | return AjaxResult.success(this.newDatasetService.getVersionsCompare(querydatasetVo)); | ||||
| } | } | ||||
| @DeleteMapping("/deleteFile") | |||||
| @ApiOperation(value = "删除文件") | |||||
| public AjaxResult deleteFile(@RequestParam("url") String url, @RequestParam("fileName") String fileName) { | |||||
| Path file = Paths.get(url, System.getProperty("file.separator"), fileName); | |||||
| dvcUtils.deletePath(file); | |||||
| return AjaxResult.success("删除成功"); | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,61 @@ | |||||
| package com.ruoyi.platform.controller.git; | |||||
| import com.ruoyi.common.core.web.controller.BaseController; | |||||
| import com.ruoyi.common.core.web.domain.GenericsAjaxResult; | |||||
| import com.ruoyi.platform.service.GitService; | |||||
| import com.ruoyi.system.api.domain.SysUser; | |||||
| import io.swagger.annotations.Api; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| import javax.annotation.Resource; | |||||
| @RestController | |||||
| @RequestMapping("gitLink") | |||||
| @Api("gitLink") | |||||
| public class GitLinkController extends BaseController { | |||||
| @Resource | |||||
| private GitService gitService; | |||||
| @GetMapping("/login") | |||||
| @ApiOperation("刷新giotLink用户信息") | |||||
| public GenericsAjaxResult<String> gitLinkLogin(@RequestParam("username") String username, @RequestParam("password") String password) { | |||||
| return genericsSuccess(gitService.login(username, password)); | |||||
| } | |||||
| @PostMapping("/createGitLinkUser") | |||||
| @ApiOperation("新增gitLink用户") | |||||
| public GenericsAjaxResult<String> createGitLinkUser(@RequestBody SysUser sysUser) throws Exception { | |||||
| gitService.createUser(sysUser); | |||||
| return GenericsAjaxResult.success("新增成功"); | |||||
| } | |||||
| @PostMapping("/resetPwd") | |||||
| @ApiOperation("更改gitLink用户密码") | |||||
| public GenericsAjaxResult<String> resetPwd(@RequestBody SysUser sysUser) throws Exception { | |||||
| gitService.resetPwd(sysUser); | |||||
| return GenericsAjaxResult.success("修改成功"); | |||||
| } | |||||
| @PutMapping("/resetEmail") | |||||
| @ApiOperation("更改gitLink用户邮箱") | |||||
| public GenericsAjaxResult<String> resetEmail(@RequestBody SysUser sysUser) throws Exception { | |||||
| gitService.resetEmail(sysUser); | |||||
| return GenericsAjaxResult.success("修改成功"); | |||||
| } | |||||
| @PutMapping("/resetPhoneNum") | |||||
| @ApiOperation("更改gitLink用户手机号") | |||||
| public GenericsAjaxResult<String> resetPhoneNum(@RequestBody SysUser sysUser) throws Exception { | |||||
| gitService.resetPhoneNum(sysUser); | |||||
| return GenericsAjaxResult.success("修改成功"); | |||||
| } | |||||
| @DeleteMapping("/deleteGitLinkUser") | |||||
| @ApiOperation("删除gitLink用户") | |||||
| public GenericsAjaxResult<String> deleteGitLinkUser(@RequestBody SysUser sysUser) throws Exception { | |||||
| gitService.deleteUser(sysUser); | |||||
| return GenericsAjaxResult.success("删除成功"); | |||||
| } | |||||
| } | |||||
| @@ -92,7 +92,7 @@ public class ImageController extends BaseController { | |||||
| @PostMapping("/addImageAndVersion") | @PostMapping("/addImageAndVersion") | ||||
| @ApiOperation("添加镜像和版本") | @ApiOperation("添加镜像和版本") | ||||
| public GenericsAjaxResult<String> addImageAndVersion(@RequestBody ImageVo imageVo) throws Exception { | public GenericsAjaxResult<String> addImageAndVersion(@RequestBody ImageVo imageVo) throws Exception { | ||||
| return genericsSuccess(this.imageService.insertImageAndVersion(imageVo)); | |||||
| return this.imageService.insertImageAndVersion(imageVo); | |||||
| } | } | ||||
| @@ -90,7 +90,7 @@ public class ImageVersionController extends BaseController { | |||||
| */ | */ | ||||
| @DeleteMapping("{id}") | @DeleteMapping("{id}") | ||||
| public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) throws Exception { | public GenericsAjaxResult<String> deleteById(@PathVariable("id") Integer id) throws Exception { | ||||
| return genericsSuccess(this.imageVersionService.removeById(id)); | |||||
| return this.imageVersionService.removeById(id); | |||||
| } | } | ||||
| } | } | ||||
| @@ -134,7 +134,19 @@ public class NewModelFromGitController { | |||||
| @PostMapping("/getVersionsCompare") | @PostMapping("/getVersionsCompare") | ||||
| @ApiOperation(value = "获取模型版本对比") | @ApiOperation(value = "获取模型版本对比") | ||||
| public AjaxResult getVersionsCompare(@RequestBody QueryModelMetricsVo queryModelMetricsVo) throws Exception{ | |||||
| public AjaxResult getVersionsCompare(@RequestBody QueryModelMetricsVo queryModelMetricsVo) throws Exception { | |||||
| return AjaxResult.success(this.modelsService.getVersionsCompare(queryModelMetricsVo)); | return AjaxResult.success(this.modelsService.getVersionsCompare(queryModelMetricsVo)); | ||||
| } | } | ||||
| @PostMapping("/praise/{id}") | |||||
| @ApiOperation(value = "点赞一个项目") | |||||
| public AjaxResult praise(@PathVariable("id") Integer id) throws Exception { | |||||
| return AjaxResult.success(this.modelsService.praise(id)); | |||||
| } | |||||
| @DeleteMapping("/unpraise/{id}") | |||||
| @ApiOperation(value = "取消点赞一个项目") | |||||
| public AjaxResult unpraise(@PathVariable("id") Integer id) throws Exception { | |||||
| return AjaxResult.success(this.modelsService.unpraise(id)); | |||||
| } | |||||
| } | } | ||||
| @@ -1,6 +1,6 @@ | |||||
| package com.ruoyi.platform.scheduling; | package com.ruoyi.platform.scheduling; | ||||
| import com.ruoyi.platform.constant.Constant; | |||||
| import com.ruoyi.system.api.constant.Constant; | |||||
| import com.ruoyi.platform.domain.AutoMl; | import com.ruoyi.platform.domain.AutoMl; | ||||
| import com.ruoyi.platform.domain.AutoMlIns; | import com.ruoyi.platform.domain.AutoMlIns; | ||||
| import com.ruoyi.platform.mapper.AutoMlDao; | import com.ruoyi.platform.mapper.AutoMlDao; | ||||