Files
HookPilot/web/src/views/hook/ScriptTab.vue
T
2026-09-27 20:09:10 +08:00

149 lines
4.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>