666
This commit is contained in:
@@ -4,7 +4,7 @@ import { useRoute } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const menuItems = [
|
||||
const baseMenuItems = [
|
||||
{ name: "home", label: "首页", to: "/", icon: "home" },
|
||||
{ name: "avatar", label: "形象", to: "/avatar", icon: "avatar" },
|
||||
{ name: "material", label: "素材", to: "/material", icon: "material" },
|
||||
@@ -16,6 +16,13 @@ const menuItems = [
|
||||
{ name: "help", label: "帮助", to: "/help", icon: "help" },
|
||||
];
|
||||
|
||||
const menuItems = computed(() => {
|
||||
if (import.meta.env.DEV) {
|
||||
return [...baseMenuItems, { name: "debug", label: "调试", to: "/debug", icon: "debug" }];
|
||||
}
|
||||
return baseMenuItems;
|
||||
});
|
||||
|
||||
const activeName = computed(() => route.name);
|
||||
</script>
|
||||
|
||||
@@ -65,6 +72,11 @@ const activeName = computed(() => route.name);
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M9.5 9a2.5 2.5 0 1 1 4.2 1.8c-.8.7-1.2 1.2-1.2 2.2M12 17h.01" stroke-linecap="round" />
|
||||
</svg>
|
||||
<svg v-else-if="item.icon === 'debug'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M12 6v2M12 16v2M6 12h2M16 12h2" stroke-linecap="round" />
|
||||
<circle cx="12" cy="12" r="4" />
|
||||
<path d="M7 7l1.5 1.5M15.5 15.5L17 17M17 7l-1.5 1.5M8.5 15.5L7 17" stroke-linecap="round" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="sidebar-label">{{ item.label }}</span>
|
||||
</RouterLink>
|
||||
|
||||
@@ -1,101 +1,112 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { useAuthStore } from "../stores/auth";
|
||||
|
||||
const placeholder = () => import("../views/PlaceholderView.vue");
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
component: () => import("../layouts/MainLayout.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: "home",
|
||||
component: () => import("../views/HomeView.vue"),
|
||||
},
|
||||
{
|
||||
path: "avatar",
|
||||
name: "avatar",
|
||||
component: placeholder,
|
||||
meta: { title: "形象" },
|
||||
},
|
||||
{
|
||||
path: "material",
|
||||
name: "material",
|
||||
component: placeholder,
|
||||
meta: { title: "素材" },
|
||||
},
|
||||
{
|
||||
path: "voice",
|
||||
name: "voice",
|
||||
component: placeholder,
|
||||
meta: { title: "声音" },
|
||||
},
|
||||
{
|
||||
path: "compose",
|
||||
name: "compose",
|
||||
component: placeholder,
|
||||
meta: { title: "合成" },
|
||||
},
|
||||
{
|
||||
path: "extract",
|
||||
name: "extract",
|
||||
component: placeholder,
|
||||
meta: { title: "提取" },
|
||||
},
|
||||
{
|
||||
path: "design",
|
||||
name: "design",
|
||||
component: placeholder,
|
||||
meta: { title: "设计" },
|
||||
},
|
||||
{
|
||||
path: "model",
|
||||
name: "model",
|
||||
component: placeholder,
|
||||
meta: { title: "模型" },
|
||||
},
|
||||
{
|
||||
path: "help",
|
||||
name: "help",
|
||||
component: placeholder,
|
||||
meta: { title: "帮助" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
name: "login",
|
||||
component: () => import("../views/LoginView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/register",
|
||||
name: "register",
|
||||
component: () => import("../views/RegisterView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
redirect: "/login",
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
router.beforeEach((to) => {
|
||||
const auth = useAuthStore();
|
||||
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth);
|
||||
|
||||
if (requiresAuth && !auth.isAuthenticated) {
|
||||
return { name: "login" };
|
||||
}
|
||||
|
||||
if ((to.name === "login" || to.name === "register") && auth.isAuthenticated) {
|
||||
return { name: "home" };
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
|
||||
import { useAuthStore } from "../stores/auth";
|
||||
|
||||
|
||||
|
||||
const placeholder = () => import("../views/PlaceholderView.vue");
|
||||
|
||||
|
||||
|
||||
const mainChildren = [
|
||||
|
||||
{
|
||||
|
||||
path: "",
|
||||
|
||||
name: "home",
|
||||
|
||||
component: () => import("../views/HomeView.vue"),
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
path: "avatar",
|
||||
|
||||
name: "avatar",
|
||||
|
||||
component: placeholder,
|
||||
|
||||
meta: { title: "形象" },
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
path: "material",
|
||||
|
||||
name: "material",
|
||||
|
||||
component: placeholder,
|
||||
|
||||
meta: { title: "素材" },
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
path: "voice",
|
||||
|
||||
name: "voice",
|
||||
|
||||
component: placeholder,
|
||||
|
||||
meta: { title: "声音" },
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
path: "compose",
|
||||
|
||||
name: "compose",
|
||||
|
||||
component: placeholder,
|
||||
|
||||
meta: { title: "合成" },
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
path: "extract",
|
||||
|
||||
name: "extract",
|
||||
|
||||
component: placeholder,
|
||||
|
||||
meta: { title: "提取" },
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
path: "design",
|
||||
|
||||
name: "design",
|
||||
|
||||
component: placeholder,
|
||||
|
||||
meta: { title: "设计" },
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
path: "model",
|
||||
|
||||
name: "model",
|
||||
|
||||
component: placeholder,
|
||||
|
||||
meta: { title: "模型" },
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
path: "help",
|
||||
|
||||
name: "help",
|
||||
|
||||
|
||||
123
src/views/DebugView.vue
Normal file
123
src/views/DebugView.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import { CodeEditor } from "monaco-editor-vue3";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
|
||||
const DEFAULT_JS = `__native.log("info", "debug.run", { note: "编辑器内脚本" });
|
||||
return { ok: true, echo: params };
|
||||
`;
|
||||
|
||||
const editorCode = ref(DEFAULT_JS);
|
||||
const running = ref(false);
|
||||
const resultText = ref("");
|
||||
const eventLog = ref("");
|
||||
|
||||
let unlisten = null;
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
unlisten = await listen("quickjs:event", (e) => {
|
||||
const line =
|
||||
typeof e.payload === "object"
|
||||
? JSON.stringify(e.payload)
|
||||
: String(e.payload);
|
||||
eventLog.value += `${line}\n`;
|
||||
});
|
||||
} catch {
|
||||
/* 非 Tauri WebView */
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (typeof unlisten === "function") {
|
||||
unlisten();
|
||||
}
|
||||
});
|
||||
|
||||
async function executeScript() {
|
||||
running.value = true;
|
||||
resultText.value = "";
|
||||
try {
|
||||
const out = await invoke("run_quickjs_script_source", {
|
||||
scriptSource: editorCode.value,
|
||||
params: {},
|
||||
});
|
||||
resultText.value = JSON.stringify(out, null, 2);
|
||||
} catch (err) {
|
||||
resultText.value = String(err);
|
||||
} finally {
|
||||
running.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function clearEventLog() {
|
||||
eventLog.value = "";
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full min-h-0 flex-col gap-4 p-6">
|
||||
<div>
|
||||
<h1 class="gradient-text text-xl font-semibold">调试 · QuickJS</h1>
|
||||
<p class="mt-1 text-sm text-text-muted">
|
||||
源码按<strong class="text-text-secondary">异步函数体</strong>执行,可使用变量
|
||||
<code class="rounded bg-white/10 px-1 py-0.5 font-mono text-xs">params</code>、
|
||||
<code class="rounded bg-white/10 px-1 py-0.5 font-mono text-xs">await</code>、
|
||||
<code class="rounded bg-white/10 px-1 py-0.5 font-mono text-xs">return</code>
|
||||
及全局
|
||||
<code class="rounded bg-white/10 px-1 py-0.5 font-mono text-xs">__native</code>。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 flex-wrap items-center gap-2">
|
||||
<Button label="执行" :loading="running" :disabled="running" @click="executeScript" />
|
||||
<Button
|
||||
label="清空事件日志"
|
||||
severity="secondary"
|
||||
outlined
|
||||
size="small"
|
||||
@click="clearEventLog"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="min-h-[280px] flex-1 overflow-hidden rounded-lg border border-white/10 bg-[#1e1e1e]"
|
||||
style="height: min(58vh, 520px)"
|
||||
>
|
||||
<CodeEditor
|
||||
v-model:value="editorCode"
|
||||
language="javascript"
|
||||
theme="vs-dark"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:options="{
|
||||
automaticLayout: true,
|
||||
minimap: { enabled: true },
|
||||
fontSize: 13,
|
||||
scrollBeyondLastLine: false,
|
||||
tabSize: 2,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid min-h-0 shrink-0 gap-3 md:grid-cols-2">
|
||||
<div class="flex min-h-[140px] flex-col rounded-lg border border-white/10 bg-bg-deep/80">
|
||||
<div class="border-b border-white/10 px-3 py-2 text-xs font-medium text-text-muted">返回值</div>
|
||||
<pre
|
||||
class="min-h-[120px] flex-1 overflow-auto p-3 font-mono text-xs text-emerald-200/90 whitespace-pre-wrap"
|
||||
>{{ resultText || "(尚无)" }}</pre
|
||||
>
|
||||
</div>
|
||||
<div class="flex min-h-[140px] flex-col rounded-lg border border-white/10 bg-bg-deep/80">
|
||||
<div class="border-b border-white/10 px-3 py-2 text-xs font-medium text-text-muted">
|
||||
quickjs:event
|
||||
</div>
|
||||
<pre
|
||||
class="min-h-[120px] flex-1 overflow-auto p-3 font-mono text-[11px] text-text-muted whitespace-pre-wrap"
|
||||
>{{ eventLog || "(尚无)" }}</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user