feat(web): 前端逻辑骨架——http/router/stores/queries 与薄壳路由

This commit is contained in:
2026-10-07 14:36:06 +08:00
parent 9ecf817df2
commit fbb989c3fb
39 changed files with 1431 additions and 175 deletions
+10
View File
@@ -0,0 +1,10 @@
<script setup lang="ts">
/** 审计日志占位——管理操作留痕(交互设计-审计日志) */
</script>
<template>
<section>
<h1>审计日志</h1>
<p>管理操作留痕时间线(占位,待 UI 设计)。</p>
</section>
</template>
+10
View File
@@ -0,0 +1,10 @@
<script setup lang="ts">
/** Key 管理占位——登记 / 启停 / 归属(交互设计-key管理) */
</script>
<template>
<section>
<h1>Key 管理</h1>
<p>登记、启停、归属(占位,待 UI 设计)。</p>
</section>
</template>
+89
View File
@@ -0,0 +1,89 @@
<script setup lang="ts">
/** 登录 + 空库三态分流(交互登录篇);视觉细节待与 antdv-next 工具链一并设计 */
import { computed, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useLogin, useSetup, useSetupState } from "@/queries/session.ts";
const route = useRoute();
const router = useRouter();
const setupState = useSetupState();
const login = useLogin();
const setup = useSetup();
const username = ref("");
const password = ref("");
const passwordConfirm = ref("");
const error = ref("");
const isSetup = computed(() => setupState.data.value?.initialized === false);
async function onSubmit() {
error.value = "";
try {
if (isSetup.value) {
if (password.value !== passwordConfirm.value) {
error.value = "两次输入的密码不一致";
return;
}
await setup.mutateAsync({
username: username.value,
password: password.value,
});
} else {
await login.mutateAsync({
username: username.value,
password: password.value,
});
}
const redirect = route.query.redirect;
void router.replace(typeof redirect === "string" ? redirect : "/admin/overview");
} catch (e) {
error.value = e instanceof Error ? e.message : "登录失败";
}
}
</script>
<template>
<div class="login-view">
<h1>{{ isSetup ? "初始化管理员" : "管理面登录" }}</h1>
<form @submit.prevent="onSubmit">
<label>
用户名
<input v-model="username" name="username" autocomplete="username" required />
</label>
<label>
密码
<input
v-model="password"
name="password"
type="password"
autocomplete="current-password"
required
/>
</label>
<label v-if="isSetup">
确认密码
<input v-model="passwordConfirm" name="password_confirm" type="password" required />
</label>
<button type="submit" :disabled="login.isPending.value || setup.isPending.value">
{{ isSetup ? "完成设置" : "登录" }}
</button>
</form>
<p v-if="error" role="alert">{{ error }}</p>
</div>
</template>
<style scoped>
.login-view {
max-width: 360px;
margin: 10vh auto;
display: flex;
flex-direction: column;
gap: 12px;
}
.login-view label {
display: flex;
flex-direction: column;
gap: 4px;
}
</style>
+10
View File
@@ -0,0 +1,10 @@
<script setup lang="ts">
/** 成员管理占位——开户 / 令牌 / 停用(交互设计-成员管理) */
</script>
<template>
<section>
<h1>成员管理</h1>
<p>开户、令牌签发、停用(占位,待 UI 设计)。</p>
</section>
</template>
@@ -0,0 +1,10 @@
<script setup lang="ts">
/** 全员总览占位——信息结构见交互设计-全员总览;UI 待设计阶段 */
</script>
<template>
<section>
<h1>全员总览</h1>
<p>健康带 + 按成员 / key 汇总(占位,待 UI 设计)。</p>
</section>
</template>
@@ -0,0 +1,10 @@
<script setup lang="ts">
/** 支付记录占位——订阅/充值同构表单(交互设计-支付记录) */
</script>
<template>
<section>
<h1>支付记录</h1>
<p>订阅 / 充值登记与核对(占位,待 UI 设计)。</p>
</section>
</template>
@@ -0,0 +1,10 @@
<script setup lang="ts">
/** 账号设置(改密)占位——交互设计-登录与会话 */
</script>
<template>
<section>
<h1>账号设置</h1>
<p>修改密码(占位,待 UI 设计)。</p>
</section>
</template>