Skip to content

Commit 2c08a91

Browse files
committed
feat: add download skill file functionality and update related components
1 parent 3f6122d commit 2c08a91

File tree

7 files changed

+100
-15
lines changed

7 files changed

+100
-15
lines changed

apps/tools/serializers/tool.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,27 @@ def upload(self):
13561356
file.save(self.data.get('file').read())
13571357
return file_id
13581358

1359+
class DownloadSkillFile(serializers.Serializer):
1360+
user_id = serializers.UUIDField(required=True, label=_("User ID"))
1361+
workspace_id = serializers.CharField(required=True, label=_("workspace id"))
1362+
tool_id = serializers.CharField(required=True, label=_("tool id"))
1363+
1364+
def download(self):
1365+
self.is_valid(raise_exception=True)
1366+
tool = QuerySet(Tool).filter(
1367+
id=self.data.get('tool_id'), workspace_id=self.data.get('workspace_id'), tool_type=ToolType.SKILL
1368+
).first()
1369+
1370+
if tool is None:
1371+
raise AppApiException(500, _('Tool does not exist'))
1372+
skill_file = QuerySet(File).filter(id=tool.code).first()
1373+
if skill_file is None:
1374+
raise AppApiException(500, _('Skill file does not exist'))
1375+
1376+
response = HttpResponse(content_type='application/zip', content=skill_file.get_bytes())
1377+
response['Content-Disposition'] = f'attachment; filename="{skill_file.file_name}"'
1378+
return response
1379+
13591380
class GenerateCodeSerializer(serializers.Serializer):
13601381
workspace_id = serializers.CharField(required=True, label=_('Workspace ID'))
13611382
model_id = serializers.UUIDField(required=True, label=_('Model ID'))

apps/tools/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
path('workspace/<str:workspace_id>/tool/<str:tool_id>/add_internal_tool', views.ToolView.AddInternalTool.as_view()),
2929
path('workspace/<str:workspace_id>/tool/<str:tool_id>/add_store_tool', views.ToolView.AddStoreTool.as_view()),
3030
path('workspace/<str:workspace_id>/tool/<str:tool_id>/update_store_tool', views.ToolView.UpdateStoreTool.as_view()),
31+
path('workspace/<str:workspace_id>/tool/<str:tool_id>/download_skill_file', views.ToolView.DownloadSkillFile.as_view()),
3132
path('workspace/<str:workspace_id>/tool/<str:tool_id>/tool_record/<str:record_id>', views.ToolView.ToolRecord.as_view()),
3233
path('workspace/<str:workspace_id>/tool/<str:tool_id>/tool_record/<int:current_page>/<int:page_size>', views.ToolView.PageToolRecord.as_view()),
3334
path('workspace/<str:workspace_id>/tool/<int:current_page>/<int:page_size>', views.ToolView.Page.as_view()),

apps/tools/views/tool.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
from rest_framework.request import Request
66
from rest_framework.views import APIView
77

8+
from common import result
89
from common.auth import TokenAuth
910
from common.auth.authentication import has_permissions, check_batch_permissions
1011
from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants
1112
from common.log.log import log
12-
from common import result
1313
from tools.api.tool import ToolCreateAPI, ToolEditAPI, ToolReadAPI, ToolDeleteAPI, ToolTreeReadAPI, ToolDebugApi, \
1414
ToolExportAPI, ToolImportAPI, ToolPageAPI, PylintAPI, EditIconAPI, GetInternalToolAPI, AddInternalToolAPI, \
1515
ToolBatchOperateAPI
@@ -25,6 +25,7 @@ def get_tool_operation_object(tool_id):
2525
}
2626
return {}
2727

28+
2829
def get_tool_operation_object_batch(tool_id_list):
2930
tool_model_list = QuerySet(model=Tool).filter(id__in=tool_id_list)
3031
if tool_model_list is not None:
@@ -684,6 +685,22 @@ def put(self, request: Request, workspace_id: str):
684685
'file': request.FILES.get('file'),
685686
}).upload())
686687

688+
class DownloadSkillFile(APIView):
689+
authentication_classes = [TokenAuth]
690+
691+
@has_permissions(
692+
PermissionConstants.TOOL_EDIT.get_workspace_permission(),
693+
PermissionConstants.TOOL_EDIT.get_workspace_permission_workspace_manage_role(),
694+
RoleConstants.WORKSPACE_MANAGE.get_workspace_role(),
695+
RoleConstants.USER.get_workspace_role()
696+
)
697+
def get(self, request: Request, workspace_id: str, tool_id: str):
698+
return ToolSerializer.DownloadSkillFile(data={
699+
'workspace_id': workspace_id,
700+
'user_id': request.user.id,
701+
'tool_id': tool_id,
702+
}).download()
703+
687704
class GenerateCode(APIView):
688705
authentication_classes = [TokenAuth]
689706

ui/src/api/system-resource-management/tool.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Result } from '@/request/Result'
2-
import { get, post, del, put, exportFile, postStream } from '@/request/index'
2+
import { get, post, del, put, exportFile, postStream, download } from '@/request/index'
33
import { type Ref } from 'vue'
44
import type { pageRequest } from '@/api/type/common'
55
import type { toolData } from '@/api/type/tool'
@@ -146,6 +146,13 @@ const uploadSkillFile: (data: toolData, loading?: Ref<boolean>) => Promise<Resul
146146
return put(`${prefix}/upload_skill_file`, data, undefined, loading)
147147
}
148148

149+
const downloadSkillFile: (tool_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
150+
tool_id,
151+
loading,
152+
) => {
153+
return download(`${prefix}/${tool_id}/download_skill_file`, 'GET', undefined, undefined, loading)
154+
}
155+
149156
const generateCode: (data: any) => Promise<Result<any>> = (data: any) => {
150157
const p = (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/admin') + '/api'
151158
return postStream(`${p}${prefix}/generate_code`, data)
@@ -227,6 +234,7 @@ export default {
227234
pageToolRecord,
228235
getToolRecordDetail,
229236
uploadSkillFile,
237+
downloadSkillFile,
230238
generateCode,
231239
listToolWorkflowVersion,
232240
updateToolWorkflowVersion,

ui/src/api/system-shared/tool.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Result} from '@/request/Result'
2-
import {get, post, del, put, exportFile, postStream} from '@/request/index'
2+
import { get, post, del, put, exportFile, postStream, download } from '@/request/index'
33
import {type Ref} from 'vue'
44
import type {pageRequest} from '@/api/type/common'
55
import type {toolData, AddInternalToolParam} from '@/api/type/tool'
@@ -200,6 +200,13 @@ const uploadSkillFile: (data: toolData, loading?: Ref<boolean>) => Promise<Resul
200200
return put(`${prefix}/upload_skill_file`, data, undefined, loading)
201201
}
202202

203+
const downloadSkillFile: (tool_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
204+
tool_id,
205+
loading,
206+
) => {
207+
return download(`${prefix}/${tool_id}/download_skill_file`, 'GET', undefined, undefined, loading)
208+
}
209+
203210
const generateCode: (data: any) => Promise<Result<any>> = (
204211
data: any,
205212
) => {
@@ -301,6 +308,7 @@ export default {
301308
pageToolRecord,
302309
getToolRecordDetail,
303310
uploadSkillFile,
311+
downloadSkillFile,
304312
generateCode,
305313
putToolWorkflow,
306314
importToolWorkflow,

ui/src/api/tool/tool.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Result } from '@/request/Result'
2-
import { get, post, del, put, exportFile, postStream } from '@/request/index'
2+
import { get, post, del, put, exportFile, postStream, download } from '@/request/index'
33
import { type Ref } from 'vue'
44
import type { pageRequest } from '@/api/type/common'
55
import type { AddInternalToolParam, toolData } from '@/api/type/tool'
@@ -195,6 +195,14 @@ const uploadSkillFile: (data: toolData, loading?: Ref<boolean>) => Promise<Resul
195195
) => {
196196
return put(`${prefix.value}/upload_skill_file`, data, undefined, loading)
197197
}
198+
199+
const downloadSkillFile: (tool_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
200+
tool_id,
201+
loading,
202+
) => {
203+
return download(`${prefix.value}/${tool_id}/download_skill_file`, 'GET', undefined, undefined, loading)
204+
}
205+
198206
/**
199207
* 保存工具工作流
200208
* @param tool_id
@@ -348,6 +356,7 @@ export default {
348356
pageToolRecord,
349357
getToolRecordDetail,
350358
uploadSkillFile,
359+
downloadSkillFile,
351360
putToolWorkflow,
352361
importToolWorkflow,
353362
listToolWorkflowVersion,

ui/src/views/tool/SkillToolFormDrawer.vue

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,19 @@
145145
</div>
146146
</el-card>
147147
</template>
148-
149-
<el-upload
150-
v-model:file-list="form.fileList"
151-
action="#"
152-
:auto-upload="false"
153-
:show-file-list="false"
154-
accept=".zip"
155-
:on-change="fileHandleChange"
156-
>
157-
<el-button link type="primary">{{ $t('views.tool.skill.reUpload') }}</el-button>
158-
</el-upload>
148+
<div class="flex">
149+
<el-upload
150+
v-model:file-list="form.fileList"
151+
action="#"
152+
:auto-upload="false"
153+
:show-file-list="false"
154+
accept=".zip"
155+
:on-change="fileHandleChange"
156+
>
157+
<el-button link type="primary">{{ $t('views.tool.skill.reUpload') }}</el-button>
158+
</el-upload>
159+
<el-button link type="primary" @click="downloadZip">下载</el-button>
160+
</div>
159161
</div>
160162
<el-upload
161163
v-else
@@ -378,6 +380,25 @@ const fileHandleChange = (file: any, fileList: UploadFiles) => {
378380
})
379381
}
380382
383+
const downloadZip = () => {
384+
if (!form.value.code) {
385+
MsgError(t('views.tool.skill.noFileTip'))
386+
return
387+
}
388+
const fileName = form.value.fileList && form.value.fileList[0] ? form.value.fileList[0].name : `${form.value.name}.zip`
389+
loadSharedApi({ type: 'tool', systemType: apiType.value })
390+
.downloadSkillFile(form.value.id as string)
391+
.then((res: any) => {
392+
const url = window.URL.createObjectURL(new Blob([res]))
393+
const link = document.createElement('a')
394+
link.href = url
395+
link.setAttribute('download', fileName)
396+
document.body.appendChild(link)
397+
link.click()
398+
window.URL.revokeObjectURL(link.href)
399+
})
400+
}
401+
381402
const submit = async (formEl: FormInstance | undefined) => {
382403
if (!formEl) return
383404
await formEl.validate((valid: any) => {

0 commit comments

Comments
 (0)