feat: add stop functionality for decompress tasks and improve UI for task management (#12582)

This commit is contained in:
2026-04-28 07:31:40 +00:00
committed by GitHub
parent 4c396d68ca
commit e57dc1240b
18 changed files with 466 additions and 85 deletions
+20
View File
@@ -292,6 +292,26 @@ func (b *BaseApi) DeCompressFile(c *gin.Context) {
helper.Success(c)
}
// @Tags File
// @Summary Stop decompress task
// @Accept json
// @Param request body request.FileDeCompressStopReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/decompress/stop [post]
func (b *BaseApi) StopDeCompressFile(c *gin.Context) {
var req request.FileDeCompressStopReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fileService.StopDeCompress(req.TaskID); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags File
// @Summary Load file content
// @Accept json
+5
View File
@@ -90,6 +90,11 @@ type FileDeCompress struct {
Type string `json:"type" validate:"required"`
Path string `json:"path" validate:"required"`
Secret string `json:"secret"`
TaskID string `json:"taskID"`
}
type FileDeCompressStopReq struct {
TaskID string `json:"taskID" validate:"required"`
}
type FileEdit struct {
+1 -1
View File
@@ -896,7 +896,7 @@ func getAppFromRepo(downloadPath string) error {
return err
}
if err := fileOp.Decompress(packagePath, global.Dir.ResourceDir, files.SdkZip, ""); err != nil {
if err := fileOp.Decompress(context.Background(), packagePath, global.Dir.ResourceDir, files.SdkZip, ""); err != nil {
return err
}
defer func() {
+1 -1
View File
@@ -1034,7 +1034,7 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
}
return
}
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
if err = fileOp.Decompress(context.Background(), filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
if logger == nil {
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
} else {
+2 -1
View File
@@ -1,6 +1,7 @@
package service
import (
"context"
"fmt"
"os"
"path"
@@ -263,7 +264,7 @@ func loadSqlFile(file string) (string, error) {
_ = os.RemoveAll(dstDir)
return "", err
}
if err := archiver.Extract(file, dstDir, ""); err != nil {
if err := archiver.Extract(context.Background(), file, dstDir, ""); err != nil {
_ = os.RemoveAll(dstDir)
return "", err
}
+64 -2
View File
@@ -40,6 +40,7 @@ import (
"golang.org/x/text/transform"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
@@ -59,6 +60,7 @@ type IFileService interface {
Compress(c request.FileCompress) error
StopCompress(taskID string) error
DeCompress(c request.FileDeCompress) error
StopDeCompress(taskID string) error
GetContent(op request.FileContentReq) (response.FileInfo, error)
GetPreviewContent(op request.FileContentReq) (response.FileInfo, error)
SaveContent(edit request.FileEdit) error
@@ -89,7 +91,7 @@ const (
fileRemarkEncodedMaxLen = 256
)
func NewIFileService() IFileService {
func NewIFileService() FileService {
return &FileService{}
}
@@ -472,6 +474,16 @@ func preflightCompressTool(compressType files.CompressType) error {
}
}
func preflightDecompressTool(decompressType files.CompressType) error {
switch decompressType {
case files.Tar, files.Zip, files.TarGz, files.Rar, files.X7z:
_, err := files.NewExtractShellArchiver(decompressType)
return err
default:
return nil
}
}
func (f *FileService) StopCompress(taskID string) error {
if cancel, ok := global.TaskCtxMap[taskID]; ok {
cancel()
@@ -480,12 +492,62 @@ func (f *FileService) StopCompress(taskID string) error {
return buserr.New("TaskNotFound")
}
func (f *FileService) StopDeCompress(taskID string) error {
if cancel, ok := global.TaskCtxMap[taskID]; ok {
cancel()
return nil
}
return buserr.New("TaskNotFound")
}
func (f *FileService) DeCompress(c request.FileDeCompress) error {
fo := files.NewFileOp()
if c.Type == "tar" && len(c.Secret) != 0 {
c.Type = "tar.gz"
}
return fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type), c.Secret)
if err := preflightDecompressTool(files.CompressType(c.Type)); err != nil {
return err
}
taskItem, err := task.NewTask(c.Path, task.TaskExec, task.TaskScopeTask, c.TaskID, 1)
if err != nil {
return err
}
go func() {
taskItem.AddSubTask(c.Path, func(t *task.Task) error {
t.LogStart(c.Path)
dstExisted := fo.Stat(c.Dst)
parentDir := filepath.Dir(c.Dst)
if !fo.Stat(parentDir) {
if err := fo.CreateDir(parentDir, constant.DirPerm); err != nil {
return err
}
}
tempDst, err := os.MkdirTemp(parentDir, ".decompress-*")
if err != nil {
return err
}
success := false
defer func() {
_ = os.RemoveAll(tempDst)
if !success && !dstExisted {
_ = os.RemoveAll(c.Dst)
}
}()
if err := fo.Decompress(t.TaskCtx, c.Path, tempDst, files.CompressType(c.Type), c.Secret); err != nil {
return err
}
if err := fo.CreateDir(c.Dst, constant.DirPerm); err != nil {
return err
}
if err := cmd.NewCommandMgr(cmd.WithContext(t.TaskCtx)).RunBashCf("cp -rfp '%s'/. '%s'", tempDst, c.Dst); err != nil {
return err
}
success = true
return nil
}, nil)
_ = taskItem.Execute()
}()
return nil
}
func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo, error) {
+1
View File
@@ -25,6 +25,7 @@ func (f *FileRouter) InitRouter(Router *gin.RouterGroup) {
fileRouter.POST("/compress", baseApi.CompressFile)
fileRouter.POST("/compress/stop", baseApi.StopCompressFile)
fileRouter.POST("/decompress", baseApi.DeCompressFile)
fileRouter.POST("/decompress/stop", baseApi.StopDeCompressFile)
fileRouter.POST("/content", baseApi.GetContent)
fileRouter.POST("/preview", baseApi.PreviewContent)
fileRouter.POST("/save", baseApi.SaveContent)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
type ShellArchiver interface {
Extract(filePath, dstDir string, secret string) error
Extract(ctx context.Context, filePath, dstDir string, secret string) error
Compress(ctx context.Context, sourcePaths []string, dstFile string, secret string) error
}
+12 -12
View File
@@ -910,13 +910,13 @@ func decodeGBK(input string) (string, error) {
return decoded, nil
}
func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType) error {
func (f FileOp) decompressWithSDK(ctx context.Context, srcFile string, dst string, cType CompressType) error {
format := getFormat(cType)
if cType == Gz {
if err := f.tryDecompressTarGz(srcFile, dst, format); err == nil {
if err := f.tryDecompressTarGz(ctx, srcFile, dst, format); err == nil {
return nil
}
return f.DecompressGzFile(srcFile, dst)
return f.DecompressGzFile(ctx, srcFile, dst)
}
type dirEntry struct {
@@ -976,7 +976,7 @@ func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType
return err
}
defer input.Close()
if err := format.Extract(context.Background(), input, nil, handler); err != nil {
if err := format.Extract(ctx, input, nil, handler); err != nil {
return err
}
for i := len(dirs) - 1; i >= 0; i-- {
@@ -985,21 +985,21 @@ func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType
return nil
}
func (f FileOp) Decompress(srcFile string, dst string, cType CompressType, secret string) error {
func (f FileOp) Decompress(ctx context.Context, srcFile string, dst string, cType CompressType, secret string) error {
if cType == Tar || cType == Zip || cType == TarGz || cType == Rar || cType == X7z {
shellArchiver, err := NewExtractShellArchiver(cType)
if !f.Stat(dst) {
_ = f.CreateDir(dst, 0755)
}
if err == nil {
if err = shellArchiver.Extract(srcFile, dst, secret); err == nil {
if err = shellArchiver.Extract(ctx, srcFile, dst, secret); err == nil {
return nil
}
if cType == TarGz {
if strings.Contains(err.Error(), "bad decrypt") {
return buserr.New("ErrBadDecrypt")
}
if err := shellArchiver.Extract(srcFile, dst, "-"); strings.Contains(err.Error(), "bad decrypt") {
if err := shellArchiver.Extract(ctx, srcFile, dst, "-"); strings.Contains(err.Error(), "bad decrypt") {
return buserr.New("ErrBadDecrypt")
}
}
@@ -1009,7 +1009,7 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType, secre
}
}
}
return f.decompressWithSDK(srcFile, dst, cType)
return f.decompressWithSDK(ctx, srcFile, dst, cType)
}
func ZipFile(ctx context.Context, files []archiver.File, dst afero.File, progress func(current, total int, message string)) error {
@@ -1088,7 +1088,7 @@ func (r *contextReader) Read(p []byte) (int, error) {
}
}
func (f FileOp) tryDecompressTarGz(srcFile string, dst string, format archiver.CompressedArchive) error {
func (f FileOp) tryDecompressTarGz(ctx context.Context, srcFile string, dst string, format archiver.CompressedArchive) error {
input, err := f.Fs.Open(srcFile)
if err != nil {
return err
@@ -1139,7 +1139,7 @@ func (f FileOp) tryDecompressTarGz(srcFile string, dst string, format archiver.C
return nil
}
if err := format.Extract(context.Background(), input, nil, handler); err != nil {
if err := format.Extract(ctx, input, nil, handler); err != nil {
return err
}
if !extracted {
@@ -1151,7 +1151,7 @@ func (f FileOp) tryDecompressTarGz(srcFile string, dst string, format archiver.C
return nil
}
func (f FileOp) DecompressGzFile(srcFile, dst string) error {
func (f FileOp) DecompressGzFile(ctx context.Context, srcFile, dst string) error {
var archiveModTime time.Time
if st, err := f.Fs.Stat(srcFile); err == nil {
archiveModTime = st.ModTime()
@@ -1163,7 +1163,7 @@ func (f FileOp) DecompressGzFile(srcFile, dst string) error {
}
defer in.Close()
gr, err := gzip.NewReader(in)
gr, err := gzip.NewReader(&contextReader{ctx: ctx, r: in})
if err != nil {
return fmt.Errorf("gzip reader creation failed: %w", err)
}
+2 -2
View File
@@ -19,11 +19,11 @@ func NewRarArchiver() ShellArchiver {
return &RarArchiver{}
}
func (z RarArchiver) Extract(filePath, dstDir string, _ string) error {
func (z RarArchiver) Extract(ctx context.Context, filePath, dstDir string, _ string) error {
if err := checkCmdAvailability("unrar"); err != nil {
return err
}
return cmd.RunDefaultBashCf("unrar x -y -o+ %q %q", filePath, dstDir)
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("unrar x -y -o+ %q %q", filePath, dstDir)
}
func (z RarArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, _ string) (err error) {
+2 -2
View File
@@ -18,8 +18,8 @@ func NewTarArchiver(compressType CompressType) ShellArchiver {
}
}
func (t TarArchiver) Extract(FilePath string, dstDir string, secret string) error {
return cmd.RunDefaultBashCf("%s %s \"%s\" -C \"%s\"", t.Cmd, t.getOptionStr("extract"), FilePath, dstDir)
func (t TarArchiver) Extract(ctx context.Context, FilePath string, dstDir string, secret string) error {
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("%s %s \"%s\" -C \"%s\"", t.Cmd, t.getOptionStr("extract"), FilePath, dstDir)
}
func (t TarArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, secret string) error {
+2 -2
View File
@@ -21,7 +21,7 @@ func NewTarGzArchiver() ShellArchiver {
return &TarGzArchiver{}
}
func (t TarGzArchiver) Extract(filePath, dstDir string, secret string) error {
func (t TarGzArchiver) Extract(ctx context.Context, filePath, dstDir string, secret string) error {
if err := os.MkdirAll(dstDir, 0755); err != nil {
return fmt.Errorf("failed to create destination dir: %w", err)
}
@@ -35,7 +35,7 @@ func (t TarGzArchiver) Extract(filePath, dstDir string, secret string) error {
commands = fmt.Sprintf("tar -zxvf '%s' -C '%s' > /dev/null 2>&1", filePath, dstDir)
global.LOG.Debug(commands)
}
if err = cmd.RunDefaultBashC(commands); err != nil {
if err = cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashC(commands); err != nil {
return err
}
return nil
+2 -2
View File
@@ -19,11 +19,11 @@ func NewX7zArchiver() ShellArchiver {
return &X7zArchiver{}
}
func (z X7zArchiver) Extract(filePath, dstDir string, _ string) error {
func (z X7zArchiver) Extract(ctx context.Context, filePath, dstDir string, _ string) error {
if err := checkCmdAvailability("7z"); err != nil {
return err
}
return cmd.RunDefaultBashCf("7z x -y -o%q %q", dstDir, filePath)
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("7z x -y -o%q %q", dstDir, filePath)
}
func (z X7zArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, _ string) (err error) {
+2 -2
View File
@@ -20,11 +20,11 @@ func NewZipArchiver() ShellArchiver {
return &ZipArchiver{}
}
func (z ZipArchiver) Extract(filePath, dstDir string, secret string) error {
func (z ZipArchiver) Extract(ctx context.Context, filePath, dstDir string, secret string) error {
if err := checkCmdAvailability("unzip"); err != nil {
return err
}
return cmd.RunDefaultBashCf("unzip -qo %s -d %s", filePath, dstDir)
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("unzip -qo %s -d %s", filePath, dstDir)
}
func (z ZipArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, _ string) error {
+5
View File
@@ -149,6 +149,11 @@ export namespace File {
dst: string;
type: string;
secret: string;
taskID?: string;
}
export interface FileDeCompressStopReq {
taskID: string;
}
export interface FileEdit {
+9 -2
View File
@@ -57,8 +57,15 @@ export const stopCompressFile = (taskID: string) => {
return http.post('files/compress/stop', { taskID } as File.FileCompressStopReq);
};
export const deCompressFile = (form: File.FileDeCompress) => {
return http.post<File.File>('files/decompress', form, TimeoutEnum.T_10M);
export const deCompressFile = (form: File.FileDeCompress, config?: AxiosRequestConfig) => {
return http.service.post<File.File>('files/decompress', form, {
timeout: TimeoutEnum.T_10M,
...config,
});
};
export const stopDeCompressFile = (taskID: string) => {
return http.post('files/decompress/stop', { taskID } as File.FileDeCompressStopReq);
};
export const getFileContent = (params: File.ReqFile) => {
@@ -1,49 +1,109 @@
<template>
<DrawerPro v-model="open" :header="$t('file.deCompress')" :resource="name" @close="handleClose" size="normal">
<el-form
ref="fileForm"
label-position="top"
:model="form"
label-width="100px"
:rules="rules"
v-loading="loading"
>
<el-form-item :label="$t('commons.table.name')">
<el-input v-model="name" disabled></el-input>
</el-form-item>
<el-form-item :label="$t('file.deCompressDst')" prop="dst">
<el-input v-model="form.dst">
<template #prepend>
<el-button icon="Folder" @click="fileRef.acceptParams({ path: form.dst, dir: true })" />
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.compressPassword')" prop="secret" v-if="name.includes('tar.gz')">
<el-input v-model="form.secret"></el-input>
</el-form-item>
</el-form>
<DrawerPro v-model="open" :header="title" @close="handleClose" size="large">
<div class="space-y-4">
<div
v-if="showTaskStatus"
class="rounded-lg border border-[var(--el-border-color-light)] bg-[var(--el-fill-color-light)] px-4 py-3"
>
<div class="flex items-center justify-between gap-3">
<div class="space-y-1">
<div class="text-sm font-medium">
{{ $t('commons.status.executing') }}
</div>
<div class="text-xs text-[var(--el-text-color-secondary)]">
{{ taskInfo?.name || $t('file.deCompress') }}
</div>
</div>
<el-button link type="primary" @click="openTaskLog" :disabled="!currentTaskID">
{{ $t('commons.button.log') }}
</el-button>
</div>
<el-progress
class="mt-3"
:percentage="100"
:indeterminate="true"
:duration="1"
:stroke-width="8"
:show-text="false"
/>
</div>
<el-form
v-if="!currentTaskID"
ref="fileForm"
label-position="top"
:model="form"
label-width="100px"
:rules="rules"
v-loading="loading"
>
<el-form-item :label="$t('commons.table.name')">
<el-input v-model="name" disabled></el-input>
</el-form-item>
<el-form-item :label="$t('file.deCompressDst')" prop="dst">
<el-input v-model="form.dst">
<template #prepend>
<el-button icon="Folder" @click="fileRef.acceptParams({ path: form.dst, dir: true })" />
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.compressPassword')" prop="secret" v-if="name.includes('tar.gz')">
<el-input v-model="form.secret"></el-input>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" @click="submit(fileForm)">{{ $t('commons.button.confirm') }}</el-button>
<template v-if="!currentTaskID">
<el-button :type="loading ? 'danger' : 'default'" :loading="canceling" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button v-if="!loading" type="primary" @click="submit(fileForm)">
{{ $t('commons.button.confirm') }}
</el-button>
</template>
<template v-else>
<el-button
v-if="isTaskExecuting || loading"
type="danger"
:loading="stopping"
@click="stopCurrentTask"
>
{{ $t('commons.button.cancel') }}
</el-button>
<el-button v-if="currentTaskID" @click="openTaskLog">
{{ $t('commons.button.log') }}
</el-button>
<el-button type="default" @click="closeDrawer">
{{ $t('commons.button.close') }}
</el-button>
</template>
</span>
</template>
</DrawerPro>
<FileList ref="fileRef" @choose="getLinkPath" />
<TaskLog ref="taskLogRef" />
</template>
<script setup lang="ts">
import i18n from '@/lang';
import { reactive, ref } from 'vue';
import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import { File } from '@/api/interface/file';
import { Log } from '@/api/interface/log';
import { FormInstance, FormRules } from 'element-plus';
import { Rules } from '@/global/form-rules';
import { deCompressFile } from '@/api/modules/files';
import { deCompressFile, stopDeCompressFile } from '@/api/modules/files';
import { searchTasks } from '@/api/modules/log';
import FileList from '@/components/file-list/index.vue';
import { MsgSuccess } from '@/utils/message';
import TaskLog from '@/components/log/task/index.vue';
import { MsgError } from '@/utils/message';
import { getErrorMessage } from '@/utils/misc';
import { newUUID } from '@/utils/id';
import { GlobalStore } from '@/store';
interface CompressProps {
files: Array<any>;
const globalStore = GlobalStore();
interface DecompressProps {
dst: string;
name: string;
path: string;
@@ -55,20 +115,166 @@ const rules = reactive<FormRules>({
});
const fileForm = ref<FormInstance>();
let loading = ref(false);
let form = ref<File.FileDeCompress>({ type: 'zip', dst: '', path: '', secret: '' });
let open = ref(false);
let name = ref('');
const loading = ref(false);
const canceling = ref(false);
const stopping = ref(false);
const abortController = ref<AbortController | null>(null);
const form = ref<File.FileDeCompress>({ type: 'zip', dst: '', path: '', secret: '' });
const open = ref(false);
const title = ref('');
const fileRef = ref();
const taskLogRef = ref<InstanceType<typeof TaskLog> | null>(null);
const currentTaskID = ref('');
const taskInfo = ref<Log.Task | null>(null);
let taskTimer: ReturnType<typeof setInterval> | null = null;
const decompressTaskKey = 'file-management-decompress-task';
const name = ref('');
const em = defineEmits(['close']);
const em = defineEmits<{
(e: 'close', value: boolean): void;
(
e: 'task-change',
value: {
taskID: string;
status: string;
},
): void;
}>();
const handleClose = () => {
const isTaskExecuting = computed(() => taskInfo.value?.status === 'Executing');
const showTaskStatus = computed(() =>
Boolean(currentTaskID.value || loading.value || canceling.value || stopping.value),
);
const emitTaskChange = () => {
const payload = {
taskID: currentTaskID.value,
status: taskInfo.value?.status || (currentTaskID.value || loading.value ? 'Executing' : ''),
};
if (payload.taskID && payload.status) {
localStorage.setItem(decompressTaskKey, JSON.stringify(payload));
} else {
localStorage.removeItem(decompressTaskKey);
}
em('task-change', payload);
};
const stopTaskPolling = () => {
if (taskTimer) {
clearInterval(taskTimer);
taskTimer = null;
}
};
const loadTaskInfo = async () => {
if (!currentTaskID.value) {
taskInfo.value = null;
emitTaskChange();
return;
}
try {
const res = await searchTasks(
{
taskID: currentTaskID.value,
type: '',
status: '',
page: 1,
pageSize: 1,
},
globalStore.currentNode,
);
taskInfo.value = res.data.items?.[0] || null;
emitTaskChange();
if (!taskInfo.value || taskInfo.value.status !== 'Executing') {
stopTaskPolling();
currentTaskID.value = '';
taskInfo.value = null;
emitTaskChange();
}
} catch {
stopTaskPolling();
currentTaskID.value = '';
taskInfo.value = null;
emitTaskChange();
}
};
const startTaskPolling = () => {
stopTaskPolling();
void loadTaskInfo();
taskTimer = setInterval(() => {
void loadTaskInfo();
}, 1500);
};
const resetDrawerState = () => {
if (fileForm.value) {
fileForm.value.resetFields();
}
abortController.value = null;
canceling.value = false;
loading.value = false;
taskInfo.value = null;
currentTaskID.value = '';
stopTaskPolling();
emitTaskChange();
open.value = false;
em('close', open.value);
};
const closeDrawer = () => {
if (currentTaskID.value && isTaskExecuting.value) {
stopTaskPolling();
open.value = false;
em('close', false);
return;
}
resetDrawerState();
em('close', false);
};
const resumeTask = () => {
if (!currentTaskID.value) {
return;
}
open.value = true;
};
const stopCurrentTask = async () => {
if (stopping.value || (!loading.value && !currentTaskID.value)) {
return;
}
stopping.value = true;
try {
if (loading.value) {
if (abortController.value) {
canceling.value = true;
abortController.value.abort();
}
} else if (currentTaskID.value) {
await stopDeCompressFile(currentTaskID.value);
}
open.value = false;
em('close', false);
} catch (err) {
MsgError(getErrorMessage(err));
} finally {
stopping.value = false;
}
};
const handleClose = () => {
if (loading.value) {
stopCurrentTask();
return;
}
closeDrawer();
};
const openTaskLog = () => {
if (!currentTaskID.value) {
return;
}
taskLogRef.value?.openWithTaskID(currentTaskID.value, true, globalStore.currentNode);
};
const getLinkPath = (path: string) => {
@@ -81,25 +287,111 @@ const submit = async (formEl: FormInstance | undefined) => {
if (!valid) {
return;
}
const taskID = newUUID();
form.value.taskID = taskID;
canceling.value = false;
abortController.value = new AbortController();
loading.value = true;
deCompressFile(form.value)
deCompressFile(form.value, {
signal: abortController.value?.signal,
})
.then(() => {
MsgSuccess(i18n.global.t('file.deCompressSuccess'));
handleClose();
currentTaskID.value = taskID;
loading.value = false;
taskInfo.value = null;
emitTaskChange();
startTaskPolling();
})
.catch((error) => {
const message = getErrorMessage(error).toLowerCase();
if (canceling.value || message.includes('canceled') || message.includes('errshutdown')) {
currentTaskID.value = '';
emitTaskChange();
return;
}
currentTaskID.value = '';
emitTaskChange();
MsgError(getErrorMessage(error));
})
.finally(() => {
loading.value = false;
const wasCanceling = canceling.value;
abortController.value = null;
canceling.value = false;
if (wasCanceling) {
closeDrawer();
}
});
});
};
const acceptParams = (props: CompressProps) => {
const acceptParams = (props: DecompressProps) => {
if (currentTaskID.value) {
open.value = true;
if (!taskTimer) {
startTaskPolling();
}
emitTaskChange();
return;
}
form.value.type = props.type;
form.value.dst = props.dst;
form.value.path = props.path;
form.value.secret = '';
name.value = props.name;
abortController.value = null;
canceling.value = false;
stopping.value = false;
taskInfo.value = null;
currentTaskID.value = '';
emitTaskChange();
stopTaskPolling();
open.value = true;
title.value = i18n.global.t('file.deCompress');
};
defineExpose({ acceptParams });
const restoreRunningTask = () => {
const taskText = localStorage.getItem(decompressTaskKey);
if (!taskText) {
return;
}
try {
const task = JSON.parse(taskText) as {
taskID?: string;
status?: string;
};
if (!task.taskID) {
localStorage.removeItem(decompressTaskKey);
return;
}
currentTaskID.value = task.taskID;
if (task.status === 'Executing') {
emitTaskChange();
} else {
localStorage.removeItem(decompressTaskKey);
}
} catch {
localStorage.removeItem(decompressTaskKey);
}
};
onMounted(() => {
restoreRunningTask();
});
watch(open, (val) => {
if (val && currentTaskID.value) {
startTaskPolling();
} else {
stopTaskPolling();
}
});
onUnmounted(() => {
stopTaskPolling();
});
defineExpose({ acceptParams, resumeTask });
</script>
@@ -660,7 +660,7 @@
<CreateFile ref="createRef" @close="search" />
<ChangeRole ref="roleRef" @close="search" />
<Compress ref="compressRef" @close="search" @task-change="onCompressTaskChange" />
<Compress ref="compressRef" @close="search" />
<Decompress ref="deCompressRef" @close="search" />
<CodeEditor ref="codeEditorRef" @close="search" />
<FileRename ref="renameRef" @close="search" />
@@ -883,10 +883,6 @@ const recycleBinRef = ref();
const favoriteRef = ref();
const shareListRef = ref();
const historyDrawerRef = ref<InstanceType<typeof FileHistoryDrawer> | null>(null);
const compressTaskState = ref<{
taskID: string;
status: string;
} | null>(null);
const hoveredRowPath = ref(null);
const favorites = ref([]);
const batchRoleRef = ref();
@@ -926,14 +922,6 @@ const setPathRef = (key: string, el: any) => {
};
const getCurrentPath = () => pathRefs.value[editableTabsKey.value];
const onCompressTaskChange = (task: { taskID: string; status: string }) => {
if (!task.taskID) {
compressTaskState.value = null;
return;
}
compressTaskState.value = task;
};
const { searchableStatus, searchablePath, setSearchableInputRef, searchableInputBlur } = useMultipleSearchable(paths);
const paginationConfig = reactive({