| @@ -10,7 +10,6 @@ const config: StorybookConfig = { | |||
| '@storybook/addon-essentials', | |||
| '@chromatic-com/storybook', | |||
| '@storybook/addon-interactions', | |||
| '@storybook/addon-styling-webpack', | |||
| ], | |||
| framework: { | |||
| name: '@storybook/react-webpack5', | |||
| @@ -31,7 +30,27 @@ const config: StorybookConfig = { | |||
| test: /\.less$/, | |||
| use: [ | |||
| 'style-loader', | |||
| 'css-loader', | |||
| { | |||
| loader: 'css-loader', | |||
| options: { | |||
| importLoaders: 1, | |||
| import: true, | |||
| esModule: true, | |||
| modules: { | |||
| auto: (resourcePath: string) => { | |||
| if ( | |||
| resourcePath.endsWith('MenuIconSelector/index.less') || | |||
| resourcePath.endsWith('theme.less') | |||
| ) { | |||
| return true; | |||
| } else { | |||
| return false; | |||
| } | |||
| }, | |||
| localIdentName: '[local]___[hash:base64:5]', | |||
| }, | |||
| }, | |||
| }, | |||
| { | |||
| loader: 'less-loader', | |||
| options: { | |||
| @@ -1,7 +1,8 @@ | |||
| import '@/global.less'; | |||
| import '@/overrides.less'; | |||
| import themes from '@/styles/theme.less'; | |||
| import type { Preview } from '@storybook/react'; | |||
| import { ConfigProvider } from 'antd'; | |||
| import { App, ConfigProvider } from 'antd'; | |||
| import zhCN from 'antd/locale/zh_CN'; | |||
| import React from 'react'; | |||
| import { BrowserRouter as Router } from 'react-router-dom'; | |||
| @@ -15,14 +16,69 @@ const preview: Preview = { | |||
| date: /Date$/i, | |||
| }, | |||
| }, | |||
| actions: { argTypesRegex: '^on.*' }, | |||
| }, | |||
| decorators: [ | |||
| (Story) => ( | |||
| <React.Fragment> | |||
| <ConfigProvider locale={zhCN}> | |||
| <Router> | |||
| <Story /> | |||
| </Router> | |||
| <ConfigProvider | |||
| locale={zhCN} | |||
| theme={{ | |||
| token: { | |||
| colorPrimary: themes['primaryColor'], | |||
| colorSuccess: themes['successColor'], | |||
| colorError: themes['errorColor'], | |||
| colorWarning: themes['warningColor'], | |||
| colorLink: themes['primaryColor'], | |||
| colorText: themes['textColor'], | |||
| controlHeightLG: 46, | |||
| }, | |||
| components: { | |||
| Button: { | |||
| defaultBg: 'rgba(22, 100, 255, 0.06)', | |||
| defaultBorderColor: 'rgba(22, 100, 255, 0.11)', | |||
| defaultColor: themes['textColor'], | |||
| defaultHoverBg: 'rgba(22, 100, 255, 0.06)', | |||
| defaultHoverBorderColor: 'rgba(22, 100, 255, 0.5)', | |||
| defaultHoverColor: '#3F7FFF ', | |||
| defaultActiveBg: 'rgba(22, 100, 255, 0.12)', | |||
| defaultActiveBorderColor: 'rgba(22, 100, 255, 0.75)', | |||
| defaultActiveColor: themes['primaryColor'], | |||
| contentFontSize: parseInt(themes['fontSize']), | |||
| }, | |||
| Input: { | |||
| inputFontSize: parseInt(themes['fontSizeInput']), | |||
| inputFontSizeLG: parseInt(themes['fontSizeInputLg']), | |||
| paddingBlockLG: 10, | |||
| }, | |||
| Select: { | |||
| singleItemHeightLG: 46, | |||
| optionSelectedColor: themes['primaryColor'], | |||
| }, | |||
| Table: { | |||
| headerBg: 'rgba(242, 244, 247, 0.36)', | |||
| headerBorderRadius: 4, | |||
| rowSelectedBg: 'rgba(22, 100, 255, 0.05)', | |||
| }, | |||
| Tabs: { | |||
| titleFontSize: 16, | |||
| }, | |||
| Form: { | |||
| labelColor: 'rgba(29, 29, 32, 0.8);', | |||
| }, | |||
| Breadcrumb: { | |||
| iconFontSize: parseInt(themes['fontSize']), | |||
| linkColor: 'rgba(29, 29, 32, 0.7)', | |||
| separatorColor: 'rgba(29, 29, 32, 0.7)', | |||
| }, | |||
| }, | |||
| }} | |||
| > | |||
| <App message={{ maxCount: 3 }}> | |||
| <Router> | |||
| <Story /> | |||
| </Router> | |||
| </App> | |||
| </ConfigProvider> | |||
| </React.Fragment> | |||
| ), | |||
| @@ -41,7 +41,7 @@ export async function getInitialState(): Promise<GlobalInitialState> { | |||
| roleNames: response.user.roles, | |||
| } as API.CurrentUser; | |||
| } catch (error) { | |||
| console.error('1111', error); | |||
| console.error('getInitialState', error); | |||
| gotoLoginPage(); | |||
| } | |||
| return undefined; | |||
| @@ -215,7 +215,7 @@ export const antd: RuntimeAntdConfig = (memo) => { | |||
| defaultColor: themes['textColor'], | |||
| defaultHoverBg: 'rgba(22, 100, 255, 0.06)', | |||
| defaultHoverBorderColor: 'rgba(22, 100, 255, 0.5)', | |||
| defaultHoverColor: '#3F7FFF ', | |||
| defaultHoverColor: '#3F7FFF', | |||
| defaultActiveBg: 'rgba(22, 100, 255, 0.12)', | |||
| defaultActiveBorderColor: 'rgba(22, 100, 255, 0.75)', | |||
| defaultActiveColor: themes['primaryColor'], | |||
| @@ -18,15 +18,23 @@ type BasicInfoItemProps = { | |||
| classPrefix: string; | |||
| /** 标题是否显示省略号 */ | |||
| labelEllipsis?: boolean; | |||
| /** 标签对齐方式 */ | |||
| labelAlign?: 'start' | 'end' | 'justify'; | |||
| }; | |||
| function BasicInfoItem({ data, labelWidth, classPrefix, labelEllipsis }: BasicInfoItemProps) { | |||
| function BasicInfoItem({ | |||
| data, | |||
| labelWidth, | |||
| classPrefix, | |||
| labelEllipsis = true, | |||
| labelAlign = 'start', | |||
| }: BasicInfoItemProps) { | |||
| const { label, value, format, ellipsis } = data; | |||
| const formatValue = format ? format(value) : value; | |||
| const myClassName = `${classPrefix}__item`; | |||
| let valueComponent = undefined; | |||
| if (React.isValidElement(formatValue)) { | |||
| valueComponent = formatValue; | |||
| valueComponent = <div className={`${myClassName}__node`}>{formatValue}</div>; | |||
| } else if (Array.isArray(formatValue)) { | |||
| valueComponent = ( | |||
| <div className={`${myClassName}__value-container`}> | |||
| @@ -59,8 +67,14 @@ function BasicInfoItem({ data, labelWidth, classPrefix, labelEllipsis }: BasicIn | |||
| } | |||
| return ( | |||
| <div className={myClassName} key={label}> | |||
| <div className={`${myClassName}__label`} style={{ width: labelWidth }}> | |||
| <Typography.Text ellipsis={labelEllipsis !== false ? { tooltip: label } : false}> | |||
| <div | |||
| className={`${myClassName}__label`} | |||
| style={{ width: labelWidth, textAlign: labelAlign, textAlignLast: labelAlign }} | |||
| > | |||
| <Typography.Text | |||
| ellipsis={labelEllipsis !== false ? { tooltip: label } : false} | |||
| style={{ width: labelAlign === 'justify' ? '100%' : 'auto' }} | |||
| > | |||
| {label} | |||
| </Typography.Text> | |||
| </div> | |||
| @@ -21,7 +21,13 @@ type BasicInfoItemValueProps = { | |||
| url?: string; | |||
| }; | |||
| function BasicInfoItemValue({ value, link, url, ellipsis, classPrefix }: BasicInfoItemValueProps) { | |||
| function BasicInfoItemValue({ | |||
| value, | |||
| link, | |||
| url, | |||
| classPrefix, | |||
| ellipsis = true, | |||
| }: BasicInfoItemValueProps) { | |||
| const myClassName = `${classPrefix}__item__value`; | |||
| let component = undefined; | |||
| if (url && value) { | |||
| @@ -17,12 +17,6 @@ | |||
| color: @text-color-secondary; | |||
| font-size: @font-size-content; | |||
| line-height: 1.6; | |||
| text-align: justify; | |||
| text-align-last: justify; | |||
| .ant-typography { | |||
| width: 100% !important; | |||
| } | |||
| &::after { | |||
| position: absolute; | |||
| @@ -55,5 +49,29 @@ | |||
| text-underline-offset: 3px; | |||
| } | |||
| } | |||
| &__node { | |||
| flex: 1; | |||
| min-width: 0; | |||
| margin-left: 16px; | |||
| font-size: @font-size-content; | |||
| line-height: 1.6; | |||
| word-break: break-all; | |||
| } | |||
| } | |||
| } | |||
| .kf-basic-info--three-columns { | |||
| width: 100%; | |||
| .kf-basic-info__item { | |||
| width: calc((100% - 80px) / 3); | |||
| &__label { | |||
| font-size: @font-size; | |||
| } | |||
| &__value { | |||
| font-size: @font-size; | |||
| } | |||
| } | |||
| } | |||
| @@ -12,6 +12,10 @@ export type BasicInfoProps = { | |||
| labelWidth: number; | |||
| /** 标题是否显示省略号 */ | |||
| labelEllipsis?: boolean; | |||
| /** 是否一行三列 */ | |||
| threeColumns?: boolean; | |||
| /** 标签对齐方式 */ | |||
| labelAlign?: 'start' | 'end' | 'justify'; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| @@ -19,17 +23,26 @@ export type BasicInfoProps = { | |||
| }; | |||
| /** | |||
| * 基础信息展示组件,用于展示基础信息,一行两列,支持格式化数据 | |||
| * 基础信息展示组件,用于展示基础信息,支持一行两列或一行三列,支持数据格式化 | |||
| */ | |||
| export default function BasicInfo({ | |||
| datas, | |||
| className, | |||
| labelEllipsis, | |||
| style, | |||
| labelWidth, | |||
| labelEllipsis = true, | |||
| threeColumns = false, | |||
| labelAlign = 'start', | |||
| }: BasicInfoProps) { | |||
| return ( | |||
| <div className={classNames('kf-basic-info', className)} style={style}> | |||
| <div | |||
| className={classNames( | |||
| 'kf-basic-info', | |||
| { 'kf-basic-info--three-columns': threeColumns }, | |||
| className, | |||
| )} | |||
| style={style} | |||
| > | |||
| {datas.map((item) => ( | |||
| <BasicInfoItem | |||
| key={item.label} | |||
| @@ -37,6 +50,7 @@ export default function BasicInfo({ | |||
| labelWidth={labelWidth} | |||
| classPrefix="kf-basic-info" | |||
| labelEllipsis={labelEllipsis} | |||
| labelAlign={labelAlign} | |||
| /> | |||
| ))} | |||
| </div> | |||
| @@ -34,7 +34,6 @@ | |||
| &__value { | |||
| flex: 1; | |||
| min-width: 0; | |||
| margin: 0 !important; | |||
| padding: 12px 20px 4px; | |||
| font-size: @font-size; | |||
| word-break: break-all; | |||
| @@ -56,5 +55,13 @@ | |||
| text-underline-offset: 3px; | |||
| } | |||
| } | |||
| &__node { | |||
| flex: 1; | |||
| min-width: 0; | |||
| padding: 12px 20px; | |||
| font-size: @font-size; | |||
| word-break: break-all; | |||
| } | |||
| } | |||
| } | |||
| @@ -6,7 +6,7 @@ import './index.less'; | |||
| export type { BasicInfoData, BasicInfoLink }; | |||
| /** | |||
| * 表格基础信息展示组件,用于展示基础信息,一行四列,支持格式化数据 | |||
| * 表格基础信息展示组件,用于展示基础信息,一行四列,支持数据格式化 | |||
| */ | |||
| export default function BasicTableInfo({ | |||
| datas, | |||
| @@ -21,7 +21,7 @@ export default function BasicTableInfo({ | |||
| for (let i = 0; i < 4 - remainder; i++) { | |||
| array.push({ | |||
| label: '', | |||
| value: '', | |||
| value: false, // 用于区分是否是空数据,不能是空字符串、null、undefined | |||
| }); | |||
| } | |||
| } | |||
| @@ -2,22 +2,27 @@ import classNames from 'classnames'; | |||
| import './index.less'; | |||
| type FullScreenFrameProps = { | |||
| /** URL */ | |||
| url: string; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| onload?: (e?: React.SyntheticEvent<HTMLIFrameElement, Event>) => void; | |||
| onerror?: (e?: React.SyntheticEvent<HTMLIFrameElement, Event>) => void; | |||
| /** 加载完成回调 */ | |||
| onLoad?: (e?: React.SyntheticEvent<HTMLIFrameElement, Event>) => void; | |||
| /** 加载失败回调 */ | |||
| onError?: (e?: React.SyntheticEvent<HTMLIFrameElement, Event>) => void; | |||
| }; | |||
| function FullScreenFrame({ url, className, style, onload, onerror }: FullScreenFrameProps) { | |||
| function FullScreenFrame({ url, className, style, onLoad, onError }: FullScreenFrameProps) { | |||
| return ( | |||
| <div className={classNames('kf-full-screen-frame', className ?? '')} style={style}> | |||
| {url && ( | |||
| <iframe | |||
| src={url} | |||
| className="kf-full-screen-frame__iframe" | |||
| onLoad={onload} | |||
| onError={onerror} | |||
| onLoad={onLoad} | |||
| onError={onError} | |||
| ></iframe> | |||
| )} | |||
| </div> | |||
| @@ -66,7 +66,7 @@ function IframePage({ type, className, style }: IframePageProps) { | |||
| return ( | |||
| <div className={classNames('kf-iframe-page', className)} style={style}> | |||
| {loading && createPortal(<KFSpin size="large" />, document.body)} | |||
| <FullScreenFrame url={iframeUrl} onload={hideLoading} onerror={hideLoading} /> | |||
| <FullScreenFrame url={iframeUrl} onLoad={hideLoading} onError={hideLoading} /> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -3,14 +3,23 @@ import InfoGroupTitle from '../InfoGroupTitle'; | |||
| import './index.less'; | |||
| type InfoGroupProps = { | |||
| /** 标题 */ | |||
| title: string; | |||
| height?: string | number; // 如果要纵向滚动,需要设置高度 | |||
| width?: string | number; // 如果要横向滚动,需要设置宽度 | |||
| /** 高度, 如果要纵向滚动,需要设置高度 */ | |||
| height?: string | number; | |||
| /** 宽度, 如果要横向滚动,需要设置宽度 */ | |||
| width?: string | number; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| /** 子元素 */ | |||
| children?: React.ReactNode; | |||
| }; | |||
| /** | |||
| * 信息组,用于展示基本信息,支持横向、纵向滚动。自动机器学习、超参数寻优都是使用这个组件 | |||
| */ | |||
| function InfoGroup({ title, height, width, className, style, children }: InfoGroupProps) { | |||
| const contentStyle: React.CSSProperties = {}; | |||
| if (height) { | |||
| @@ -1,7 +1,8 @@ | |||
| .kf-info-group-title { | |||
| box-sizing: border-box; | |||
| width: 100%; | |||
| height: 56px; | |||
| padding-left: @content-padding; | |||
| padding: 0 @content-padding; | |||
| background: linear-gradient( | |||
| 179.03deg, | |||
| rgba(199, 223, 255, 0.12) 0%, | |||
| @@ -21,6 +22,7 @@ | |||
| color: @text-color; | |||
| font-weight: 500; | |||
| font-size: @font-size-title; | |||
| .singleLine(); | |||
| &::after { | |||
| position: absolute; | |||
| @@ -3,11 +3,17 @@ import classNames from 'classnames'; | |||
| import './index.less'; | |||
| type InfoGroupTitleProps = { | |||
| /** 标题 */ | |||
| title: string; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| }; | |||
| /** | |||
| * 信息组标题 | |||
| */ | |||
| function InfoGroupTitle({ title, style, className }: InfoGroupTitleProps) { | |||
| return ( | |||
| <Flex align="center" className={classNames('kf-info-group-title', className)} style={style}> | |||
| @@ -33,7 +33,7 @@ | |||
| margin-top: 20px; | |||
| margin-bottom: 30px; | |||
| &__back-btn { | |||
| &__button { | |||
| height: 32px; | |||
| } | |||
| } | |||
| @@ -9,15 +9,24 @@ export enum EmptyType { | |||
| } | |||
| type EmptyProps = { | |||
| className?: string; | |||
| style?: React.CSSProperties; | |||
| /** 类型 */ | |||
| type: EmptyType; | |||
| /** 标题 */ | |||
| title?: string; | |||
| /** 内容 */ | |||
| content?: string; | |||
| /** 是否有页脚,如果有默认是一个按钮 */ | |||
| hasFooter?: boolean; | |||
| footer?: () => React.ReactNode; | |||
| /** 按钮标题,默认是"刷新" */ | |||
| buttonTitle?: string; | |||
| onRefresh?: () => void; | |||
| /** 按钮点击回调 */ | |||
| onButtonClick?: () => void; | |||
| /** 自定义页脚内容 */ | |||
| footer?: () => React.ReactNode; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| }; | |||
| function getEmptyImage(type: EmptyType) { | |||
| @@ -31,6 +40,7 @@ function getEmptyImage(type: EmptyType) { | |||
| } | |||
| } | |||
| /** 空状态 */ | |||
| function KFEmpty({ | |||
| className, | |||
| style, | |||
| @@ -40,7 +50,7 @@ function KFEmpty({ | |||
| hasFooter = true, | |||
| footer, | |||
| buttonTitle = '刷新', | |||
| onRefresh, | |||
| onButtonClick, | |||
| }: EmptyProps) { | |||
| const image = getEmptyImage(type); | |||
| @@ -54,7 +64,7 @@ function KFEmpty({ | |||
| {footer ? ( | |||
| footer() | |||
| ) : ( | |||
| <Button className="kf-empty__footer__back-btn" type="primary" onClick={onRefresh}> | |||
| <Button className="kf-empty__footer__button" type="primary" onClick={onButtonClick}> | |||
| {buttonTitle} | |||
| </Button> | |||
| )} | |||
| @@ -14,14 +14,20 @@ const Icon = createFromIconfontCN({ | |||
| type IconFontProps = Parameters<typeof Icon>[0]; | |||
| interface KFIconProps extends IconFontProps { | |||
| /** 图标 */ | |||
| type: string; | |||
| /** 字体大小 */ | |||
| font?: number; | |||
| /** 字体颜色 */ | |||
| color?: string; | |||
| style?: React.CSSProperties; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| } | |||
| function KFIcon({ type, font = 15, color = '', style = {}, className, ...rest }: KFIconProps) { | |||
| /** 封装 iconfont 图标 */ | |||
| function KFIcon({ type, font = 15, color, className, style, ...rest }: KFIconProps) { | |||
| const iconStyle = { | |||
| ...style, | |||
| fontSize: font, | |||
| @@ -6,12 +6,16 @@ | |||
| import classNames from 'classnames'; | |||
| import React from 'react'; | |||
| import './index.less'; | |||
| import './KFModalTitle.less'; | |||
| type ModalTitleProps = { | |||
| /** 标题 */ | |||
| title: React.ReactNode; | |||
| /** 图片 */ | |||
| image?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| }; | |||
| @@ -4,19 +4,21 @@ | |||
| * @Description: 自定义 Modal | |||
| */ | |||
| import ModalTitle from '@/components/ModalTitle'; | |||
| import { Modal, type ModalProps } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import KFModalTitle from './KFModalTitle'; | |||
| import './index.less'; | |||
| export interface KFModalProps extends ModalProps { | |||
| image?: string; | |||
| } | |||
| /** 自定义 Modal */ | |||
| function KFModal({ | |||
| title, | |||
| image, | |||
| children, | |||
| className = '', | |||
| className, | |||
| centered, | |||
| maskClosable, | |||
| ...rest | |||
| @@ -27,7 +29,7 @@ function KFModal({ | |||
| {...rest} | |||
| centered={centered ?? true} | |||
| maskClosable={maskClosable ?? false} | |||
| title={<ModalTitle title={title} image={image}></ModalTitle>} | |||
| title={<KFModalTitle title={title} image={image} />} | |||
| > | |||
| {children} | |||
| </Modal> | |||
| @@ -8,32 +8,40 @@ import classNames from 'classnames'; | |||
| import './index.less'; | |||
| export type KFRadioItem = { | |||
| key: string; | |||
| title: string; | |||
| value: string; | |||
| icon?: React.ReactNode; | |||
| }; | |||
| type KFRadioProps = { | |||
| /** 选项 */ | |||
| items: KFRadioItem[]; | |||
| /** 当前选中项 */ | |||
| value?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 选中回调 */ | |||
| onChange?: (value: string) => void; | |||
| }; | |||
| /** | |||
| * 自定义 Radio | |||
| */ | |||
| function KFRadio({ items, value, style, className, onChange }: KFRadioProps) { | |||
| return ( | |||
| <span className={classNames('kf-radio', className)} style={style}> | |||
| {items.map((item) => { | |||
| return ( | |||
| <span | |||
| key={item.key} | |||
| key={item.value} | |||
| className={ | |||
| value === item.key | |||
| value === item.value | |||
| ? classNames('kf-radio__item', 'kf-radio__item--active') | |||
| : 'kf-radio__item' | |||
| } | |||
| onClick={() => onChange?.(item.key)} | |||
| onClick={() => onChange?.(item.value)} | |||
| > | |||
| {item.icon} | |||
| <span style={{ marginLeft: '5px' }}>{item.title}</span> | |||
| @@ -5,13 +5,14 @@ | |||
| */ | |||
| import { Spin, SpinProps } from 'antd'; | |||
| import styles from './index.less'; | |||
| import './index.less'; | |||
| /** 自定义 Spin */ | |||
| function KFSpin(props: SpinProps) { | |||
| return ( | |||
| <div className={styles['kf-spin']}> | |||
| <div className={'kf-spin'}> | |||
| <Spin {...props} /> | |||
| <div className={styles['kf-spin__label']}>加载中</div> | |||
| <div className={'kf-spin__label'}>加载中</div> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -1,19 +0,0 @@ | |||
| .kf-label-value { | |||
| display: flex; | |||
| align-items: flex-start; | |||
| font-size: 16px; | |||
| line-height: 1.6; | |||
| &__label { | |||
| flex: none; | |||
| width: 80px; | |||
| color: @text-color-secondary; | |||
| } | |||
| &__value { | |||
| flex: 1; | |||
| color: @text-color; | |||
| white-space: pre-line; | |||
| word-break: break-all; | |||
| } | |||
| } | |||
| @@ -1,20 +0,0 @@ | |||
| import classNames from 'classnames'; | |||
| import './index.less'; | |||
| type labelValueProps = { | |||
| label: string; | |||
| value?: any; | |||
| className?: string; | |||
| style?: React.CSSProperties; | |||
| }; | |||
| function LabelValue({ label, value, className, style }: labelValueProps) { | |||
| return ( | |||
| <div className={classNames('kf-label-value', className)} style={style}> | |||
| <div className="kf-label-value__label">{label}</div> | |||
| <div className="kf-label-value__value">{value ?? '--'}</div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default LabelValue; | |||
| @@ -1,5 +1,4 @@ | |||
| .menu-icon-selector { | |||
| // grid 布局,每行显示 8 个图标 | |||
| display: grid; | |||
| grid-template-columns: repeat(4, 80px); | |||
| gap: 20px; | |||
| @@ -10,7 +9,7 @@ | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| width: 80x; | |||
| width: 80px; | |||
| height: 80px; | |||
| border: 1px solid transparent; | |||
| border-radius: 4px; | |||
| @@ -12,7 +12,9 @@ import { useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| interface MenuIconSelectorProps extends Omit<ModalProps, 'onOk'> { | |||
| /** 选中的图标 */ | |||
| selectedIcon?: string; | |||
| /** 选择回调 */ | |||
| onOk: (param: string) => void; | |||
| } | |||
| @@ -21,6 +23,7 @@ type IconObject = { | |||
| font_class: string; | |||
| }; | |||
| /** 菜单图标选择器 */ | |||
| function MenuIconSelector({ open, selectedIcon, onOk, ...rest }: MenuIconSelectorProps) { | |||
| const [icons, setIcons] = useState<IconObject[]>([]); | |||
| useEffect(() => { | |||
| @@ -8,10 +8,17 @@ import React from 'react'; | |||
| import './index.less'; | |||
| type PageTitleProps = { | |||
| /** 标题 */ | |||
| title: string; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| }; | |||
| /** | |||
| * 页面标题 | |||
| */ | |||
| function PageTitle({ title, style, className = '' }: PageTitleProps) { | |||
| return ( | |||
| <div className={classNames('kf-page-title', className)} style={style}> | |||
| @@ -1,9 +1,8 @@ | |||
| import { useModel } from '@umijs/max'; | |||
| import React from 'react'; | |||
| // import KFBreadcrumb from '../KFBreadcrumb'; | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { ProBreadcrumb } from '@ant-design/pro-components'; | |||
| import { useModel } from '@umijs/max'; | |||
| import { Button } from 'antd'; | |||
| import React from 'react'; | |||
| import Avatar from './AvatarDropdown'; | |||
| import styles from './index.less'; | |||
| // import { SelectLang } from '@umijs/max'; | |||
| @@ -44,8 +43,6 @@ const GlobalHeaderRight: React.FC = () => { | |||
| <ProBreadcrumb></ProBreadcrumb> | |||
| {/* <KFBreadcrumb /> */} | |||
| <Avatar menu={true} /> | |||
| {/* <SelectLang className={actionClassName} /> */} | |||
| </div> | |||
| @@ -8,13 +8,20 @@ import classNames from 'classnames'; | |||
| import './index.less'; | |||
| type SubAreaTitleProps = { | |||
| /** 标题 */ | |||
| title: string; | |||
| /** 图片 */ | |||
| image?: string; | |||
| style?: React.CSSProperties; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| style?: React.CSSProperties; | |||
| }; | |||
| function SubAreaTitle({ title, image, style, className }: SubAreaTitleProps) { | |||
| /** | |||
| * 表单或者详情页的分区标题 | |||
| */ | |||
| function SubAreaTitle({ title, image, className, style }: SubAreaTitleProps) { | |||
| return ( | |||
| <div className={classNames('kf-subarea-title', className)} style={style}> | |||
| {image && ( | |||
| @@ -12,7 +12,7 @@ const NoFoundPage = () => { | |||
| content={'很抱歉,您访问的页面地址有误,\n或者该页面不存在。'} | |||
| hasFooter={true} | |||
| buttonTitle="返回首页" | |||
| onRefresh={() => navigate('/')} | |||
| onButtonClick={() => navigate('/')} | |||
| ></KFEmpty> | |||
| ); | |||
| }; | |||
| @@ -197,7 +197,7 @@ function CodeConfigList() { | |||
| title="暂无数据" | |||
| content={'很抱歉,没有搜索到您想要的内容\n建议刷新试试'} | |||
| hasFooter={true} | |||
| onRefresh={getDataList} | |||
| onButtonClick={getDataList} | |||
| /> | |||
| )} | |||
| </div> | |||
| @@ -226,7 +226,7 @@ function ResourceList( | |||
| title="暂无数据" | |||
| content={'很抱歉,没有搜索到您想要的内容\n建议刷新试试'} | |||
| hasFooter={true} | |||
| onRefresh={getDataList} | |||
| onButtonClick={getDataList} | |||
| /> | |||
| )} | |||
| </div> | |||
| @@ -36,13 +36,13 @@ enum ComputingResourceType { | |||
| const EditorRadioItems: KFRadioItem[] = [ | |||
| { | |||
| key: ComputingResourceType.GPU, | |||
| title: '英伟达GPU', | |||
| value: ComputingResourceType.GPU, | |||
| icon: <KFIcon type="icon-jiyugongwangjingxiang" />, | |||
| }, | |||
| { | |||
| key: ComputingResourceType.NPU, | |||
| title: '昇腾NPU', | |||
| value: ComputingResourceType.NPU, | |||
| icon: <KFIcon type="icon-bendishangchuan" />, | |||
| }, | |||
| ]; | |||
| @@ -30,13 +30,13 @@ type FormData = { | |||
| const mirrorRadioItems: KFRadioItem[] = [ | |||
| { | |||
| key: CommonTabKeys.Public, | |||
| title: '基于公网镜像', | |||
| value: CommonTabKeys.Public, | |||
| icon: <KFIcon type="icon-jiyugongwangjingxiang" />, | |||
| }, | |||
| { | |||
| key: CommonTabKeys.Private, | |||
| title: '本地上传', | |||
| value: CommonTabKeys.Private, | |||
| icon: <KFIcon type="icon-bendishangchuan" />, | |||
| }, | |||
| ]; | |||
| @@ -401,7 +401,12 @@ function ServiceInfo() { | |||
| image={require('@/assets/img/mirror-basic.png')} | |||
| style={{ marginBottom: '26px', flex: 'none' }} | |||
| ></SubAreaTitle> | |||
| <BasicInfo datas={basicInfo} labelWidth={66} style={{ flex: 'none' }}></BasicInfo> | |||
| <BasicInfo | |||
| datas={basicInfo} | |||
| labelWidth={66} | |||
| labelAlign="justify" | |||
| style={{ flex: 'none' }} | |||
| ></BasicInfo> | |||
| <SubAreaTitle | |||
| title="服务版本" | |||
| image={require('@/assets/img/service-version.png')} | |||
| @@ -113,7 +113,7 @@ function VersionBasicInfo({ info }: BasicInfoProps) { | |||
| }, | |||
| ]; | |||
| return <BasicInfo datas={datas} labelWidth={66}></BasicInfo>; | |||
| return <BasicInfo datas={datas} labelWidth={66} labelAlign="justify"></BasicInfo>; | |||
| } | |||
| export default VersionBasicInfo; | |||
| @@ -1,4 +1,3 @@ | |||
| import RobotFrame from '@/components/RobotFrame'; | |||
| import { useDraggable } from '@/hooks/draggable'; | |||
| import { getWorkspaceOverviewReq } from '@/services/workspace'; | |||
| import { ExperimentInstance } from '@/types'; | |||
| @@ -9,6 +8,7 @@ import AssetsManagement from './components/AssetsManagement'; | |||
| import ExperimentChart, { type ExperimentStatistics } from './components/ExperimentChart'; | |||
| import ExperitableTable from './components/ExperimentTable'; | |||
| import QuickStart from './components/QuickStart'; | |||
| import RobotFrame from './components/RobotFrame'; | |||
| import TotalStatistics from './components/TotalStatistics'; | |||
| import UserSpace from './components/UserSpace'; | |||
| import WorkspaceIntro from './components/WorkspaceIntro'; | |||
| @@ -12,7 +12,7 @@ const MissingPage = () => { | |||
| content={'很抱歉,您访问的正在开发中,\n请耐心等待。'} | |||
| hasFooter={true} | |||
| buttonTitle="返回首页" | |||
| onRefresh={() => navigate('/')} | |||
| onButtonClick={() => navigate('/')} | |||
| ></KFEmpty> | |||
| ); | |||
| }; | |||
| @@ -66,16 +66,32 @@ export const Primary: Story = { | |||
| }, | |||
| }, | |||
| { label: '日期', value: new Date(), format: formatDate }, | |||
| { label: '数组', value: ['a', 'b'], format: formatList }, | |||
| { label: '数组', value: ['a', 'b', 'c'], format: formatList }, | |||
| { | |||
| label: '带省略号', | |||
| value: '这是一个很长的字符串这是一个很长的字符串这是一个很长的字符串这是一个很长的字符串', | |||
| }, | |||
| { | |||
| label: '多行', | |||
| value: [ | |||
| { | |||
| label: '服务名称', | |||
| value: '手写体识别', | |||
| }, | |||
| { | |||
| label: '服务名称', | |||
| value: '人脸识别', | |||
| }, | |||
| ], | |||
| format: (value: any) => | |||
| value.map((item: any) => ({ | |||
| value: item.label + ':' + item.value, | |||
| })), | |||
| }, | |||
| { | |||
| label: '自定义组件', | |||
| value: ( | |||
| <Button type="primary" style={{ marginLeft: 16, height: 26 }}> | |||
| <Button type="primary" style={{ height: 26 }}> | |||
| click | |||
| </Button> | |||
| ), | |||
| @@ -1,19 +1,6 @@ | |||
| import BasicTableInfo from '@/components/BasicTableInfo'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { Button } from 'antd'; | |||
| const formatList = (value: string[] | null | undefined): string => { | |||
| if ( | |||
| value === undefined || | |||
| value === null || | |||
| Array.isArray(value) === false || | |||
| value.length === 0 | |||
| ) { | |||
| return '--'; | |||
| } | |||
| return value.join(','); | |||
| }; | |||
| import * as BasicInfoStories from './BasicInfo.stories'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| @@ -39,47 +26,7 @@ type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| datas: [ | |||
| { label: '服务名称', value: '手写体识别' }, | |||
| { | |||
| label: '无数据', | |||
| value: '', | |||
| }, | |||
| { | |||
| label: '外部链接', | |||
| value: 'https://www.baidu.com/', | |||
| format: (value: string) => { | |||
| return { | |||
| value: '百度', | |||
| url: value, | |||
| }; | |||
| }, | |||
| }, | |||
| { | |||
| label: '内部链接', | |||
| value: 'https://www.baidu.com/', | |||
| format: () => { | |||
| return { | |||
| value: '实验', | |||
| link: '/pipeline/experiment/instance/1/1', | |||
| }; | |||
| }, | |||
| }, | |||
| { label: '日期', value: new Date(), format: formatDate }, | |||
| { label: '数组', value: ['a', 'b'], format: formatList }, | |||
| { | |||
| label: '带省略号', | |||
| value: '这是一个很长的字符串这是一个很长的字符串这是一个很长的字符串这是一个很长的字符串', | |||
| }, | |||
| { | |||
| label: '自定义组件', | |||
| value: ( | |||
| <div style={{ padding: '12px 20px 4px' }}> | |||
| <Button type="primary">click</Button> | |||
| </div> | |||
| ), | |||
| }, | |||
| ], | |||
| ...BasicInfoStories.Primary.args, | |||
| labelWidth: 70, | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,31 @@ | |||
| import FullScreenFrame from '@/components/FullScreenFrame'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/FullScreenFrame', | |||
| component: FullScreenFrame, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| // args: { onClick: fn() }, | |||
| } satisfies Meta<typeof FullScreenFrame>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| url: 'https://www.hao123.com/', | |||
| style: { height: '500px' }, | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,31 @@ | |||
| import InfoGroup from '@/components/InfoGroup'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/InfoGroup', | |||
| component: InfoGroup, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| // args: { onClick: fn() }, | |||
| } satisfies Meta<typeof InfoGroup>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| title: '基本信息', | |||
| children: <div>I am a child element </div>, | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,30 @@ | |||
| import InfoGroupTitle from '@/components/InfoGroupTitle'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/InfoGroupTitle', | |||
| component: InfoGroupTitle, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| // args: { onClick: fn() }, | |||
| } satisfies Meta<typeof InfoGroupTitle>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| title: '基本信息', | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,53 @@ | |||
| import KFEmpty, { EmptyType } from '@/components/KFEmpty'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { fn } from '@storybook/test'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/KFEmpty', | |||
| component: KFEmpty, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| type: { control: 'select', options: Object.values(EmptyType) }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| args: { onButtonClick: fn() }, | |||
| } satisfies Meta<typeof KFEmpty>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Developing: Story = { | |||
| args: { | |||
| type: EmptyType.Developing, | |||
| title: '敬请期待~', | |||
| content: '很抱歉,您访问的正在开发中,\n请耐心等待。', | |||
| hasFooter: true, | |||
| buttonTitle: '返回首页', | |||
| }, | |||
| }; | |||
| export const NoData: Story = { | |||
| args: { | |||
| type: EmptyType.NoData, | |||
| title: '暂无数据', | |||
| content: '很抱歉,没有搜索到您想要的内容\n建议刷新试试', | |||
| hasFooter: true, | |||
| }, | |||
| }; | |||
| export const NotFound: Story = { | |||
| args: { | |||
| type: EmptyType.NotFound, | |||
| title: '404', | |||
| content: '很抱歉,您访问的页面地址有误,\n或者该页面不存在。', | |||
| hasFooter: true, | |||
| buttonTitle: '返回首页', | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,41 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { Button } from 'antd'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/KFIcon', | |||
| component: KFIcon, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: {}, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| args: {}, | |||
| } satisfies Meta<typeof KFIcon>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| type: 'icon-xiazai', | |||
| }, | |||
| }; | |||
| export const InButton: Story = { | |||
| args: { | |||
| type: 'icon-xiazai', | |||
| }, | |||
| render: function Render(args) { | |||
| return ( | |||
| <Button icon={<KFIcon {...args} />} type="primary"> | |||
| 下载 | |||
| </Button> | |||
| ); | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,59 @@ | |||
| import CreateExperiment from '@/assets/img/create-experiment.png'; | |||
| import KFModal from '@/components/KFModal'; | |||
| import { useArgs } from '@storybook/preview-api'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { fn } from '@storybook/test'; | |||
| import { Button } from 'antd'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/KFModal', | |||
| component: KFModal, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| args: { onCancel: fn(), onOk: fn() }, | |||
| } satisfies Meta<typeof KFModal>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| title: '创建实验', | |||
| image: CreateExperiment, | |||
| open: false, | |||
| children: '这是一个模态框', | |||
| }, | |||
| render: function Render(args) { | |||
| const [{ open }, updateArgs] = useArgs(); | |||
| function onClick() { | |||
| updateArgs({ open: true }); | |||
| } | |||
| function onOk() { | |||
| updateArgs({ open: false }); | |||
| } | |||
| function onCancel() { | |||
| updateArgs({ open: false }); | |||
| } | |||
| return ( | |||
| <> | |||
| <Button type="primary" onClick={onClick}> | |||
| 打开 KFModal | |||
| </Button> | |||
| <KFModal {...args} open={open} onOk={onOk} onCancel={onCancel} /> | |||
| </> | |||
| ); | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,52 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import KFRadio from '@/components/KFRadio'; | |||
| import { useArgs } from '@storybook/preview-api'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/KFRadio', | |||
| component: KFRadio, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| // args: { onClick: fn() }, | |||
| } satisfies Meta<typeof KFRadio>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| items: [ | |||
| { | |||
| title: '英伟达GPU', | |||
| value: 'GPU', | |||
| icon: <KFIcon type="icon-jiyugongwangjingxiang" />, | |||
| }, | |||
| { | |||
| title: '昇腾NPU', | |||
| value: 'NPU', | |||
| icon: <KFIcon type="icon-bendishangchuan" />, | |||
| }, | |||
| ], | |||
| value: 'GPU', | |||
| }, | |||
| render: function Render(args) { | |||
| const [{ value }, updateArgs] = useArgs(); | |||
| function onChange(value: string) { | |||
| updateArgs({ value: value }); | |||
| } | |||
| return <KFRadio {...args} value={value} onChange={onChange} />; | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,35 @@ | |||
| import KFSpin from '@/components/KFSpin'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/KFSpin', | |||
| component: KFSpin, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| decorators: [ | |||
| (Story) => ( | |||
| <div style={{ height: '200px' }}> | |||
| <Story /> | |||
| </div> | |||
| ), | |||
| ], | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| // args: { onClick: fn() }, | |||
| } satisfies Meta<typeof KFSpin>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: {}, | |||
| }; | |||
| @@ -0,0 +1,65 @@ | |||
| import MenuIconSelector from '@/components/MenuIconSelector'; | |||
| import { useArgs } from '@storybook/preview-api'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| import { fn } from '@storybook/test'; | |||
| import { Button } from 'antd'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/MenuIconSelector', | |||
| component: MenuIconSelector, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| open: { | |||
| control: 'boolean', | |||
| description: '对话框是否可见', | |||
| }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| args: { onCancel: fn(), onOk: fn() }, | |||
| } satisfies Meta<typeof MenuIconSelector>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| selectedIcon: 'manual-icon', | |||
| open: false, | |||
| }, | |||
| render: function Render(args) { | |||
| const [{ open, selectedIcon }, updateArgs] = useArgs(); | |||
| function onClick() { | |||
| updateArgs({ open: true }); | |||
| } | |||
| function onOk(value: string) { | |||
| updateArgs({ selectedIcon: value, open: false }); | |||
| } | |||
| function onCancel() { | |||
| updateArgs({ open: false }); | |||
| } | |||
| return ( | |||
| <> | |||
| <Button type="primary" onClick={onClick}> | |||
| 打开 MenuIconSelector | |||
| </Button> | |||
| <MenuIconSelector | |||
| {...args} | |||
| open={open} | |||
| selectedIcon={selectedIcon} | |||
| onOk={onOk} | |||
| onCancel={onCancel} | |||
| /> | |||
| </> | |||
| ); | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,30 @@ | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/PageTitle', | |||
| component: PageTitle, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| // args: { onClick: fn() }, | |||
| } satisfies Meta<typeof PageTitle>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| title: '数据集列表', | |||
| }, | |||
| }; | |||
| @@ -0,0 +1,32 @@ | |||
| import MirrorBasic from '@/assets/img/mirror-basic.png'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import type { Meta, StoryObj } from '@storybook/react'; | |||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||
| const meta = { | |||
| title: 'Components/SubAreaTitle', | |||
| component: SubAreaTitle, | |||
| parameters: { | |||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||
| // layout: 'centered', | |||
| }, | |||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||
| tags: ['autodocs'], | |||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||
| argTypes: { | |||
| // backgroundColor: { control: 'color' }, | |||
| }, | |||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | |||
| // args: { onClick: fn() }, | |||
| } satisfies Meta<typeof SubAreaTitle>; | |||
| export default meta; | |||
| type Story = StoryObj<typeof meta>; | |||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||
| export const Primary: Story = { | |||
| args: { | |||
| title: '基本信息', | |||
| image: MirrorBasic, | |||
| }, | |||
| }; | |||