feat: 重构 Skill 管理与编辑工作台

- 统一列表、分类、新建与详情页的产品交互

- 提供 Markdown 实时编辑、源码与脚本资源工作台

- 实现能力自动保存、导入导出及完整状态反馈
This commit is contained in:
2026-07-27 18:54:57 +08:00
parent 2a9e882ac6
commit 0dc5c3ca55
78 changed files with 16413 additions and 799 deletions

View File

@@ -1,49 +1,114 @@
import type {
RequestResult,
SkillCapabilityBinding,
SkillCapabilityCandidate,
SkillCapabilityReplaceResult,
SkillCapabilityTools,
SkillCapabilityType,
SkillCategory,
SkillCategoryDraft,
SkillExportFormat,
SkillFileContent,
SkillFileNode,
SkillImportPreview,
SkillInfo,
SkillValidationResult,
} from './types';
import {api} from '#/api/request';
import { api } from '#/api/request';
import { buildCapabilityBindingsPayload } from './skill-capability';
import { buildSkillCategoryPayload } from './skill-category';
import { buildSkillDraftPayload } from './skill-draft';
export function getSkillDetail(id: number | string) {
return api.get<RequestResult<SkillInfo>>('/api/v1/skill/getDetail', {
return api.get<RequestResult<SkillInfo>>('/api/v1/skill/detail', {
params: { id },
});
}
export function saveSkill(skill: SkillInfo) {
return api.post<RequestResult<SkillInfo>>('/api/v1/skill/save', skill);
return api.post<RequestResult<SkillInfo>>(
'/api/v1/skill/save',
buildSkillDraftPayload(skill, false),
);
}
export function updateSkill(skill: SkillInfo) {
return api.post<RequestResult<SkillInfo>>('/api/v1/skill/update', skill);
return api.post<RequestResult<SkillInfo>>(
'/api/v1/skill/update',
buildSkillDraftPayload(skill, true),
);
}
export function copySkill(payload: {
categoryId?: number | string;
displayName: string;
name: string;
sourceId: number | string;
}) {
return api.post<RequestResult<SkillInfo>>('/api/v1/skill/copy', payload);
}
export function validateSkillForPublish(id: number | string) {
return api.post<RequestResult<SkillValidationResult>>(
'/api/v1/skill/validatePublish',
{ id },
);
}
export function getSkillCategories() {
return api.get<RequestResult<any[]>>('/api/v1/skillCategory/visibleList', {
params: { sortKey: 'sortNo', sortType: 'asc' },
});
return api.get<RequestResult<SkillCategory[]>>(
'/api/v1/skill/category/visibleList',
{
params: { sortKey: 'sortNo', sortType: 'asc' },
},
);
}
export function getSkillCategoryTree() {
return api.get<RequestResult<SkillCategory[]>>(
'/api/v1/skill/category/tree',
{
params: { sortKey: 'sortNo', sortType: 'asc' },
},
);
}
export function saveSkillCategory(category: SkillCategoryDraft) {
return api.post<RequestResult<SkillCategory>>(
'/api/v1/skill/category/save',
buildSkillCategoryPayload(category, false),
);
}
export function updateSkillCategory(category: SkillCategoryDraft) {
return api.post<RequestResult<SkillCategory>>(
'/api/v1/skill/category/update',
buildSkillCategoryPayload(category, true),
);
}
export function deleteSkillCategory(id: number | string) {
return api.post<RequestResult<void>>('/api/v1/skill/category/remove', { id });
}
export function submitSkillPublishApproval(id: number | string) {
return api.post<RequestResult<number | string>>(
return api.post<RequestResult<null | number | string>>(
'/api/v1/skill/submitPublishApproval',
{ id },
);
}
export function submitSkillOfflineApproval(id: number | string) {
return api.post<RequestResult<number | string>>(
return api.post<RequestResult<null | number | string>>(
'/api/v1/skill/submitOfflineApproval',
{ id },
);
}
export function submitSkillDeleteApproval(id: number | string) {
return api.post<RequestResult<number | string>>(
return api.post<RequestResult<null | number | string>>(
'/api/v1/skill/submitDeleteApproval',
{ id },
);
@@ -56,23 +121,146 @@ export function getSkillFileTree(skillId: number | string) {
}
export function getSkillFileContent(skillId: number | string, path: string) {
return api.get<RequestResult<SkillFileContent>>('/api/v1/skill/file/content', {
params: { skillId, path },
});
return api.get<RequestResult<SkillFileContent>>(
'/api/v1/skill/file/content',
{
params: { path, skillId },
},
);
}
export function saveSkillFile(
skillId: number | string,
path: string,
content: string,
expectedContentHash?: string,
) {
return api.post<RequestResult<SkillFileContent>>('/api/v1/skill/file/save', {
skillId,
path,
content,
expectedContentHash,
path,
skillId,
});
}
export function createSkillFile(
skillId: number | string,
path: string,
content = '',
) {
return api.post<RequestResult<SkillFileContent>>(
'/api/v1/skill/file/create',
{
content,
path,
skillId,
},
);
}
export function renameSkillFile(
skillId: number | string,
path: string,
newPath: string,
expectedContentHash: string,
) {
return api.post<RequestResult<SkillFileContent>>(
'/api/v1/skill/file/rename',
{
expectedContentHash,
newPath,
path,
skillId,
},
);
}
export function deleteSkillFile(
skillId: number | string,
path: string,
expectedContentHash: string,
) {
return api.post<RequestResult<void>>('/api/v1/skill/file/delete', {
expectedContentHash,
path,
skillId,
});
}
export function uploadSkillFile(
skillId: number | string,
path: string,
file: File,
expectedContentHash?: string,
) {
const formData = new FormData();
formData.append('skillId', String(skillId));
formData.append('path', path);
if (expectedContentHash) {
formData.append('expectedContentHash', expectedContentHash);
}
formData.append('file', file);
return api.postFile<RequestResult<SkillFileContent>>(
'/api/v1/skill/file/upload',
formData,
);
}
export function downloadSkillFile(skillId: number | string, path: string) {
return api.download<Blob>('/api/v1/skill/file/download', {
params: { path, skillId },
});
}
export function previewSkillFile(skillId: number | string, path: string) {
return api.download<Blob>('/api/v1/skill/file/preview', {
params: { path, skillId },
});
}
export function getSkillCapabilityBindings(skillId: number | string) {
return api.get<RequestResult<SkillCapabilityBinding[]>>(
'/api/v1/skill/capability/list',
{
params: { skillId },
},
);
}
export function getSkillCapabilityCandidates(
type: SkillCapabilityType,
keyword = '',
) {
return api.get<RequestResult<SkillCapabilityCandidate[]>>(
'/api/v1/skill/capability/candidates',
{ params: { keyword, type } },
);
}
export function getSkillCapabilityTools(targetId: number | string) {
return api.get<RequestResult<SkillCapabilityTools>>(
'/api/v1/skill/capability/tools',
{
params: { targetId },
},
);
}
export function replaceSkillCapabilityBindings(
skillId: number | string,
bindings: SkillCapabilityBinding[],
expectedCapabilityHash: string,
) {
return api.post<RequestResult<SkillCapabilityReplaceResult>>(
'/api/v1/skill/capability/replace',
{
bindings: buildCapabilityBindingsPayload(bindings),
expectedCapabilityHash,
skillId,
},
);
}
export function importSkillPreview(file: File) {
const formData = new FormData();
formData.append('file', file);
@@ -82,25 +270,35 @@ export function importSkillPreview(file: File) {
);
}
export function importSkillConfirm(
file: File,
categoryId?: number | string,
overwriteDraft = false,
) {
const formData = new FormData();
formData.append('file', file);
if (categoryId) {
formData.append('categoryId', String(categoryId));
}
formData.append('overwriteDraft', String(overwriteDraft));
return api.postFile<RequestResult<SkillInfo[]>>(
export function importSkillConfirm(payload: {
capabilityMappings?: Array<{
bindingKey: string;
disabled?: boolean;
targetId?: number | string;
}>;
categoryId?: number | string;
conflictStrategy: 'OVERWRITE' | 'REJECT' | 'RENAME';
importToken: string;
renames?: Record<string, string>;
}) {
return api.post<RequestResult<SkillInfo[]>>(
'/api/v1/skill/import/confirm',
formData,
payload,
);
}
export function resolveSkillAssetUrl(skillId: number | string, path: string) {
return `/api/v1/skill/file/asset?skillId=${encodeURIComponent(
String(skillId),
)}&path=${encodeURIComponent(path)}`;
export function cancelSkillImport(importToken: string) {
return api.post<RequestResult<void>>('/api/v1/skill/import/cancel', {
importToken,
});
}
export function exportSkills(
ids: Array<number | string>,
format: SkillExportFormat,
) {
return api.download<Blob>('/api/v1/skill/export', {
data: { format, ids },
method: 'POST',
});
}