前端添加代码编辑器主题 缩略图

This commit is contained in:
opensnail 2024-12-20 23:39:57 +08:00
parent 6eb66b4c18
commit 7b556668d2
4 changed files with 197 additions and 115 deletions

View File

@ -29,3 +29,11 @@ export function saveFileContent(data: Api.File.SaveFileRequest) {
data
});
}
export function fetchInitProject(data: Api.File.InitProjectRequest) {
return request<Api.File.FileContent>({
url: '/file/init/project',
method: 'post',
data
});
}

View File

@ -8,18 +8,16 @@ export function runPythonCommand(data: Api.Python.SaveFileRequest) {
});
}
export function pythonCommand3(data: Api.Python.SaveFileRequest) {
export function pythonCommand3() {
return request<boolean>({
url: '/python/stop',
method: 'post',
data
method: 'post'
});
}
export function fetchPythonStatus(data: Api.Python.SaveFileRequest) {
export function fetchPythonStatus() {
return request<boolean>({
url: '/python/status',
method: 'get',
data
method: 'get'
});
}

View File

@ -1260,6 +1260,11 @@ declare namespace Api {
filePath: string;
content: string;
}>;
type InitProjectRequest = Common.CommonRecord<{
projectName: string;
projectUrl: string;
}>;
}
namespace Python {

View File

@ -5,6 +5,7 @@ import { Folder, FolderOpenOutline } from '@vicons/ionicons5';
import { NIcon } from 'naive-ui';
import MonacoEditor from '@/components/common/monaco-editor.vue';
import {
fetchInitProject,
fetchListFiles,
fetchPythonStatus,
fetchViewFile,
@ -14,26 +15,66 @@ import {
} from '@/service/api';
import { $t } from '@/locales';
import { fetchQuickPublish } from '@/service/api/docker';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
const code = ref(`Hello SnailJob`);
const dataRef = ref(createData());
const dataRef = ref();
const curFile = ref<TreeOption>();
const pyStatus = ref<boolean>(false);
const theme = ref('vs');
const eol = ref(0);
const wordWrap = ref('on');
const minimap = ref('true');
const themes = [
{
label: 'Visual Studio',
value: 'vs'
},
{
label: 'Visual Studio Dark',
value: 'vs-dark'
},
{
label: 'High Contrast Dark',
value: 'hc-black'
}
];
function createData() {
return [
{
label: 'snail-job-python',
key: 'snail-job-python',
isLeaf: false,
prefix: () => {
return h(NIcon, null, {
default: () => h(Folder)
});
}
}
];
}
const eols = [
{
label: 'LF (Linux)',
value: 0
},
{
label: 'CRLF (Windows)',
value: 1
}
];
const wordWraps = [
{
label: '启用',
value: 'on'
},
{
label: '关闭',
value: 'off'
}
];
const minimaps = [
{
label: '启用',
value: 'true'
},
{
label: '关闭',
value: 'false'
}
];
const pyStatus = defineModel<boolean>('pyStatus', {
default: false
});
const updatePrefixWithExpaned = (
_keys: Array<string | number>,
@ -60,6 +101,22 @@ const updatePrefixWithExpaned = (
break;
}
};
const { formRef, validate } = useNaiveForm();
const { defaultRequiredRule } = useFormRules();
type Model = Pick<Api.File.InitProjectRequest, 'projectName' | 'projectUrl'>;
type RuleKey = Extract<keyof Model, 'projectName' | 'projectUrl'>;
const rules: Record<RuleKey, App.Global.FormRule> = {
projectName: defaultRequiredRule,
projectUrl: defaultRequiredRule
};
const model: Model = reactive({
projectName: 'project',
projectUrl: 'https://github.com/open-snail/python-client/archive/refs/tags/v0.0.1.zip'
});
const nodeProps = ({ option }: { option: TreeOption }) => {
return {
onClick() {
@ -71,6 +128,14 @@ const nodeProps = ({ option }: { option: TreeOption }) => {
};
};
async function handleSubmit() {
await validate();
const { projectName, projectUrl } = model;
const { error } = await fetchInitProject({ projectName, projectUrl });
if (error) return;
window.$message?.success($t('common.addSuccess'));
}
async function getViewFile(option: TreeOption) {
if (option.isLeaf) {
const res = await fetchViewFile(option.key as string);
@ -141,65 +206,18 @@ async function onLoad(node: TreeOption) {
});
}
type Model = Pick<Api.File.SaveFileRequest, 'fileName' | 'filePath' | 'content'>;
const model: Model = reactive(createDefaultModel());
function createDefaultModel(): Model {
return {
fileName: curFile.value?.key as string,
filePath: curFile.value?.label as string,
content: code.value
};
}
async function saveFile() {
model.fileName = curFile.value?.key as string;
model.filePath = curFile.value?.label as string;
model.content = code.value;
const { fileName, filePath, content } = model;
const { error } = await saveFileContent({ fileName, filePath, content });
if (error) {
window.$message?.success($t('common.updateFailed'));
} else {
window.$message?.success($t('common.updateSuccess'));
}
}
type PythonModel = Pick<Api.Python.SaveFileRequest, 'pythonPath' | 'command'>;
const pythonModel: PythonModel = reactive(createPythonModelModel());
function createPythonModelModel(): PythonModel {
return {
pythonPath: '',
command: 'pip3 install -r && python3 ' /// ToDo
};
}
function codeUpdate(content: string) {
code.value = content;
}
function saveFileClick() {
saveFile();
}
async function runPython() {
pythonModel.pythonPath = curFile.value?.key as string;
const { pythonPath, command } = pythonModel;
const { error } = await runPythonCommand({ pythonPath, command });
if (error) {
window.$message?.success($t('common.updateFailed'));
} else {
window.$message?.success($t('common.updateSuccess'));
}
}
async function stopPython() {
const { error } = await pythonCommand3(pythonModel);
async function saveFileClick() {
const { error } = await saveFileContent(
reactive<Api.File.SaveFileRequest>({
fileName: curFile.value?.key as string,
filePath: curFile.value?.label as string,
content: code.value
})
);
if (error) {
window.$message?.success($t('common.updateFailed'));
} else {
@ -208,7 +226,7 @@ async function stopPython() {
}
async function pythonStatus() {
const { error, data } = await fetchPythonStatus(pythonModel);
const { error, data } = await fetchPythonStatus();
if (error) {
window.$message?.success($t('common.updateFailed'));
} else {
@ -216,20 +234,40 @@ async function pythonStatus() {
}
}
type ContainerRequestModel = Pick<Api.Docker.CreateContainerRequest, 'groupName' | 'namespaceId' | 'imageName'>;
const containerRequestModel: ContainerRequestModel = reactive(createContainerRequestModelModel());
function createContainerRequestModelModel(): ContainerRequestModel {
return {
groupName: 'snail_job_demo_group',
namespaceId: '764d604ec6fc45f68cd92514c40e9e1a', /// ToDo
imageName: ''
};
async function runPythonClick() {
const { error } = await runPythonCommand(
reactive<Api.Python.SaveFileRequest>({
pythonPath: curFile.value?.key as string,
command: 'pip3 install -r && python3 ' /// ToDo
})
);
if (error) {
window.$message?.success($t('common.updateFailed'));
} else {
window.$message?.success($t('common.updateSuccess'));
pyStatus.value = true;
}
// pythonStatus();
}
async function quickPublish() {
const { error } = await fetchQuickPublish(containerRequestModel);
async function stopPythonClick() {
const { error } = await pythonCommand3();
if (error) {
window.$message?.success($t('common.updateFailed'));
} else {
window.$message?.success($t('common.updateSuccess'));
}
pythonStatus();
}
async function quickPublishClick() {
const { error } = await fetchQuickPublish(
reactive<Api.Docker.CreateContainerRequest>({
groupName: 'snail_job_demo_group',
namespaceId: '764d604ec6fc45f68cd92514c40e9e1a', /// ToDo
imageName: ''
})
);
if (error) {
window.$message?.success($t('common.updateFailed'));
} else {
@ -237,41 +275,57 @@ async function quickPublish() {
}
}
function runPythonClick() {
runPython();
pythonStatus();
}
function stopPythonClick() {
stopPython();
pythonStatus();
}
function quickPublishClick() {
quickPublish();
}
onMounted(() => {
pythonStatus();
onMounted(async () => {
const res = await fetchListFiles('');
const files = res.data as Api.File.FileInfo[];
dataRef.value = [
{
label: files[0].fileName,
key: files[0].fileName,
isLeaf: false,
prefix: () => {
return h(NIcon, null, {
default: () => h(Folder)
});
}
}
];
await pythonStatus();
});
</script>
<template>
<div class="code-editor">
<NSpace>
<NButton strong secondary type="primary" @click="saveFileClick">保存</NButton>
<NButton v-if="pyStatus" strong secondary type="primary" @click="stopPythonClick">停止</NButton>
<NButton v-else strong secondary type="primary" @click="runPythonClick">运行</NButton>
<NButton strong secondary type="primary" @click="quickPublishClick">一键发布</NButton>
</NSpace>
<div v-if="dataRef">
<NCard>
<NGrid x-gap="12" :cols="4">
<NGi>
主题: <NSelect v-model:value="theme" :options="themes" />
</NGi>
<NGi>
<NSelect v-model:value="eol" :options="eols" />
</NGi>
<NGi>
<NSelect v-model:value="wordWrap" :options="wordWraps" />
</NGi>
<NGi>
<NSelect v-model:value="minimap" :options="minimaps" />
</NGi>
</NGrid>
<br/>
<NSpace reverse>
<NButton strong secondary type="success" @click="quickPublishClick">一键发布</NButton>
<NButton v-if="pyStatus" strong secondary type="error" @click="stopPythonClick">停止</NButton>
<NButton v-else strong secondary type="info" @click="runPythonClick">运行</NButton>
<NButton strong secondary type="warning" @click="saveFileClick">保存</NButton>
</NSpace>
</NCard>
<NLayout has-sider>
<NLayoutSider
collapse-mode="transform"
:collapsed-width="120"
:width="240"
show-trigger="bar"
content-style="padding: 24px;"
content-style="padding-top: 12px;"
bordered
>
<NTree
@ -297,6 +351,23 @@ onMounted(() => {
</NLayoutContent>
</NLayout>
</div>
<div v-else>
<NForm ref="formRef" :model="model" :rules="rules">
<NFormItem path="projectName" label="项目名称">
<NInput v-model:value="model.projectName" />
</NFormItem>
<NFormItem path="项目地址" label="项目地址">
<NInput v-model:value="model.projectUrl" />
</NFormItem>
<NRow :gutter="[0, 24]">
<NCol :span="24">
<div style="display: flex; justify-content: flex-end">
<NButton type="primary" @click="handleSubmit">{{ $t('common.save') }}</NButton>
</div>
</NCol>
</NRow>
</NForm>
</div>
</template>
<style scoped></style>