Merge remote-tracking branch 'origin/dev-opt' into dev-opt

This commit is contained in:
cyc 2026-04-03 16:00:45 +08:00
commit de4dcf33e9
14 changed files with 250 additions and 100 deletions

View File

@ -82,6 +82,7 @@
"echarts": "^5.5.0", "echarts": "^5.5.0",
"echarts-gl": "~2.0.9", "echarts-gl": "~2.0.9",
"fabric": "^5.3.0", "fabric": "^5.3.0",
"gray-matter": "~4.0.3",
"highlight.js": "^11.7.0", "highlight.js": "^11.7.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"motion": "~12.23.12", "motion": "~12.23.12",
@ -122,7 +123,7 @@
"@testing-library/react": "^14.0.0", "@testing-library/react": "^14.0.0",
"@types/antd": "^1.0.0", "@types/antd": "^1.0.0",
"@types/express": "^4.17.14", "@types/express": "^4.17.14",
"@types/jest": "^29.5.1", "@types/jest": "~29.5.14",
"@types/lodash": "^4.14.194", "@types/lodash": "^4.14.194",
"@types/react": "^18.0.38", "@types/react": "^18.0.38",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",

View File

@ -92,11 +92,5 @@
&__content { &__content {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
:global {
.wmde-markdown {
border-radius: 8px !important;
}
}
} }
} }

View File

@ -1,6 +1,7 @@
import KFIcon from '@/components/KFIcon'; import KFIcon from '@/components/KFIcon';
import KFMdEditor from '@/components/KFMdEditor'; import KFMdEditor from '@/components/KFMdEditor';
import DownloadSkillModal from '@/pages/Skill/components/DownloadModal'; import DownloadSkillModal from '@/pages/Skill/components/DownloadModal';
import FrontmatterCard from '@/pages/Skill/components/FrontmatterCard';
import { SkillData } from '@/pages/Skill/types'; import { SkillData } from '@/pages/Skill/types';
import { import {
cancelPraiseSkillReq, cancelPraiseSkillReq,
@ -16,7 +17,9 @@ import { to } from '@/utils/promise';
import { useModel, useParams } from '@umijs/max'; import { useModel, useParams } from '@umijs/max';
import { Button, Flex, message } from 'antd'; import { Button, Flex, message } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import matter from 'gray-matter';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import AddSkillModal from '../components/AddSkillModal';
import styles from './index.less'; import styles from './index.less';
function SkillInfo() { function SkillInfo() {
@ -63,35 +66,60 @@ function SkillInfo() {
} }
}, [id, info]); }, [id, info]);
// 处理下载 const downloadSkill = useCallback(async (url: string) => {
const handleDownload = useCallback(async () => {
if (info!.file_tree_json) {
const treeData = parseJsonText(info!.file_tree_json);
const { close } = openAntdModal(DownloadSkillModal, {
treeData,
onOk: () => {
close();
downloadSkill(info!.file_path);
},
});
} else {
downloadSkill(info!.file_path);
}
}, [info]);
const downloadSkill = async (url: string) => {
if (url) { if (url) {
const [res] = await to(downloadSkillReq(url)); const [res] = await to(downloadSkillReq(url));
if (res && res.data) { if (res && res.data) {
downloadFileWithUrl(res.data); downloadFileWithUrl(res.data);
} }
} }
}, []);
// 处理下载
const handleDownload = useCallback(async () => {
const addLeaf = (treeData: any[]) => {
treeData.forEach((item) => {
item.isLeaf = !item.children;
if (item.children) {
addLeaf(item.children);
}
});
};
if (info!.file_tree_json) {
const treeData = parseJsonText(info!.file_tree_json);
if (Array.isArray(treeData) && treeData.length > 0) {
addLeaf(treeData);
const { close } = openAntdModal(DownloadSkillModal, {
treeData,
onOk: () => {
close();
downloadSkill(info!.file_path);
},
});
return;
}
}
downloadSkill(info!.file_path);
}, [info, downloadSkill]);
const handleEdit = () => {
const { close } = openAntdModal(AddSkillModal, {
id: id as string,
onOk: () => {
close();
getSkillInfo();
},
});
}; };
if (!info) { if (!info) {
return null; return null;
} }
const { data: frontmatter, content: mdContent } = matter(info!.md_content || '');
return ( return (
<div className={styles['skill-info']}> <div className={styles['skill-info']}>
<div className={styles['skill-info__top']}> <div className={styles['skill-info__top']}>
@ -120,17 +148,32 @@ function SkillInfo() {
</Button> </Button>
)} )}
<Button color="default" variant="filled" onClick={handleDownload}> {isMine && (
<Button
color="default"
variant="filled"
className={styles['skill-info__top__btn']}
onClick={handleEdit}
>
</Button>
)}
<Button
color="default"
variant="filled"
className={styles['skill-info__top__btn']}
onClick={handleDownload}
>
</Button> </Button>
<div <div
className={classNames(styles['skill-info__top__praise-btn'], { className={classNames(styles['skill-info__top__praise-btn'], {
[styles['skill-info__top__praise-btn--praised']]: info?.prraised, [styles['skill-info__top__praise-btn--praised']]: info?.praised,
})} })}
onClick={handlePraise} onClick={handlePraise}
> >
<KFIcon <KFIcon
type={info?.prraised ? 'icon-a-dianzanicon1' : 'icon-dianzanicon'} type={info?.praised ? 'icon-a-dianzanicon1' : 'icon-dianzanicon'}
font={16} font={16}
style={{ marginRight: '6px' }} style={{ marginRight: '6px' }}
/> />
@ -140,9 +183,10 @@ function SkillInfo() {
</Flex> </Flex>
<div className={styles['skill-info__top__desc']}>{info?.description || ''}</div> <div className={styles['skill-info__top__desc']}>{info?.description || ''}</div>
</div> </div>
<FrontmatterCard data={frontmatter} />
<KFMdEditor <KFMdEditor
className={styles['skill-info__content']} className={styles['skill-info__content']}
defaultValue={info?.md_content || ''} defaultValue={mdContent || ''}
canEdit={false} canEdit={false}
noDataTitle={'没有内容'} noDataTitle={'没有内容'}
placeholder="请输入" placeholder="请输入"

View File

@ -1,7 +1,7 @@
import { CommonTabKeys } from '@/enums'; import { CommonTabKeys } from '@/enums';
import { useCacheState } from '@/hooks/useCacheState'; import { useCacheState } from '@/hooks/useCacheState';
import { SkillCategoryData } from '@/pages/Skill/types'; import { SkillCategoryData } from '@/pages/Skill/types';
import { getSkillTypesReq } from '@/services/skill/index'; import { getAssetIcon } from '@/services/dataset/index.js';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
import { type TabsProps } from 'antd'; import { type TabsProps } from 'antd';
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
@ -9,11 +9,13 @@ import CategoryList from '../components/CategoryList';
import ResourceList, { ResourceListRef } from '../components/ResourceList'; import ResourceList, { ResourceListRef } from '../components/ResourceList';
import styles from './index.less'; import styles from './index.less';
export const ALL_TYPE = '全部分类';
function SkillPage() { function SkillPage() {
const [cacheState, setCacheState] = useCacheState(); const [cacheState, setCacheState] = useCacheState();
const [activeTab, setActiveTab] = useState<string>(cacheState?.activeTab ?? CommonTabKeys.Public); const [activeTab, setActiveTab] = useState<string>(cacheState?.activeTab ?? CommonTabKeys.Public);
const [typeList, setTypeList] = useState<SkillCategoryData[]>([]); const [typeList, setTypeList] = useState<SkillCategoryData[]>([]);
const [activeTypes, setActiveTypes] = useState<string[]>(cacheState?.activeType ?? []); const [activeType, setActiveType] = useState<string | undefined>(cacheState?.activeType);
const dataListRef = useRef<ResourceListRef>(null); const dataListRef = useRef<ResourceListRef>(null);
const [typeSearchText, setTypeSearchText] = useState<string>(''); const [typeSearchText, setTypeSearchText] = useState<string>('');
@ -23,10 +25,11 @@ function SkillPage() {
name: typeSearchText, name: typeSearchText,
page: 0, page: 0,
size: 10000, size: 10000,
category_id: 3,
}; };
const [res] = await to(getSkillTypesReq(params)); const [res] = await to(getAssetIcon(params));
if (res && res.data) { if (res && res.data) {
setTypeList(res.data); setTypeList([{ id: '', name: ALL_TYPE, path: 'icon-quanbuyuyan' }, ...res.data]);
} }
}, [typeSearchText]); }, [typeSearchText]);
@ -42,14 +45,7 @@ function SkillPage() {
// 选择类型 // 选择类型
const chooseType = (type: string) => { const chooseType = (type: string) => {
dataListRef.current?.resetPage(); dataListRef.current?.resetPage();
setActiveTypes([type]); setActiveType(type);
// setActiveTypes((prev) => {
// if (prev.includes(type)) {
// return prev.filter((v) => v !== type);
// } else {
// return [...prev, type];
// }
// });
}; };
// 切换 Tab重置数据 // 切换 Tab重置数据
@ -63,7 +59,7 @@ function SkillPage() {
<div className={styles['skill-page']}> <div className={styles['skill-page']}>
<CategoryList <CategoryList
typeList={typeList} typeList={typeList}
activeTypes={activeTypes} activeType={activeType}
onTypeSelect={chooseType} onTypeSelect={chooseType}
currentTab={activeTab} currentTab={activeTab}
tabItems={[ tabItems={[
@ -81,9 +77,8 @@ function SkillPage() {
/> />
<ResourceList <ResourceList
ref={dataListRef} ref={dataListRef}
skillTypes={typeList}
isPublic={isPublic} isPublic={isPublic}
dataTypes={activeTypes} dataType={activeType}
initialSearchText={cacheState?.searchText} initialSearchText={cacheState?.searchText}
initialPagination={cacheState?.pagination} initialPagination={cacheState?.pagination}
setCacheState={setCacheState} setCacheState={setCacheState}

View File

@ -3,7 +3,8 @@ import ImagePicker, { ImagePickerServerType } from '@/components/ImagePicker';
import KFModal from '@/components/KFModal'; import KFModal from '@/components/KFModal';
import UploadBlock from '@/components/UploadBlock'; import UploadBlock from '@/components/UploadBlock';
import { SkillCategoryData } from '@/pages/Skill/types'; import { SkillCategoryData } from '@/pages/Skill/types';
import { SkillUploadUrl, createSkillReq } from '@/services/skill'; import { getAssetIcon } from '@/services/dataset/index.js';
import { SkillUploadUrl, createSkillReq, getSkillInfoReq, updateSkillReq } from '@/services/skill';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
import { getFileListFromEvent, limitUploadFileType, validateUploadFiles } from '@/utils/ui'; import { getFileListFromEvent, limitUploadFileType, validateUploadFiles } from '@/utils/ui';
import { import {
@ -17,17 +18,17 @@ import {
type UploadProps, type UploadProps,
} from 'antd'; } from 'antd';
import { omit } from 'lodash'; import { omit } from 'lodash';
import { useState } from 'react'; import { useEffect, useState } from 'react';
interface AddVersionModalProps extends Omit<ModalProps, 'onOk'> { interface AddVersionModalProps extends Omit<ModalProps, 'onOk'> {
skillTypes: SkillCategoryData[]; id?: number | string;
onOk: () => void; onOk: () => void;
} }
function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) { function AddSkillModal({ id, onOk, ...rest }: AddVersionModalProps) {
const [uuid] = useState(Date.now()); const [uuid] = useState(Date.now());
const [form] = Form.useForm(); const [form] = Form.useForm();
// const [sliptUploadRequest, cancelUpload] = useSplitUpload(); const [typeList, setTypeList] = useState<SkillCategoryData[]>([]);
// 上传组件参数 // 上传组件参数
const uploadProps: UploadProps = { const uploadProps: UploadProps = {
@ -45,11 +46,46 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
maxCount: 1, maxCount: 1,
}; };
// 获取分类
useEffect(() => {
const getSkillTypes = async () => {
const params = {
page: 0,
size: 10000,
category_id: 3,
};
const [res] = await to(getAssetIcon(params));
if (res && res.data) {
setTypeList(res.data);
}
};
getSkillTypes();
}, []);
// 获取 Skill 详情
useEffect(() => {
const getSkillInfo = async (id: number | string) => {
const [res] = await to(getSkillInfoReq(id));
if (res && res.data) {
form.setFieldsValue(res.data);
}
};
if (id) {
getSkillInfo(id);
}
}, [id, form]);
// 上传请求 // 上传请求
const createDatasetVersion = async (params: any) => { const createDatasetVersion = async (params: any) => {
const [res] = await to(createSkillReq(params)); if (id) {
params.id = id;
}
const request = id ? updateSkillReq : createSkillReq;
const [res] = await to(request(params));
if (res) { if (res) {
message.success('创建成功'); message.success(id ? '编辑成功' : '创建成功');
onOk?.(); onOk?.();
} }
}; };
@ -57,20 +93,24 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
// 提交 // 提交
const onFinish = (formData: any) => { const onFinish = (formData: any) => {
const fileList: UploadFile[] = formData['fileList'] ?? []; const fileList: UploadFile[] = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) { if (id) {
const data = fileList[0]?.response?.data ?? {}; createDatasetVersion(formData);
const params = { } else {
...omit(formData, 'fileList'), if (validateUploadFiles(fileList)) {
...data, const data = fileList[0]?.response?.data ?? {};
}; const params = {
createDatasetVersion(params); ...omit(formData, 'fileList'),
...data,
};
createDatasetVersion(params);
}
} }
}; };
return ( return (
<KFModal <KFModal
{...rest} {...rest}
title="创建 Skill" title={id ? '编辑 Skill' : '创建 Skill'}
width={608} width={608}
okButtonProps={{ okButtonProps={{
htmlType: 'submit', htmlType: 'submit',
@ -99,7 +139,7 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
}, },
]} ]}
> >
<Input placeholder={`请输入 Skill 名称`} /> <Input placeholder={`请输入 Skill 名称`} disabled={id !== undefined} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@ -133,7 +173,7 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
> >
<Select <Select
placeholder="请选择 Skill 类型" placeholder="请选择 Skill 类型"
options={skillTypes} options={typeList}
fieldNames={{ label: 'name', value: 'name' }} fieldNames={{ label: 'name', value: 'name' }}
optionFilterProp="name" optionFilterProp="name"
showSearch showSearch
@ -153,22 +193,24 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
<ImagePicker serverType={ImagePickerServerType.Skill} uuid={uuid}></ImagePicker> <ImagePicker serverType={ImagePickerServerType.Skill} uuid={uuid}></ImagePicker>
</Form.Item> </Form.Item>
<Form.Item {!id && (
label="Skill 文件" <Form.Item
name="fileList" label="Skill 文件"
valuePropName="fileList" name="fileList"
getValueFromEvent={getFileListFromEvent} valuePropName="fileList"
rules={[ getValueFromEvent={getFileListFromEvent}
{ rules={[
required: true, {
message: '请上传 Skill 文件', required: true,
}, message: '请上传 Skill 文件',
]} },
> ]}
<Upload {...uploadProps} data={{ uuid: uuid }} className={'common-upload-component'}> >
<UploadBlock desc={'请上传 SKILL.md 或包含 SKILL.md 的 .zip 文件'}></UploadBlock> <Upload {...uploadProps} data={{ uuid: uuid }} className={'common-upload-component'}>
</Upload> <UploadBlock desc={'请上传 SKILL.md 或包含 SKILL.md 的 .zip 文件'}></UploadBlock>
</Form.Item> </Upload>
</Form.Item>
)}
</Form> </Form>
</KFModal> </KFModal>
); );

View File

@ -48,6 +48,7 @@
color: @text-color; color: @text-color;
font-size: 15px; font-size: 15px;
border-radius: 8px; border-radius: 8px;
cursor: pointer;
&:hover { &:hover {
background-color: rgba(81, 76, 249, 0.1); background-color: rgba(81, 76, 249, 0.1);

View File

@ -9,7 +9,7 @@ import styles from './index.less';
type CategoryProps = { type CategoryProps = {
typeList: SkillCategoryData[]; typeList: SkillCategoryData[];
activeTypes: string[]; activeType: string | undefined;
tabItems: KFRadioItem[]; tabItems: KFRadioItem[];
currentTab: string; currentTab: string;
onTabChange: (value: string) => void; onTabChange: (value: string) => void;
@ -19,7 +19,7 @@ type CategoryProps = {
function CategoryList({ function CategoryList({
typeList, typeList,
activeTypes, activeType,
tabItems, tabItems,
currentTab, currentTab,
onTabChange, onTabChange,
@ -53,11 +53,15 @@ function CategoryList({
key={item.id} key={item.id}
align="center" align="center"
className={classNames(styles['category-list__content__title'], { className={classNames(styles['category-list__content__title'], {
[styles['category-list__content__title--active']]: activeTypes.includes(item.name), [styles['category-list__content__title--active']]: activeType === item.name,
})} })}
onClick={() => onTypeSelect(item.name)} onClick={() => {
if (item.name !== activeType) {
onTypeSelect(item.name);
}
}}
> >
<KFIcon type={'icon-diancuihuacailiao'} width={16} height={16} /> <KFIcon type={item.path} width={16} height={16} />
<div style={{ marginLeft: 8 }}>{item.name}</div> <div style={{ marginLeft: 8 }}>{item.name}</div>
</Flex> </Flex>
))} ))}

View File

@ -26,7 +26,12 @@ function DownloadSkillModal({ treeData, onOk, ...rest }: AddVersionModalProps) {
> >
<DirectoryTree <DirectoryTree
treeData={treeData} treeData={treeData}
defaultExpandAll={true} fieldNames={{
title: 'name',
key: 'name',
}}
defaultExpandedKeys={[treeData[0]?.name || '']}
selectable={false}
titleRender={(record: DownloadTreeDataNode) => { titleRender={(record: DownloadTreeDataNode) => {
const label = record.name + (record.fileSize ? `${record.fileSize}` : ''); const label = record.name + (record.fileSize ? `${record.fileSize}` : '');
return <span style={{ fontSize: 14 }}>{label}</span>; return <span style={{ fontSize: 14 }}>{label}</span>;

View File

@ -0,0 +1,22 @@
.frontmatter-card {
padding: 12px;
background-color: @background-color;
font-size: @font-size-input;
color: @text-color;
display: flex;
flex-direction: column;
gap: 10px;
border-radius: 8px 8px 0 0;
border-bottom: 1px solid hsl(210, 18%, 87%);
;
&__title {
flex: none;
width: 80px;
}
&__value {
flex: 1;
font-weight: bold;
}
}

View File

@ -0,0 +1,27 @@
import { Flex } from 'antd';
import styles from './index.less';
type FrontmatterCardProps = {
data: Record<string, string>;
};
function FrontmatterCard({ data }: FrontmatterCardProps) {
const { name, description } = data;
if (!name && !description) {
return null;
}
return (
<div className={styles['frontmatter-card']}>
<Flex align="flex-start">
<span className={styles['frontmatter-card__title']}>name: </span>
<span className={styles['frontmatter-card__value']}>{name}</span>
</Flex>
<Flex align="flex-start">
<span className={styles['frontmatter-card__title']}>description: </span>
<span className={styles['frontmatter-card__value']}>{description}</span>
</Flex>
</div>
);
}
export default FrontmatterCard;

View File

@ -1,7 +1,8 @@
import KFEmpty, { EmptyType } from '@/components/KFEmpty'; import KFEmpty, { EmptyType } from '@/components/KFEmpty';
import KFSearch from '@/components/KFSearch'; import KFSearch from '@/components/KFSearch';
import { CommonTabKeys } from '@/enums'; import { CommonTabKeys } from '@/enums';
import { SkillCategoryData, SkillData } from '@/pages/Skill/types'; import { ALL_TYPE } from '@/pages/Skill/List';
import { SkillData } from '@/pages/Skill/types';
import { deleteSkillReq, getSkillListReq } from '@/services/skill'; import { deleteSkillReq, getSkillListReq } from '@/services/skill';
import { openAntdModal } from '@/utils/modal'; import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
@ -12,14 +13,14 @@ import { Ref, forwardRef, useCallback, useEffect, useImperativeHandle, useState
import AddSkillModal from '../AddSkillModal'; import AddSkillModal from '../AddSkillModal';
import ResourceItem from '../ResourceItem'; import ResourceItem from '../ResourceItem';
import styles from './index.less'; import styles from './index.less';
export type ResourceListRef = { export type ResourceListRef = {
reset: () => void; reset: () => void;
resetPage: () => void; resetPage: () => void;
}; };
type ResourceListProps = { type ResourceListProps = {
skillTypes?: SkillCategoryData[]; dataType?: string | undefined;
dataTypes?: string[];
isPublic: boolean; isPublic: boolean;
initialSearchText?: string; initialSearchText?: string;
initialPagination?: PaginationProps; initialPagination?: PaginationProps;
@ -27,14 +28,7 @@ type ResourceListProps = {
}; };
function ResourceList( function ResourceList(
{ { dataType, isPublic, initialSearchText, initialPagination, setCacheState }: ResourceListProps,
skillTypes,
dataTypes,
isPublic,
initialSearchText,
initialPagination,
setCacheState,
}: ResourceListProps,
ref: Ref<ResourceListRef>, ref: Ref<ResourceListRef>,
) { ) {
const navigate = useNavigate(); const navigate = useNavigate();
@ -57,7 +51,7 @@ function ResourceList(
size: pagination.pageSize, size: pagination.pageSize,
is_public: isPublic, is_public: isPublic,
name: searchText || undefined, name: searchText || undefined,
is_hot_stone: true, type: dataType === ALL_TYPE ? undefined : dataType,
}; };
const [res] = await to(getSkillListReq(params)); const [res] = await to(getSkillListReq(params));
if (res && res.data && res.data.content) { if (res && res.data && res.data.content) {
@ -67,7 +61,7 @@ function ResourceList(
setDataList([]); setDataList([]);
setTotal(0); setTotal(0);
} }
}, [pagination, searchText, isPublic]); }, [pagination, searchText, isPublic, dataType]);
useEffect(() => { useEffect(() => {
getDataList(); getDataList();
@ -142,7 +136,7 @@ function ResourceList(
activeTab: isPublic ? CommonTabKeys.Public : CommonTabKeys.Private, activeTab: isPublic ? CommonTabKeys.Public : CommonTabKeys.Private,
pagination, pagination,
searchText, searchText,
activeType: dataTypes, activeType: dataType,
}); });
navigate(`/dataset/skills/info/${record.id}`); navigate(`/dataset/skills/info/${record.id}`);
}; };
@ -158,7 +152,6 @@ function ResourceList(
// 新建弹框 // 新建弹框
const showModal = () => { const showModal = () => {
const { close } = openAntdModal(AddSkillModal, { const { close } = openAntdModal(AddSkillModal, {
skillTypes: skillTypes || [],
onOk: () => { onOk: () => {
close(); close();
getDataList(); getDataList();

View File

@ -1,6 +1,7 @@
export type SkillCategoryData = { export type SkillCategoryData = {
id: string; id: string;
name: string; name: string;
path: string;
}; };
export type SkillData = { export type SkillData = {
@ -14,10 +15,8 @@ export type SkillData = {
description: string; description: string;
type: string; type: string;
md_content: string; md_content: string;
prraised: boolean;
prraises_count: number;
is_public: boolean;
praised: boolean; praised: boolean;
is_public: boolean;
file_tree_json: string; file_tree_json: string;
file_path: string; file_path: string;
}; };

View File

@ -153,6 +153,27 @@ function ApprovalModal({ record, onOk, ...rest }: ApprovalModalProps) {
}, },
]; ];
break; break;
case ApprovalType.SKILL:
items = [
{
label: 'Skill 名称',
value: content.name,
},
{
label: 'Skill 描述',
value: content.description,
},
{
label: 'Skill 类型',
value: content.type,
},
{
label: '创建时间',
value: content.createTime,
format: formatDate,
},
];
break;
default: default:
items = []; items = [];
break; break;

View File

@ -47,6 +47,7 @@ export enum ApprovalType {
IMAGE = 'IMAGE', IMAGE = 'IMAGE',
CODE = 'CODE', CODE = 'CODE',
SERVICE = 'SERVICE', SERVICE = 'SERVICE',
SKILL = 'SKILL',
} }
const approvalTypeOptions = [ const approvalTypeOptions = [
@ -55,6 +56,7 @@ const approvalTypeOptions = [
{ label: '镜像', value: ApprovalType.IMAGE }, { label: '镜像', value: ApprovalType.IMAGE },
{ label: '代码配置', value: ApprovalType.CODE }, { label: '代码配置', value: ApprovalType.CODE },
{ label: '应用', value: ApprovalType.SERVICE }, { label: '应用', value: ApprovalType.SERVICE },
{ label: 'Skill', value: ApprovalType.SKILL },
]; ];
const statusOptions = [{ label: '全部', value: '' }, ...approvalStatusOptions]; const statusOptions = [{ label: '全部', value: '' }, ...approvalStatusOptions];