feat(ops-web): add operations console frontend
Vue 3 + Ant Design Vue SPA scaffold for the internal ops console. Ships the auth, account list, and audit log views that consume the new ops-api endpoints, plus the nginx/vite/typecheck plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<h2 class="ops-page-title">个人资料</h2>
|
||||
|
||||
<div class="ops-card" style="max-width: 640px; margin-bottom: 24px;">
|
||||
<h3 style="margin-top: 0; margin-bottom: 16px; font-size: 16px; font-weight: 600;">账号信息</h3>
|
||||
<a-descriptions :column="1" bordered>
|
||||
<a-descriptions-item label="ID">{{ auth.operator?.id }}</a-descriptions-item>
|
||||
<a-descriptions-item label="账号">{{ auth.operator?.username }}</a-descriptions-item>
|
||||
<a-descriptions-item label="姓名">{{ auth.operator?.display_name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="邮箱">{{ auth.operator?.email || "—" }}</a-descriptions-item>
|
||||
<a-descriptions-item label="角色">{{ auth.operator?.role }}</a-descriptions-item>
|
||||
<a-descriptions-item label="状态">
|
||||
<a-tag :color="auth.operator?.status === 'active' ? 'green' : 'red'">
|
||||
{{ auth.operator?.status === "active" ? "启用" : "停用" }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
|
||||
<div class="ops-card" style="max-width: 640px">
|
||||
<h3 style="margin-top: 0; margin-bottom: 16px; font-size: 16px; font-weight: 600;">修改密码</h3>
|
||||
<a-form layout="vertical" :model="form" @submit.prevent="onSubmit">
|
||||
<a-form-item label="原密码" required>
|
||||
<a-input-password v-model:value="form.oldPassword" autocomplete="current-password" />
|
||||
</a-form-item>
|
||||
<a-form-item label="新密码" required help="至少 8 位">
|
||||
<a-input-password v-model:value="form.newPassword" autocomplete="new-password" />
|
||||
</a-form-item>
|
||||
<a-form-item label="确认新密码" required>
|
||||
<a-input-password v-model:value="form.confirmPassword" autocomplete="new-password" />
|
||||
</a-form-item>
|
||||
<a-button type="primary" html-type="submit" :loading="loading">提交</a-button>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from "ant-design-vue";
|
||||
import { reactive, ref } from "vue";
|
||||
|
||||
import { OpsApiError, http } from "@/lib/http";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
const auth = useAuthStore();
|
||||
|
||||
const form = reactive({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
async function onSubmit() {
|
||||
if (!form.oldPassword || form.newPassword.length < 8) {
|
||||
message.warning("请输入原密码,并将新密码设为至少 8 位");
|
||||
return;
|
||||
}
|
||||
if (form.newPassword !== form.confirmPassword) {
|
||||
message.warning("两次输入的新密码不一致");
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
await http.post("/auth/password", {
|
||||
old_password: form.oldPassword,
|
||||
new_password: form.newPassword,
|
||||
});
|
||||
message.success("密码已更新,请使用新密码重新登录");
|
||||
auth.logout();
|
||||
// router redirect handled by 401 interceptor or next route guard
|
||||
if (typeof window !== "undefined") window.location.href = "/login";
|
||||
} catch (error) {
|
||||
if (error instanceof OpsApiError) {
|
||||
message.error(error.detail || error.message);
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user