feat: initial commit for HookPilot
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { api, errorMessage } from '../../api/client';
|
||||
import type { HookDetail, HookForm, ScriptVersion, ScriptVersionSummary } from '../../api/types';
|
||||
import ModalDialog from '../../components/ModalDialog.vue';
|
||||
import ScriptEditor from '../../components/ScriptEditor.vue';
|
||||
import { confirmDialog } from '../../composables/dialog';
|
||||
import { toast } from '../../composables/toast';
|
||||
import { formatTime } from '../../utils/format';
|
||||
import { SCRIPT_TEMPLATES } from '../../utils/templates';
|
||||
|
||||
const props = defineProps<{ form: HookForm; hook: HookDetail | null; versionsKey: number }>();
|
||||
|
||||
const versions = ref<ScriptVersionSummary[]>([]);
|
||||
const viewing = ref<ScriptVersion | null>(null);
|
||||
const templateIndex = ref('');
|
||||
|
||||
async function loadVersions() {
|
||||
if (!props.hook) return;
|
||||
try {
|
||||
versions.value = (await api.listVersions(props.hook.id)).items;
|
||||
} catch (err) {
|
||||
toast.error(errorMessage(err));
|
||||
}
|
||||
}
|
||||
|
||||
async function applyTemplate() {
|
||||
const template = SCRIPT_TEMPLATES[Number(templateIndex.value)];
|
||||
templateIndex.value = '';
|
||||
if (!template) return;
|
||||
if (props.form.script.trim()) {
|
||||
const ok = await confirmDialog({
|
||||
title: `使用模板「${template.name}」`,
|
||||
message: '当前编辑器中的内容将被替换(已保存的版本不受影响)。',
|
||||
confirmText: '替换',
|
||||
});
|
||||
if (!ok) return;
|
||||
}
|
||||
props.form.script = template.content;
|
||||
}
|
||||
|
||||
async function view(version: ScriptVersionSummary) {
|
||||
if (!props.hook) return;
|
||||
try {
|
||||
viewing.value = await api.getVersion(props.hook.id, version.id);
|
||||
} catch (err) {
|
||||
toast.error(errorMessage(err));
|
||||
}
|
||||
}
|
||||
|
||||
function loadIntoEditor() {
|
||||
if (!viewing.value) return;
|
||||
props.form.script = viewing.value.content;
|
||||
toast.info(`已载入 v${viewing.value.version} 的内容,保存后将生成新版本`);
|
||||
viewing.value = null;
|
||||
}
|
||||
|
||||
watch(() => props.versionsKey, loadVersions);
|
||||
onMounted(loadVersions);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="script-layout">
|
||||
<div class="stack" style="min-width: 0">
|
||||
<div class="row wrap">
|
||||
<span class="muted small">
|
||||
Bash 脚本。Tab 键缩进,参数以环境变量传入,请用 <code>"$HOOK_XXX"</code> 引用。
|
||||
</span>
|
||||
<span class="spacer" />
|
||||
<select v-model="templateIndex" style="width: auto" @change="applyTemplate">
|
||||
<option value="" disabled>插入示例模板…</option>
|
||||
<option v-for="(t, i) in SCRIPT_TEMPLATES" :key="t.name" :value="String(i)">{{ t.name }} — {{ t.description }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<ScriptEditor v-model="form.script" height="520px" />
|
||||
<div class="small faint">
|
||||
保存时若脚本内容有变化会生成新版本;已入队的任务仍使用入队时的版本执行。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="versions card">
|
||||
<div class="card-header"><h3>版本记录</h3></div>
|
||||
<div v-if="!hook" class="empty small">创建后生成 v1</div>
|
||||
<ul v-else class="version-list">
|
||||
<li v-for="v in versions" :key="v.id">
|
||||
<button class="version-item" @click="view(v)">
|
||||
<span class="row">
|
||||
<strong>v{{ v.version }}</strong>
|
||||
<span v-if="hook.currentVersion?.id === v.id" class="badge badge-info">当前</span>
|
||||
</span>
|
||||
<span class="small faint">{{ formatTime(v.createdAt) }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ModalDialog :open="!!viewing" :title="`脚本版本 v${viewing?.version ?? ''}`" wide @close="viewing = null">
|
||||
<div class="small muted">保存于 {{ formatTime(viewing?.createdAt) }}</div>
|
||||
<ScriptEditor v-if="viewing" :model-value="viewing.content" readonly height="420px" />
|
||||
<template #footer>
|
||||
<button class="btn" @click="viewing = null">关闭</button>
|
||||
<button class="btn btn-primary" @click="loadIntoEditor">载入到编辑器</button>
|
||||
</template>
|
||||
</ModalDialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.script-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 220px;
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
.versions {
|
||||
box-shadow: none;
|
||||
}
|
||||
.version-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 6px;
|
||||
max-height: 520px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.version-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 2px;
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: none;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
}
|
||||
.version-item:hover {
|
||||
background: var(--surface-2);
|
||||
}
|
||||
@media (max-width: 1000px) {
|
||||
.script-layout {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user