Merge pull request #1036 from MCSManager/abandon

Feat: file edit
This commit is contained in:
unitwk 2023-10-14 15:27:35 +08:00 committed by GitHub
commit 51a4273d42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 143 additions and 52 deletions

View File

@ -1,7 +1,22 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";
import { onBeforeUnmount, onMounted, ref } from "vue";
import { EditorView, basicSetup } from "codemirror";
import { javascript } from "@codemirror/lang-javascript";
// import { cpp } from "@codemirror/lang-cpp";
// import { css } from "@codemirror/lang-css";
// import { html } from "@codemirror/lang-html";
// import { java } from "@codemirror/lang-java";
// import { json } from "@codemirror/lang-json";
// import { less } from "@codemirror/lang-less";
// import { markdown } from "@codemirror/lang-markdown";
// import { php } from "@codemirror/lang-php";
// import { python } from "@codemirror/lang-python";
// import { sass } from "@codemirror/lang-sass";
// import { sql } from "@codemirror/lang-sql";
// import { vue } from "@codemirror/lang-vue";
// import { xml } from "@codemirror/lang-xml";
// import { getExtName } from "@/tools/fileManager";
import { EditorState } from "@codemirror/state";
import { getRandomId } from "@/tools/randId";
@ -33,11 +48,11 @@ const theme = EditorView.theme({
let editor: EditorView;
const initEditor = () => {
let startState = EditorState.create({
const startState = EditorState.create({
doc: props.text,
extensions: [
basicSetup,
theme,
// theme,
javascript(),
EditorView.updateListener.of(function (e) {
const text = e.view.state.doc.toString();
@ -56,8 +71,8 @@ onMounted(() => {
initEditor();
});
onUnmounted(() => {
editor.destroy();
onBeforeUnmount(() => {
editor?.destroy();
});
</script>

View File

@ -1,7 +1,7 @@
import { useDefineApi } from "@/stores/useDefineApi";
// 获取文件列表
export const getFileList = useDefineApi<
export const fileList = useDefineApi<
{
params: {
remote_uuid: string;
@ -203,3 +203,21 @@ export const downloadAddress = useDefineApi<
url: "/api/files/download",
method: "POST"
});
// 获取文件内容
export const fileContent = useDefineApi<
{
params: {
remote_uuid: string;
uuid: string;
};
data: {
target: string;
text?: string;
};
},
string
>({
url: "/api/files",
method: "PUT"
});

View File

@ -9,8 +9,6 @@ import dayjs from "dayjs";
import {
DownOutlined,
SearchOutlined,
FileOutlined,
FolderOutlined,
LoadingOutlined,
ExclamationCircleOutlined,
UploadOutlined
@ -20,7 +18,7 @@ import { useScreen } from "@/hooks/useScreen";
import { arrayFilter } from "@/tools/array";
import { useLayoutCardTools } from "@/hooks/useCardTools";
import {
getFileList as getFileListApi,
fileList as fileListApi,
getFileStatus as getFileStatusApi,
addFolder as addFolderApi,
deleteFile as deleteFileApi,
@ -149,9 +147,9 @@ const columns = computed(() => {
});
const getFileList = async () => {
const { execute } = getFileListApi();
const { state, execute } = fileListApi();
try {
const res = await execute({
await execute({
params: {
remote_uuid: daemonId || "",
uuid: instanceId || "",
@ -161,8 +159,10 @@ const getFileList = async () => {
target: breadcrumbs[breadcrumbs.length - 1].path
}
});
dataSource.value = res.value?.items || [];
operationForm.value.total = res.value?.total || 0;
if (state.value) {
dataSource.value = state.value.items || [];
operationForm.value.total = state.value.total || 0;
}
} catch (error: any) {
return message.error(error.message);
}
@ -296,7 +296,7 @@ const paste = async () => {
}
};
const resetname = async (file: string) => {
const resetName = async (file: string) => {
const newname = await openDialog(t("重命名"), t("请输入新名称"), file);
try {
const { execute } = moveFileApi();
@ -537,27 +537,34 @@ const handleTableChange = (e: { current: number; pageSize: number }) => {
getFileList();
};
const { execute } = getFileStatusApi();
setInterval(async () => {
await getFileStatus();
}, 3000);
const getFileStatus = async () => {
const res = await execute({
params: {
remote_uuid: daemonId || "",
uuid: instanceId || ""
const { state, execute } = getFileStatusApi();
try {
await execute({
params: {
remote_uuid: daemonId || "",
uuid: instanceId || ""
}
});
if (state.value) {
fileStatus.value = state.value;
}
});
fileStatus.value = res.value;
} catch (err: any) {
console.error(err);
return message.error(err.message);
}
};
import FileEditor from "./dialogs/FileEditor.vue";
const FileEditorDialog = ref<InstanceType<typeof FileEditor>>();
const editFile = (fileName: string) => {
FileEditorDialog.value?.openDialog(fileName);
const path = breadcrumbs[breadcrumbs.length - 1].path + fileName;
FileEditorDialog.value?.openDialog(path, fileName);
};
onMounted(() => {
@ -712,21 +719,19 @@ onMounted(() => {
<!-- eslint-disable-next-line vue/no-unused-vars -->
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<a-popconfirm :title="t('下载此文件?')" @confirm="downloadFile(record.name)">
<a-button
type="link"
class="file-name"
@click="rowClickTable(record.name, record.type)"
>
<span class="mr-4">
<component
:is="getFileIcon(record.name, record.type)"
style="font-size: 16px"
/>
</span>
{{ record.name }}
</a-button>
</a-popconfirm>
<a-button
type="link"
class="file-name"
@click="rowClickTable(record.name, record.type)"
>
<span class="mr-4">
<component
:is="getFileIcon(record.name, record.type)"
style="font-size: 16px"
/>
</span>
{{ record.name }}
</a-button>
</template>
<template v-if="column.key === 'action'">
<a-dropdown>
@ -735,7 +740,11 @@ onMounted(() => {
<a-menu-item v-if="fileStatus?.platform != 'win32'" key="1">
{{ t("TXT_CODE_16853efe") }}
</a-menu-item>
<a-menu-item key="2" @click="editFile(record.name)">
<a-menu-item
v-if="record.type === 1"
key="2"
@click="editFile(record.name)"
>
{{ t("TXT_CODE_ad207008") }}
</a-menu-item>
<a-menu-item key="3" @click="setClipBoard('copy', record.name)">
@ -744,7 +753,7 @@ onMounted(() => {
<a-menu-item key="4" @click="setClipBoard('move', record.name)">
{{ t("剪切") }}
</a-menu-item>
<a-menu-item key="5" @click="resetname(record.name)">
<a-menu-item key="5" @click="resetName(record.name)">
{{ t("TXT_CODE_c83551f5") }}
</a-menu-item>
<a-menu-item key="6" @click="deleteFile(record.name)">
@ -844,7 +853,7 @@ onMounted(() => {
}
.file-name {
color: initial;
color: inherit;
&:hover {
color: #1677ff;
}

View File

@ -1,28 +1,74 @@
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import { computed, ref } from "vue";
import { t } from "@/lang/i18n";
import { message } from "ant-design-vue";
import Editor from "@/components/Editor.vue";
import { fileContent } from "@/services/apis/fileManager";
import { useRoute } from "vue-router";
const route = useRoute();
const open = ref(false);
const text = ref("");
const openEditor = ref(false);
const editorText = ref("");
const fileName = ref("");
const path = ref("");
const daemonId = String(route.query["daemonId"]) ?? "";
const instanceId = String(route.query["instanceId"]) ?? "";
const openDialog = (path: string) => {
const openDialog = async (path_: string, fileName_: string) => {
open.value = true;
text.value = "你好世界\n123";
fileName.value = path;
path.value = path_;
fileName.value = fileName_;
await render();
};
const { state: text, execute } = fileContent();
const render = async () => {
try {
await execute({
params: {
remote_uuid: daemonId,
uuid: instanceId
},
data: {
target: path.value
}
});
if (text.value) {
typeof text.value === "boolean" ? (editorText.value = "") : (editorText.value = text.value);
}
openEditor.value = true;
} catch (err: any) {
console.error(err.message);
return message.error(err.message);
}
};
const submit = async () => {
try {
open.value = false;
return message.success(t("更新成功"));
await execute({
params: {
remote_uuid: daemonId,
uuid: instanceId
},
data: {
target: path.value,
text: editorText.value
}
});
return message.success(t("保存成功"));
} catch (err: any) {
console.error(err.message);
return message.error(err.message);
}
};
const cancel = () => {
open.value = openEditor.value = false;
};
const dialogTitle = computed(() => {
return `${t("编辑文件")} ${fileName.value}`;
});
@ -38,11 +84,14 @@ defineExpose({
centered
:mask-closable="false"
:title="dialogTitle"
:ok-text="t('保存')"
width="1000px"
@ok="submit"
:footer="null"
@cancel="cancel()"
>
RT:{{ text }}
<Editor v-if="open" v-model:text="text" height="600px" />
<a-space warp>
<a-button @click="submit">保存</a-button>
</a-space>
<Editor v-if="openEditor" ref="EditorComponent" v-model:text="editorText" height="70vh" />
<a-skeleton v-else active />
</a-modal>
</template>