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

This commit is contained in:
cyc 2026-04-01 14:56:59 +08:00
commit 2c123e04d1
11 changed files with 211 additions and 377 deletions

View File

@ -47,330 +47,20 @@ Skill 是模块化的能力包,用于扩展 Claude 的功能。每个 Skill
`;
export default defineMock({
'GET /api/skill/types/': {
'GET /api/mmp/skills/types/': {
msg: '操作成功',
code: 200,
data: [
{
id: 1,
name: '营销',
icon: 'marketing',
},
{
id: 2,
name: '编程与开发',
icon: 'development',
},
],
},
'GET /api/skill/': {
msg: '操作成功',
code: 200,
data: {
content: [
{
id: 109,
name: '介电',
create_by: 'ceshi',
description: '介电材料模型',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:09:54.000+08:00',
owner: 'ceshi',
identifier: 'public_model_20250522110231',
is_public: true,
praises_count: 2,
},
{
id: 156,
name: 'ChatGLM2-6B',
create_by: 'fanshuai',
description:
'ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本,在保留了初代模型对话流畅、部署门槛较低等众多优秀特性的基础之上',
time_ago: '2个月前',
full_last_update_time: '2025-06-20T16:09:02.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528093916',
is_public: true,
praises_count: 1,
},
{
id: 155,
name: '鹏城·脑海(原鹏城·盘古)α-2.6B-CPU',
create_by: 'fanshuai',
description:
'「鹏城·盘古α」由以鹏城实验室为首的技术团队联合攻关首次基于“鹏城云脑Ⅱ”和国产MindSpore框架的自动混合并行模式实现在2048卡算力集群上的大规模分布式训练训练出业界首个2000亿参数以中文为核心的预训练生成语言模型。鹏城·盘古α预训练模型支持丰富的场景应用在知识问答、知识检索、知识推理、阅读理解等文本生成领域表现突出具备很强的小样本学习能力。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:03:36.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528093254',
is_public: true,
praises_count: 0,
model_type: '高强度,高韧性',
},
{
id: 157,
name: 'ernie-3.0-base-zh',
create_by: 'fanshuai',
description: '大规模知识增强预训练,用于语言理解和生成',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:03:08.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528094825',
is_public: true,
praises_count: 0,
model_type: '高强度,高韧性',
model_tag: '半导体,导体',
},
{
id: 158,
name: 'FastChat-T5',
create_by: 'fanshuai',
description:
'FastChat-T5是一款开源聊天机器人通过微调Flan-t5-xl (3B参数并基于从ShareGPT.收集的用户共享对话进行训练。它基于编码器-解码器变压器架构,能够自回归生成对用户输入的响应。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:03:19.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528101831',
is_public: true,
praises_count: 0,
model_type: '高强度',
model_tag: '半导体',
},
{
id: 159,
name: 'Kolors-IP-Adapter-Plus',
create_by: 'fanshuai',
description: '基于Kolors-Basemodel提供了IP-Adapter-Plus的权重和推理代码',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:03:01.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528102217',
is_public: true,
praises_count: 0,
},
{
id: 160,
name: 'Florence-2-base',
create_by: 'fanshuai',
description:
'Florence-2是一款先进的视觉基础模型采用提示式方法处理广泛的视觉和视觉-语言任务。Florence-2能够通过简单的文本提示来执行诸如字幕生成、物体检测和分割等任务。该模型利用了包含54亿个注释的FLD-5B数据集这些注释覆盖了1.26亿张图像,从而掌握了多任务学习。模型的序列到序列架构使其在零样本和微调设置中表现出色,证明了其作为竞争性视觉基础模型的实力。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:55.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528102750',
is_public: true,
praises_count: 0,
},
{
id: 161,
name: 'E5-base',
create_by: 'fanshuai',
description: '弱监督对比预训练的文本嵌入。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:48.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528103059',
is_public: true,
praises_count: 0,
},
{
id: 162,
name: 'Mini-InternVL-Chat',
create_by: 'fanshuai',
description:
'使用了与InternVL 1相同的数据来训练这个较小的模型。此外由于较小模型的训练成本较低我们在训练时采用了8K的上下文长度。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:39.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528105945',
is_public: true,
praises_count: 0,
},
{
id: 163,
name: 'Verdict-Classifier',
create_by: 'fanshuai',
description:
'该模型是基于xlm-roberta-base的微调版本基于谷歌事实核查工具API提供的2,500条去重多语言判决并通过谷歌云翻译API转换成65种语言',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:27.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528110545',
is_public: true,
praises_count: 0,
},
{
id: 164,
name: 'Text2Vec-Base-Multilingual',
create_by: 'fanshuai',
description:
'这是一个CoSENT余弦句子模型它将句子映射到一个384维的密集向量空间并可用于任务例如句子嵌入、文本匹配或语义搜索。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:19.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528110858',
is_public: true,
praises_count: 0,
},
{
id: 167,
name: 'Latex-OCR',
create_by: 'fanshuai',
description: '识别图像中的数学公式并转换为Latex源码。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:09.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528112153',
is_public: true,
praises_count: 0,
},
{
id: 169,
name: 'XLNet',
create_by: 'fanshuai',
description:
'XLNet是一种基于新型广义置换语言建模目标的新型无监督语言表示学习方法。此外XLNet采用Transformer-XL作为骨干模型在处理长上下文的语言任务中表现出色。总体而言XLNet在包括问答、自然语言推理、情感分析和文档排序在内的多种下游语言任务中取得了最先进的SOTA成果。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T17:29:05.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528172905',
is_public: true,
praises_count: 0,
},
{
id: 173,
name: ' GTE-base',
create_by: 'fanshuai',
description:
'GTE模型由阿里巴巴达摩学院训练。这些模型主要基于BERT框架目前提供三种不同规模的版本分别是GTE-large、GTE-base和GTE-small。GTE模型在大规模的相关文本对语料库上进行训练涵盖了广泛的领域和场景。这使得GTE模型能够应用于文本嵌入的多种下游任务如信息检索、语义文本相似性分析、文本重排序等。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:14:15.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529091415',
is_public: true,
praises_count: 0,
},
{
id: 174,
name: 'Tiny-lm',
create_by: 'fanshuai',
description:
'此仓库提供了一个小型的1600万参数语言模型该模型基于英文和日文维基百科数据训练。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:18:57.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529091857',
is_public: true,
praises_count: 0,
},
{
id: 175,
name: "Snowflake's Arctic-embed-s",
create_by: 'fanshuai',
description:
'snowflake-arctic-embed是一套文本嵌入模型专注于创建高性能的高质量检索模型。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:23:58.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529092358',
is_public: true,
praises_count: 0,
},
{
id: 176,
name: 'ViTMatte model',
create_by: 'fanshuai',
description:
'ViTMatte是一种简单的图像抠图方法旨在准确估计图像中的前景物体。该模型由一个Vision TransformerViT和一个轻量级头部组成。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:41:07.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529094107',
is_public: true,
praises_count: 0,
},
{
id: 177,
name: 'Wartortle',
create_by: 'fanshuai',
description: '此模型专为语义自动补全功能而设计。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:44:43.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529094443',
is_public: true,
praises_count: 0,
},
{
id: 179,
name: 'Cerebras-GPT',
create_by: 'fanshuai',
description:
'Cerebras-GPT系列的发布旨在通过开放架构和数据集促进对大型语言模型LLM扩展规律的研究并展示在Cerebras软硬件栈上训练LLM的简便性和可扩展性。所有Cerebras-GPT模型均可在Hugging Face.上获取。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T10:01:52.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529100152',
is_public: true,
praises_count: 0,
},
{
id: 180,
name: 'Qwen2-1.5B-Instruct-AWQ',
create_by: 'fanshuai',
description:
'Qwen2是Qwen大语言模型系列的最新成员。我们为Qwen2推出了多个基础语言模型和指令调优语言模型参数规模从0.5亿到72亿不等其中包括一个专家混合模型。本仓库包含1.5亿参数的指令调优Qwen2模型。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T10:13:53.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529101353',
is_public: true,
praises_count: 0,
},
],
pageable: {
sort: {
sorted: false,
unsorted: true,
empty: true,
},
pageNumber: 0,
pageSize: 20,
offset: 0,
unpaged: false,
paged: true,
},
last: false,
totalElements: 28,
totalPages: 2,
first: true,
number: 0,
sort: {
sorted: false,
unsorted: true,
empty: true,
},
numberOfElements: 20,
size: 20,
empty: false,
},
},
'GET /api/skill/:id': {
msg: '操作成功',
code: 200,
data: {
id: 109,
name: 'Claude Code Skill',
create_by: 'ceshi',
description: `Claude Skill 是模块化能力包,通过三级加载机制(元数据→指令→资源)高效扩展 Claude 功能。创建流程包含 7 步:理解需求、规划资源、初始化目录、编写 SKILL.md、添加资源、验证打包、迭代改进。核心要素包括精准的 description 触发条件、精简的 SKILL.md<5k tokens、合理的资源组织scripts/、references/、assets/)、具体使用示例。关键原则:使用祈使句而非第二人称、保持信息渐进式披露、脚本独立可执行、避免内容重复。遵循规范可创建专业、高效、易维护的 Skill。`,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:09:54.000+08:00',
owner: 'ceshi',
identifier: 'public_model_20250522110231',
is_public: true,
praises_count: 3,
type: '编程',
content: md,
},
},
});

View File

@ -95,10 +95,8 @@
:global {
.wmde-markdown {
border-radius: 8px;
border-radius: 8px !important;
}
}
}
}

View File

@ -1,10 +1,20 @@
import KFIcon from '@/components/KFIcon';
import KFMdEditor from '@/components/KFMdEditor';
import DownloadSkillModal from '@/pages/Skill/components/DownloadModal';
import { SkillData } from '@/pages/Skill/types';
import { getSkillInfoReq } from '@/services/skill';
import {
cancelPraiseSkillReq,
downloadSkillReq,
getSkillInfoReq,
praiseSkillReq,
publishSkillReq,
} from '@/services/skill';
import { parseJsonText } from '@/utils';
import { downloadFileWithUrl } from '@/utils/downloadfile';
import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { useParams } from '@umijs/max';
import { Button, Flex } from 'antd';
import { useModel, useParams } from '@umijs/max';
import { Button, Flex, message } from 'antd';
import classNames from 'classnames';
import { useCallback, useEffect, useState } from 'react';
import styles from './index.less';
@ -12,6 +22,9 @@ import styles from './index.less';
function SkillInfo() {
const { id } = useParams();
const [info, setInfo] = useState<SkillData>();
const { initialState } = useModel('@@initialState');
const { currentUser } = initialState || {};
const isMine = currentUser?.userName === info?.create_by;
const getSkillInfo = useCallback(async () => {
const [res] = await to(getSkillInfoReq(id as string));
@ -24,6 +37,61 @@ function SkillInfo() {
getSkillInfo();
}, [getSkillInfo]);
// 发布技能
const handlePublish = useCallback(async () => {
const [res] = await to(publishSkillReq(id as string));
if (res && res.data) {
if (res.data.is_public) {
message.success('发布成功');
setInfo((prev) => ({
...prev!,
is_public: true,
}));
} else {
message.success('操作成功,等待管理员审核');
}
}
}, [id]);
// 处理点赞
const handlePraise = useCallback(async () => {
const request = info!.praised === true ? cancelPraiseSkillReq : praiseSkillReq;
const [res] = await to(request(id as string));
if (res && res.data) {
message.success('操作成功');
setInfo(res.data);
}
}, [id, info]);
// 处理下载
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) {
const [res] = await to(downloadSkillReq(url));
if (res && res.data) {
downloadFileWithUrl(res.data);
}
}
};
if (!info) {
return null;
}
return (
<div className={styles['skill-info']}>
<div className={styles['skill-info__top']}>
@ -37,38 +105,36 @@ function SkillInfo() {
<div className={styles['skill-info__top__name']}>{info?.name || ''}</div>
<div className={styles['skill-info__top__tag']}>{info?.type || ''}</div>
<Flex gap="20px" align="center" style={{ marginLeft: 'auto' }}>
<Button
type="primary"
icon={
<img
src={require('@/assets/img/app/publish-icon.png')}
draggable={false}
width={16}
></img>
}
// onClick={handlePublish}
>
</Button>
<Button
color="default"
variant="filled"
// onClick={handlePublish}
>
{info?.is_public === false && isMine && (
<Button
type="primary"
icon={
<img
src={require('@/assets/img/app/publish-icon.png')}
draggable={false}
width={16}
></img>
}
onClick={handlePublish}
>
</Button>
)}
<Button color="default" variant="filled" onClick={handleDownload}>
</Button>
<div
className={classNames(styles['skill-info__top__praise-btn'], {
[styles['skill-info__top__praise-btn--praised']]: info?.prraised,
})}
// onClick={handlePraise}
onClick={handlePraise}
>
<KFIcon
type={info?.prraised ? 'icon-a-dianzanicon1' : 'icon-dianzanicon'}
font={16}
style={{ marginRight: '6px' }}
/>
{info?.prraises_count || 0}
{info?.praises_count || 0}
</div>
</Flex>
</Flex>
@ -76,7 +142,7 @@ function SkillInfo() {
</div>
<KFMdEditor
className={styles['skill-info__content']}
defaultValue={info?.content || ''}
defaultValue={info?.md_content || ''}
canEdit={false}
noDataTitle={'没有内容'}
placeholder="请输入"

View File

@ -69,11 +69,11 @@ function SkillPage() {
tabItems={[
{
value: CommonTabKeys.Public,
label: '公共 Skill',
label: '公共 Skills',
},
{
value: CommonTabKeys.Private,
label: '个人 Skill',
label: '个人 Skills',
},
]}
onTabChange={hanleTabChange}

View File

@ -2,11 +2,10 @@ import { getAccessToken } from '@/access';
import ImagePicker, { ImagePickerServerType } from '@/components/ImagePicker';
import KFModal from '@/components/KFModal';
import UploadBlock from '@/components/UploadBlock';
import useSplitUpload from '@/hooks/useSplitUpload';
import { SkillCategoryData } from '@/pages/Skill/types';
import { createSkillReq } from '@/services/skill';
import { SkillUploadUrl, createSkillReq } from '@/services/skill';
import { to } from '@/utils/promise';
import { getFileListFromEvent, validateUploadFiles } from '@/utils/ui';
import { getFileListFromEvent, limitUploadFileType, validateUploadFiles } from '@/utils/ui';
import {
Form,
Input,
@ -28,7 +27,7 @@ interface AddVersionModalProps extends Omit<ModalProps, 'onOk'> {
function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
const [uuid] = useState(Date.now());
const [form] = Form.useForm();
const [sliptUploadRequest, cancelUpload] = useSplitUpload();
// const [sliptUploadRequest, cancelUpload] = useSplitUpload();
// 上传组件参数
const uploadProps: UploadProps = {
@ -36,10 +35,14 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
Authorization: getAccessToken() || '',
},
defaultFileList: [],
// beforeUpload: config.beforeUpload,
// accept: config.uploadAccept,
customRequest: sliptUploadRequest,
onRemove: cancelUpload,
action: SkillUploadUrl,
beforeUpload: limitUploadFileType({
extensions: '.md,.zip',
size: 100,
unit: 'MB',
}),
accept: '.md,.zip',
maxCount: 1,
};
// 上传请求
@ -55,17 +58,10 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
const onFinish = (formData: any) => {
const fileList: UploadFile[] = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const files = fileList.map((item) => {
const data = item.response?.data ?? {};
return {
file_name: data.filename,
file_size: data.totalSize,
url: data.location,
};
});
const data = fileList[0]?.response?.data ?? {};
const params = {
...omit(formData, 'fileList'),
files,
...data,
};
createDatasetVersion(params);
}
@ -108,7 +104,7 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
<Form.Item
label="Skill 描述"
name="version_desc"
name="description"
rules={[
{
required: true,
@ -138,7 +134,7 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
<Select
placeholder="请选择 Skill 类型"
options={skillTypes}
fieldNames={{ label: 'name', value: 'id' }}
fieldNames={{ label: 'name', value: 'name' }}
optionFilterProp="name"
showSearch
/>
@ -170,7 +166,7 @@ function AddSkillModal({ skillTypes, onOk, ...rest }: AddVersionModalProps) {
]}
>
<Upload {...uploadProps} data={{ uuid: uuid }} className={'common-upload-component'}>
<UploadBlock desc={'只允许上传 .md 或 .zip 格式文件'}></UploadBlock>
<UploadBlock desc={'请上传 SKILL.md 或包含 SKILL.md 的 .zip 文件'}></UploadBlock>
</Upload>
</Form.Item>
</Form>

View File

@ -1,15 +1,15 @@
.category-list {
display: flex;
flex-direction: column;
width: 350px;
width: 280px;
height: 100%;
padding-bottom: 72px;
@media screen and (max-width: 1600px) {
& {
width: 240px;
}
}
// @media screen and (max-width: 1600px) {
// & {
// width: 260px;
// }
// }
&__search {
width: 100%;

View File

@ -0,0 +1,7 @@
.download-modal {
:global {
.ant-tree-directory {
background-color: transparent;
}
}
}

View File

@ -0,0 +1,39 @@
import KFModal from '@/components/KFModal';
import { Tree, type ModalProps, type TreeDataNode } from 'antd';
import styles from './index.less';
const { DirectoryTree } = Tree;
interface DownloadTreeDataNode extends TreeDataNode {
name: string;
fileSize?: string;
}
interface AddVersionModalProps extends Omit<ModalProps, 'onOk'> {
treeData: DownloadTreeDataNode[];
onOk: () => void;
}
function DownloadSkillModal({ treeData, onOk, ...rest }: AddVersionModalProps) {
return (
<KFModal
{...rest}
title="下载 Skill"
width={608}
onOk={onOk}
className={styles['download-modal']}
okText="下载"
>
<DirectoryTree
treeData={treeData}
defaultExpandAll={true}
titleRender={(record: DownloadTreeDataNode) => {
const label = record.name + (record.fileSize ? `${record.fileSize}` : '');
return <span style={{ fontSize: 14 }}>{label}</span>;
}}
/>
</KFModal>
);
}
export default DownloadSkillModal;

View File

@ -12,7 +12,7 @@ type ResourceItemProps = {
};
function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) {
const timeAgo = `${ago(item.full_last_update_time)}更新`;
const timeAgo = `${ago(item.update_time)}更新`;
const create_by = item.create_by ?? '';
return (

View File

@ -9,11 +9,15 @@ export type SkillData = {
max_version: string;
preview_pic: string;
create_by: string;
full_last_update_time: string;
update_time: string;
praises_count: number;
description: string;
type: string;
content: string;
md_content: string;
prraised: boolean;
prraises_count: number;
is_public: boolean;
praised: boolean;
file_tree_json: string;
file_path: string;
};

View File

@ -7,7 +7,7 @@ import { request } from '@umijs/max';
// 分页查询技能列表
export function getSkillListReq(params: any) {
return request(`/api/skill/`, {
return request(`/api/mmp/skills/`, {
method: 'GET',
params,
});
@ -15,7 +15,7 @@ export function getSkillListReq(params: any) {
// 分页查询技能类型列表
export function getSkillTypesReq(params: any) {
return request(`/api/skill/types/`, {
return request(`/api/mmp/skills/types/`, {
method: 'GET',
params,
});
@ -23,14 +23,14 @@ export function getSkillTypesReq(params: any) {
// 查询技能详情
export function getSkillInfoReq(id: number | string) {
return request(`/api/skill/${id}`, {
return request(`/api/mmp/skills/${id}`, {
method: 'GET',
});
}
// 创建技能
export function createSkillReq(data: any) {
return request(`/api/skill/`, {
return request(`/api/mmp/skills/`, {
method: 'POST',
data,
});
@ -38,7 +38,7 @@ export function createSkillReq(data: any) {
// 更新技能
export function updateSkillReq(data: any) {
return request(`/api/skill/`, {
return request(`/api/mmp/skills/`, {
method: 'PUT',
data,
});
@ -46,7 +46,41 @@ export function updateSkillReq(data: any) {
// 删除技能
export function deleteSkillReq(id: number) {
return request(`/api/skill/${id}`, {
return request(`/api/mmp/skills/${id}`, {
method: 'DELETE',
});
}
// 发布技能
export function publishSkillReq(id: number | string) {
return request(`/api/mmp/skills/publish/${id}`, {
method: 'PUT',
});
}
// 点赞技能
export function praiseSkillReq(id: number | string) {
return request(`/api/mmp/skills/praise/${id}`, {
method: 'PUT',
});
}
// 取消点赞技能
export function cancelPraiseSkillReq(id: number | string) {
return request(`/api/mmp/skills/unpraise/${id}`, {
method: 'PUT',
});
}
// 下载技能
export function downloadSkillReq(url: string) {
return request(`/api/mmp/skills/download/`, {
method: 'GET',
params: {
url,
},
});
}
// 上传地址
export const SkillUploadUrl = '/api/mmp/skills/upload';