mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-23 00:21:28 +00:00
feat(admin): add zh/en i18n to the config console
Introduce a lightweight useI18n composable (zh default, auto-detect via navigator.language, manual toggle persisted to localStorage) with full zh/en dictionaries, and wire translation keys through the console UI.
This commit is contained in:
parent
d3c1349d00
commit
9e0f21c538
3 changed files with 280 additions and 25 deletions
|
|
@ -6,39 +6,44 @@
|
|||
<Transition name="slide">
|
||||
<aside v-if="open" class="editor" role="dialog" aria-modal="true">
|
||||
<div class="editor-head">
|
||||
<h2>{{ isEdit ? "Edit group" : "New group" }}</h2>
|
||||
<button class="icon-btn" title="Close" @click="close">✕</button>
|
||||
<h2>{{ isEdit ? t("groupEditor.editTitle") : t("groupEditor.newTitle") }}</h2>
|
||||
<button class="icon-btn" :title="t('groupEditor.close')" @click="close">✕</button>
|
||||
</div>
|
||||
<form class="editor-body" @submit.prevent="save">
|
||||
<div class="row2">
|
||||
<div class="field">
|
||||
<label>Name</label>
|
||||
<input v-model="form.name" type="text" placeholder="My Team" required />
|
||||
<label>{{ t("groupEditor.name") }}</label>
|
||||
<input v-model="form.name" type="text" :placeholder="t('groupEditor.namePlaceholder')" required />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>ID</label>
|
||||
<input v-model="form.id" type="text" placeholder="my-team" :disabled="isEdit" required />
|
||||
<div class="hint">Unique, use a-z / 0-9 / dashes</div>
|
||||
<label>{{ t("groupEditor.id") }}</label>
|
||||
<input
|
||||
v-model="form.id"
|
||||
type="text"
|
||||
:placeholder="t('groupEditor.idPlaceholder')"
|
||||
:disabled="isEdit"
|
||||
required
|
||||
/>
|
||||
<div class="hint">{{ t("groupEditor.idHint") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Group admins <span class="lbl-note">(GitHub login or user id, comma-separated)</span></label>
|
||||
<input v-model="form.adminIds" type="text" placeholder="octocat, 12345" />
|
||||
<div class="hint">These users can view logs and edit this group's routes.</div>
|
||||
<label>{{ t("groupEditor.admins") }} <span class="lbl-note">{{ t("groupEditor.adminsNote") }}</span></label>
|
||||
<input v-model="form.adminIds" type="text" :placeholder="t('groupEditor.adminsPlaceholder')" />
|
||||
<div class="hint">{{ t("groupEditor.adminsHint") }}</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Owner scope <span class="lbl-note">(GitHub org / user logins, comma-separated)</span></label>
|
||||
<input v-model="form.owners" type="text" placeholder="my-org, some-user" />
|
||||
<div class="hint">
|
||||
Only webhook events from these orgs/users enter this group's routes. Leave empty for no
|
||||
restriction.
|
||||
</div>
|
||||
<label>{{ t("groupEditor.owners") }} <span class="lbl-note">{{ t("groupEditor.ownersNote") }}</span></label>
|
||||
<input v-model="form.owners" type="text" :placeholder="t('groupEditor.ownersPlaceholder')" />
|
||||
<div class="hint">{{ t("groupEditor.ownersHint") }}</div>
|
||||
</div>
|
||||
<div class="err">{{ formError }}</div>
|
||||
</form>
|
||||
<div class="editor-foot">
|
||||
<button class="btn btn-ghost" type="button" @click="close">Cancel</button>
|
||||
<button class="btn btn-accent" type="button" :disabled="saving" @click="save">Save group</button>
|
||||
<button class="btn btn-ghost" type="button" @click="close">{{ t("groupEditor.cancel") }}</button>
|
||||
<button class="btn btn-accent" type="button" :disabled="saving" @click="save">
|
||||
{{ t("groupEditor.save") }}
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
</Transition>
|
||||
|
|
@ -49,6 +54,8 @@
|
|||
import { reactive, watch } from "vue";
|
||||
import type { Group } from "~/types";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{ open: boolean; group: Group | null; saving: boolean }>();
|
||||
const emit = defineEmits<{
|
||||
(e: "close"): void;
|
||||
|
|
@ -94,11 +101,11 @@ function save(): void {
|
|||
const id = form.id.trim();
|
||||
const name = form.name.trim();
|
||||
if (!/^[a-z0-9][a-z0-9-]*$/.test(id)) {
|
||||
formError.value = "ID must be a-z / 0-9 / dashes";
|
||||
formError.value = t("groupEditor.errIdFormat");
|
||||
return;
|
||||
}
|
||||
if (!name) {
|
||||
formError.value = "Name is required";
|
||||
formError.value = t("groupEditor.errName");
|
||||
return;
|
||||
}
|
||||
const owners = splitList(form.owners);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="log-toolbar">
|
||||
<span class="kpi-label">LAST {{ logs.length }} SENDS</span>
|
||||
<button class="btn btn-ghost btn-sm" :disabled="loading" @click="refresh">⟳ Refresh</button>
|
||||
<span class="kpi-label">{{ t("logs.lastSends", { n: logs.length }) }}</span>
|
||||
<button class="btn btn-ghost btn-sm" :disabled="loading" @click="refresh">{{ t("logs.refresh") }}</button>
|
||||
</div>
|
||||
|
||||
<p v-if="error" class="err">{{ error }}</p>
|
||||
<p v-else-if="!loading && !logs.length" class="empty-log">No send records yet — trigger a webhook to see results.</p>
|
||||
<p v-else-if="!loading && !logs.length" class="empty-log">{{ t("logs.empty") }}</p>
|
||||
|
||||
<div class="log-list">
|
||||
<article v-for="l in logs" :key="l.ts + l.routeId" class="log-entry" :class="{ fail: !l.ok }">
|
||||
|
|
@ -17,8 +17,8 @@
|
|||
<span class="log-time">{{ fmtTime(l.ts) }}</span>
|
||||
</div>
|
||||
<div class="log-meta">
|
||||
<span v-if="l.repo"><b>REPO</b><code>{{ l.repo }}</code></span>
|
||||
<span><b>TARGET</b><code>{{ l.target }}</code></span>
|
||||
<span v-if="l.repo"><b>{{ t("logs.repo") }}</b><code>{{ l.repo }}</code></span>
|
||||
<span><b>{{ t("logs.target") }}</b><code>{{ l.target }}</code></span>
|
||||
</div>
|
||||
<div v-if="!l.ok && l.error" class="log-error">{{ l.error }}</div>
|
||||
</article>
|
||||
|
|
@ -29,6 +29,8 @@
|
|||
<script setup lang="ts">
|
||||
import type { SendRecord } from "~/types";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{ logs: SendRecord[]; loading: boolean }>();
|
||||
const emit = defineEmits<{ (e: "refresh"): void }>();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue