diff --git a/react-ui/.storybook/main.ts b/react-ui/.storybook/main.ts index 551aa73b..ec302bf7 100644 --- a/react-ui/.storybook/main.ts +++ b/react-ui/.storybook/main.ts @@ -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: { diff --git a/react-ui/.storybook/preview.tsx b/react-ui/.storybook/preview.tsx index 9e8c4b60..fc1de258 100644 --- a/react-ui/.storybook/preview.tsx +++ b/react-ui/.storybook/preview.tsx @@ -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) => ( - - - - + + + + + + ), diff --git a/react-ui/src/app.tsx b/react-ui/src/app.tsx index 275e5e17..dcc4d247 100644 --- a/react-ui/src/app.tsx +++ b/react-ui/src/app.tsx @@ -41,7 +41,7 @@ export async function getInitialState(): Promise { 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'], diff --git a/react-ui/src/components/BasicInfo/BasicInfoItem.tsx b/react-ui/src/components/BasicInfo/BasicInfoItem.tsx index 871b8fb5..86e63891 100644 --- a/react-ui/src/components/BasicInfo/BasicInfoItem.tsx +++ b/react-ui/src/components/BasicInfo/BasicInfoItem.tsx @@ -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 =
{formatValue}
; } else if (Array.isArray(formatValue)) { valueComponent = (
@@ -59,8 +67,14 @@ function BasicInfoItem({ data, labelWidth, classPrefix, labelEllipsis }: BasicIn } return (
-
- +
+ {label}
diff --git a/react-ui/src/components/BasicInfo/BasicInfoItemValue.tsx b/react-ui/src/components/BasicInfo/BasicInfoItemValue.tsx index 7f36a13f..8f81e92c 100644 --- a/react-ui/src/components/BasicInfo/BasicInfoItemValue.tsx +++ b/react-ui/src/components/BasicInfo/BasicInfoItemValue.tsx @@ -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) { diff --git a/react-ui/src/components/BasicInfo/index.less b/react-ui/src/components/BasicInfo/index.less index 661938fb..ced2d2ba 100644 --- a/react-ui/src/components/BasicInfo/index.less +++ b/react-ui/src/components/BasicInfo/index.less @@ -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; + } } } diff --git a/react-ui/src/components/BasicInfo/index.tsx b/react-ui/src/components/BasicInfo/index.tsx index 78dc2cfc..11eacfde 100644 --- a/react-ui/src/components/BasicInfo/index.tsx +++ b/react-ui/src/components/BasicInfo/index.tsx @@ -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 ( -
+
{datas.map((item) => ( ))}
diff --git a/react-ui/src/components/BasicTableInfo/index.less b/react-ui/src/components/BasicTableInfo/index.less index 850af79c..479fe332 100644 --- a/react-ui/src/components/BasicTableInfo/index.less +++ b/react-ui/src/components/BasicTableInfo/index.less @@ -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; + } } } diff --git a/react-ui/src/components/BasicTableInfo/index.tsx b/react-ui/src/components/BasicTableInfo/index.tsx index fab761e2..68c350bd 100644 --- a/react-ui/src/components/BasicTableInfo/index.tsx +++ b/react-ui/src/components/BasicTableInfo/index.tsx @@ -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 }); } } diff --git a/react-ui/src/components/FullScreenFrame/index.tsx b/react-ui/src/components/FullScreenFrame/index.tsx index a5c3413a..41fae94e 100644 --- a/react-ui/src/components/FullScreenFrame/index.tsx +++ b/react-ui/src/components/FullScreenFrame/index.tsx @@ -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) => void; - onerror?: (e?: React.SyntheticEvent) => void; + /** 加载完成回调 */ + onLoad?: (e?: React.SyntheticEvent) => void; + /** 加载失败回调 */ + onError?: (e?: React.SyntheticEvent) => void; }; -function FullScreenFrame({ url, className, style, onload, onerror }: FullScreenFrameProps) { +function FullScreenFrame({ url, className, style, onLoad, onError }: FullScreenFrameProps) { return (
{url && ( )}
diff --git a/react-ui/src/components/IFramePage/index.tsx b/react-ui/src/components/IFramePage/index.tsx index 9cce1a3b..c85afffb 100644 --- a/react-ui/src/components/IFramePage/index.tsx +++ b/react-ui/src/components/IFramePage/index.tsx @@ -66,7 +66,7 @@ function IframePage({ type, className, style }: IframePageProps) { return (
{loading && createPortal(, document.body)} - +
); } diff --git a/react-ui/src/components/InfoGroup/index.tsx b/react-ui/src/components/InfoGroup/index.tsx index 8cf4b4e8..01e8c01b 100644 --- a/react-ui/src/components/InfoGroup/index.tsx +++ b/react-ui/src/components/InfoGroup/index.tsx @@ -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) { diff --git a/react-ui/src/components/InfoGroupTitle/index.less b/react-ui/src/components/InfoGroupTitle/index.less index ee3c9011..26ed375a 100644 --- a/react-ui/src/components/InfoGroupTitle/index.less +++ b/react-ui/src/components/InfoGroupTitle/index.less @@ -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; diff --git a/react-ui/src/components/InfoGroupTitle/index.tsx b/react-ui/src/components/InfoGroupTitle/index.tsx index 7eec2ab8..7524a9f0 100644 --- a/react-ui/src/components/InfoGroupTitle/index.tsx +++ b/react-ui/src/components/InfoGroupTitle/index.tsx @@ -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 ( diff --git a/react-ui/src/components/KFEmpty/index.less b/react-ui/src/components/KFEmpty/index.less index 39f70281..3e5a85b4 100644 --- a/react-ui/src/components/KFEmpty/index.less +++ b/react-ui/src/components/KFEmpty/index.less @@ -33,7 +33,7 @@ margin-top: 20px; margin-bottom: 30px; - &__back-btn { + &__button { height: 32px; } } diff --git a/react-ui/src/components/KFEmpty/index.tsx b/react-ui/src/components/KFEmpty/index.tsx index e59b6f6b..b2a680f6 100644 --- a/react-ui/src/components/KFEmpty/index.tsx +++ b/react-ui/src/components/KFEmpty/index.tsx @@ -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() ) : ( - )} diff --git a/react-ui/src/components/KFIcon/index.tsx b/react-ui/src/components/KFIcon/index.tsx index d84257a7..38d3644c 100644 --- a/react-ui/src/components/KFIcon/index.tsx +++ b/react-ui/src/components/KFIcon/index.tsx @@ -14,14 +14,20 @@ const Icon = createFromIconfontCN({ type IconFontProps = Parameters[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, diff --git a/react-ui/src/components/ModalTitle/index.less b/react-ui/src/components/KFModal/KFModalTitle.less similarity index 100% rename from react-ui/src/components/ModalTitle/index.less rename to react-ui/src/components/KFModal/KFModalTitle.less diff --git a/react-ui/src/components/ModalTitle/index.tsx b/react-ui/src/components/KFModal/KFModalTitle.tsx similarity index 84% rename from react-ui/src/components/ModalTitle/index.tsx rename to react-ui/src/components/KFModal/KFModalTitle.tsx index 4c0179eb..d2ec3265 100644 --- a/react-ui/src/components/ModalTitle/index.tsx +++ b/react-ui/src/components/KFModal/KFModalTitle.tsx @@ -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; }; diff --git a/react-ui/src/components/KFModal/index.tsx b/react-ui/src/components/KFModal/index.tsx index c073ab27..4cb6a9c3 100644 --- a/react-ui/src/components/KFModal/index.tsx +++ b/react-ui/src/components/KFModal/index.tsx @@ -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={} + title={} > {children} diff --git a/react-ui/src/components/KFRadio/index.tsx b/react-ui/src/components/KFRadio/index.tsx index 4bb4ccce..7191dae6 100644 --- a/react-ui/src/components/KFRadio/index.tsx +++ b/react-ui/src/components/KFRadio/index.tsx @@ -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 ( {items.map((item) => { return ( onChange?.(item.key)} + onClick={() => onChange?.(item.value)} > {item.icon} {item.title} diff --git a/react-ui/src/components/KFSpin/index.tsx b/react-ui/src/components/KFSpin/index.tsx index 519ab6ef..161775b9 100644 --- a/react-ui/src/components/KFSpin/index.tsx +++ b/react-ui/src/components/KFSpin/index.tsx @@ -5,13 +5,14 @@ */ import { Spin, SpinProps } from 'antd'; -import styles from './index.less'; +import './index.less'; +/** 自定义 Spin */ function KFSpin(props: SpinProps) { return ( -
+
-
加载中
+
加载中
); } diff --git a/react-ui/src/components/LabelValue/index.less b/react-ui/src/components/LabelValue/index.less deleted file mode 100644 index 5f1b9b0c..00000000 --- a/react-ui/src/components/LabelValue/index.less +++ /dev/null @@ -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; - } -} diff --git a/react-ui/src/components/LabelValue/index.tsx b/react-ui/src/components/LabelValue/index.tsx deleted file mode 100644 index 22b9b3eb..00000000 --- a/react-ui/src/components/LabelValue/index.tsx +++ /dev/null @@ -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 ( -
-
{label}
-
{value ?? '--'}
-
- ); -} - -export default LabelValue; diff --git a/react-ui/src/components/MenuIconSelector/index.less b/react-ui/src/components/MenuIconSelector/index.less index 77529762..5a64a8d3 100644 --- a/react-ui/src/components/MenuIconSelector/index.less +++ b/react-ui/src/components/MenuIconSelector/index.less @@ -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; diff --git a/react-ui/src/components/MenuIconSelector/index.tsx b/react-ui/src/components/MenuIconSelector/index.tsx index dd57320e..fa38910b 100644 --- a/react-ui/src/components/MenuIconSelector/index.tsx +++ b/react-ui/src/components/MenuIconSelector/index.tsx @@ -12,7 +12,9 @@ import { useEffect, useState } from 'react'; import styles from './index.less'; interface MenuIconSelectorProps extends Omit { + /** 选中的图标 */ 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([]); useEffect(() => { diff --git a/react-ui/src/components/PageTitle/index.tsx b/react-ui/src/components/PageTitle/index.tsx index ca192454..ea8a65de 100644 --- a/react-ui/src/components/PageTitle/index.tsx +++ b/react-ui/src/components/PageTitle/index.tsx @@ -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 (
diff --git a/react-ui/src/components/RightContent/index.tsx b/react-ui/src/components/RightContent/index.tsx index 3ecb5777..9b84950a 100644 --- a/react-ui/src/components/RightContent/index.tsx +++ b/react-ui/src/components/RightContent/index.tsx @@ -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 = () => { - {/* */} - {/* */}
diff --git a/react-ui/src/components/SubAreaTitle/index.tsx b/react-ui/src/components/SubAreaTitle/index.tsx index cd07b206..4c94deee 100644 --- a/react-ui/src/components/SubAreaTitle/index.tsx +++ b/react-ui/src/components/SubAreaTitle/index.tsx @@ -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 (
{image && ( diff --git a/react-ui/src/pages/404.tsx b/react-ui/src/pages/404.tsx index dbacba53..d6f45451 100644 --- a/react-ui/src/pages/404.tsx +++ b/react-ui/src/pages/404.tsx @@ -12,7 +12,7 @@ const NoFoundPage = () => { content={'很抱歉,您访问的页面地址有误,\n或者该页面不存在。'} hasFooter={true} buttonTitle="返回首页" - onRefresh={() => navigate('/')} + onButtonClick={() => navigate('/')} > ); }; diff --git a/react-ui/src/pages/CodeConfig/List/index.tsx b/react-ui/src/pages/CodeConfig/List/index.tsx index 2efef04c..0c484e54 100644 --- a/react-ui/src/pages/CodeConfig/List/index.tsx +++ b/react-ui/src/pages/CodeConfig/List/index.tsx @@ -197,7 +197,7 @@ function CodeConfigList() { title="暂无数据" content={'很抱歉,没有搜索到您想要的内容\n建议刷新试试'} hasFooter={true} - onRefresh={getDataList} + onButtonClick={getDataList} /> )}
diff --git a/react-ui/src/pages/Dataset/components/ResourceList/index.tsx b/react-ui/src/pages/Dataset/components/ResourceList/index.tsx index 6f2c7523..9577ad41 100644 --- a/react-ui/src/pages/Dataset/components/ResourceList/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceList/index.tsx @@ -226,7 +226,7 @@ function ResourceList( title="暂无数据" content={'很抱歉,没有搜索到您想要的内容\n建议刷新试试'} hasFooter={true} - onRefresh={getDataList} + onButtonClick={getDataList} /> )}
diff --git a/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx b/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx index 90ad4d15..1a0b9a18 100644 --- a/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx +++ b/react-ui/src/pages/DevelopmentEnvironment/Create/index.tsx @@ -36,13 +36,13 @@ enum ComputingResourceType { const EditorRadioItems: KFRadioItem[] = [ { - key: ComputingResourceType.GPU, title: '英伟达GPU', + value: ComputingResourceType.GPU, icon: , }, { - key: ComputingResourceType.NPU, title: '昇腾NPU', + value: ComputingResourceType.NPU, icon: , }, ]; diff --git a/react-ui/src/pages/Mirror/Create/index.tsx b/react-ui/src/pages/Mirror/Create/index.tsx index c4e89ce2..01a22031 100644 --- a/react-ui/src/pages/Mirror/Create/index.tsx +++ b/react-ui/src/pages/Mirror/Create/index.tsx @@ -30,13 +30,13 @@ type FormData = { const mirrorRadioItems: KFRadioItem[] = [ { - key: CommonTabKeys.Public, title: '基于公网镜像', + value: CommonTabKeys.Public, icon: , }, { - key: CommonTabKeys.Private, title: '本地上传', + value: CommonTabKeys.Private, icon: , }, ]; diff --git a/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx b/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx index 9cd4ebfb..b4cfb84a 100644 --- a/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx +++ b/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx @@ -401,7 +401,12 @@ function ServiceInfo() { image={require('@/assets/img/mirror-basic.png')} style={{ marginBottom: '26px', flex: 'none' }} > - + ; + return ; } export default VersionBasicInfo; diff --git a/react-ui/src/components/RobotFrame/index.less b/react-ui/src/pages/Workspace/components/RobotFrame/index.less similarity index 100% rename from react-ui/src/components/RobotFrame/index.less rename to react-ui/src/pages/Workspace/components/RobotFrame/index.less diff --git a/react-ui/src/components/RobotFrame/index.tsx b/react-ui/src/pages/Workspace/components/RobotFrame/index.tsx similarity index 100% rename from react-ui/src/components/RobotFrame/index.tsx rename to react-ui/src/pages/Workspace/components/RobotFrame/index.tsx diff --git a/react-ui/src/pages/Workspace/index.tsx b/react-ui/src/pages/Workspace/index.tsx index bdef4f83..691ca550 100644 --- a/react-ui/src/pages/Workspace/index.tsx +++ b/react-ui/src/pages/Workspace/index.tsx @@ -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'; diff --git a/react-ui/src/pages/missingPage.jsx b/react-ui/src/pages/missingPage.jsx index e8e034a4..9b3e0323 100644 --- a/react-ui/src/pages/missingPage.jsx +++ b/react-ui/src/pages/missingPage.jsx @@ -12,7 +12,7 @@ const MissingPage = () => { content={'很抱歉,您访问的正在开发中,\n请耐心等待。'} hasFooter={true} buttonTitle="返回首页" - onRefresh={() => navigate('/')} + onButtonClick={() => navigate('/')} > ); }; diff --git a/react-ui/src/stories/BasicInfo.stories.tsx b/react-ui/src/stories/BasicInfo.stories.tsx index f015096c..e6e71255 100644 --- a/react-ui/src/stories/BasicInfo.stories.tsx +++ b/react-ui/src/stories/BasicInfo.stories.tsx @@ -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: ( - ), diff --git a/react-ui/src/stories/BasicTableInfo.stories.tsx b/react-ui/src/stories/BasicTableInfo.stories.tsx index 36eb13ae..3ed5f332 100644 --- a/react-ui/src/stories/BasicTableInfo.stories.tsx +++ b/react-ui/src/stories/BasicTableInfo.stories.tsx @@ -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; // 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: ( -
- -
- ), - }, - ], + ...BasicInfoStories.Primary.args, labelWidth: 70, }, }; diff --git a/react-ui/src/stories/FullScreenFrame.stories.tsx b/react-ui/src/stories/FullScreenFrame.stories.tsx new file mode 100644 index 00000000..6a2748c2 --- /dev/null +++ b/react-ui/src/stories/FullScreenFrame.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// 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' }, + }, +}; diff --git a/react-ui/src/stories/InfoGroup.stories.tsx b/react-ui/src/stories/InfoGroup.stories.tsx new file mode 100644 index 00000000..f3a6def8 --- /dev/null +++ b/react-ui/src/stories/InfoGroup.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Primary: Story = { + args: { + title: '基本信息', + children:
I am a child element
, + }, +}; diff --git a/react-ui/src/stories/InfoGroupTitle.stories.tsx b/react-ui/src/stories/InfoGroupTitle.stories.tsx new file mode 100644 index 00000000..c37ff07e --- /dev/null +++ b/react-ui/src/stories/InfoGroupTitle.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Primary: Story = { + args: { + title: '基本信息', + }, +}; diff --git a/react-ui/src/stories/KFEmpty.stories.tsx b/react-ui/src/stories/KFEmpty.stories.tsx new file mode 100644 index 00000000..f26de4ee --- /dev/null +++ b/react-ui/src/stories/KFEmpty.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// 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: '返回首页', + }, +}; diff --git a/react-ui/src/stories/KFIcon.stories.tsx b/react-ui/src/stories/KFIcon.stories.tsx new file mode 100644 index 00000000..44cb274e --- /dev/null +++ b/react-ui/src/stories/KFIcon.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// 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 ( + + ); + }, +}; diff --git a/react-ui/src/stories/KFModal.stories.tsx b/react-ui/src/stories/KFModal.stories.tsx new file mode 100644 index 00000000..ebebe0b3 --- /dev/null +++ b/react-ui/src/stories/KFModal.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// 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 ( + <> + + + + ); + }, +}; diff --git a/react-ui/src/stories/KFRadio.stories.tsx b/react-ui/src/stories/KFRadio.stories.tsx new file mode 100644 index 00000000..c48c16e9 --- /dev/null +++ b/react-ui/src/stories/KFRadio.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// 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: , + }, + { + title: '昇腾NPU', + value: 'NPU', + icon: , + }, + ], + value: 'GPU', + }, + render: function Render(args) { + const [{ value }, updateArgs] = useArgs(); + function onChange(value: string) { + updateArgs({ value: value }); + } + + return ; + }, +}; diff --git a/react-ui/src/stories/KFSpin.stories.tsx b/react-ui/src/stories/KFSpin.stories.tsx new file mode 100644 index 00000000..7f3185d2 --- /dev/null +++ b/react-ui/src/stories/KFSpin.stories.tsx @@ -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) => ( +
+ +
+ ), + ], + // 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; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Primary: Story = { + args: {}, +}; diff --git a/react-ui/src/stories/MenuIconSelector.stories.tsx b/react-ui/src/stories/MenuIconSelector.stories.tsx new file mode 100644 index 00000000..45088835 --- /dev/null +++ b/react-ui/src/stories/MenuIconSelector.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// 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 ( + <> + + + + ); + }, +}; diff --git a/react-ui/src/stories/PageTitle.stories.tsx b/react-ui/src/stories/PageTitle.stories.tsx new file mode 100644 index 00000000..216591b1 --- /dev/null +++ b/react-ui/src/stories/PageTitle.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Primary: Story = { + args: { + title: '数据集列表', + }, +}; diff --git a/react-ui/src/stories/SubAreaTitle.stories.tsx b/react-ui/src/stories/SubAreaTitle.stories.tsx new file mode 100644 index 00000000..9fad7d71 --- /dev/null +++ b/react-ui/src/stories/SubAreaTitle.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Primary: Story = { + args: { + title: '基本信息', + image: MirrorBasic, + }, +};